From effa09a42f57898427bb55aac23ff646378586e7 Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Tue, 31 Aug 2021 17:03:25 +0200 Subject: [PATCH 001/366] Add full support for setting securityContext for restic restore container Signed-off-by: MatthieuFin --- go.mod | 1 + pkg/restore/restic_restore_action.go | 18 +-- pkg/util/kube/security_context.go | 10 +- pkg/util/kube/security_context_test.go | 181 +++++++++++++++++++++++-- 4 files changed, 186 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index bfea339265..bcce08eb73 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( k8s.io/kube-aggregator v0.19.12 sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091 + sigs.k8s.io/yaml v1.2.0 // indirect ) replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index c20af38c75..f72923efe3 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -139,11 +139,11 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu ) } - runAsRoot, runAsGroup, allowPrivilegeEscalation := getSecurityContext(log, config) + runAsUser, runAsGroup, allowPrivilegeEscalation, secCtx := getSecurityContext(log, config) - securityContext, err := kube.ParseSecurityContext(runAsRoot, runAsGroup, allowPrivilegeEscalation) + securityContext, err := kube.ParseSecurityContext(runAsUser, runAsGroup, allowPrivilegeEscalation, secCtx) if err != nil { - log.Errorf("Using default resource values, couldn't parse resource requirements: %s.", err) + log.Errorf("Using default securityContext values, couldn't parse securityContext requirements: %s.", err) } initContainerBuilder := newResticInitContainerBuilder(image, string(input.Restore.UID)) @@ -245,15 +245,17 @@ func getResourceLimits(log logrus.FieldLogger, config *corev1.ConfigMap) (string return config.Data["cpuLimit"], config.Data["memLimit"] } - -// getSecurityContext extracts securityContext runAsUser, runAsGroup, and allowPrivilegeEscalation from a ConfigMap. -func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (string, string, string) { +// getSecurityContext extracts securityContext runAsUser, runAsGroup, allowPrivilegeEscalation, and securityContext from a ConfigMap. +func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (string, string, string, string) { if config == nil { log.Debug("No config found for plugin") - return "", "", "" + return "", "", "", "" } - return config.Data["secCtxRunAsUser"], config.Data["secCtxRunAsGroup"], config.Data["secCtxAllowPrivilegeEscalation"] + return config.Data["secCtxRunAsUser"], + config.Data["secCtxRunAsGroup"], + config.Data["secCtxAllowPrivilegeEscalation"], + config.Data["secCtx"] } // TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple diff --git a/pkg/util/kube/security_context.go b/pkg/util/kube/security_context.go index 2011515ecd..22c2c4ce86 100644 --- a/pkg/util/kube/security_context.go +++ b/pkg/util/kube/security_context.go @@ -21,9 +21,10 @@ import ( "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" ) -func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEscalation string) (corev1.SecurityContext, error) { +func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEscalation string, secCtx string) (corev1.SecurityContext, error) { securityContext := corev1.SecurityContext{} if runAsUser != "" { @@ -53,5 +54,12 @@ func ParseSecurityContext(runAsUser string, runAsGroup string, allowPrivilegeEsc securityContext.AllowPrivilegeEscalation = &parsedAllowPrivilegeEscalation } + if secCtx != "" { + err := yaml.UnmarshalStrict([]byte(secCtx), &securityContext) + if err != nil { + return securityContext, errors.WithStack(errors.Errorf(`Security context secCtx error: "%s"`, err)) + } + } + return securityContext, nil } diff --git a/pkg/util/kube/security_context_test.go b/pkg/util/kube/security_context_test.go index 2e20974bca..8ad72e49ce 100644 --- a/pkg/util/kube/security_context_test.go +++ b/pkg/util/kube/security_context_test.go @@ -30,6 +30,7 @@ func TestParseSecurityContext(t *testing.T) { runAsUser string runAsGroup string allowPrivilegeEscalation string + secCtx string } tests := []struct { name string @@ -37,33 +38,184 @@ func TestParseSecurityContext(t *testing.T) { wantErr bool expected *corev1.SecurityContext }{ - {"valid security context", args{"1001", "999", "true"}, false, &corev1.SecurityContext{ - RunAsUser: pointInt64(1001), - RunAsGroup: pointInt64(999), - AllowPrivilegeEscalation: boolptr.True(), - }}, + { + "valid security context", + args{"1001", "999", "true", ``}, + false, + &corev1.SecurityContext{ + RunAsUser: pointInt64(1001), + RunAsGroup: pointInt64(999), + AllowPrivilegeEscalation: boolptr.True(), + }, + }, + { + "valid security context with override runAsUser", + args{"1001", "999", "true", `runAsUser: 2000`}, + false, + &corev1.SecurityContext{ + RunAsUser: pointInt64(2000), + RunAsGroup: pointInt64(999), + AllowPrivilegeEscalation: boolptr.True(), + }, + }, + { + "valid securityContext with comments only secCtx key", + args{"", "", "",` +capabilities: + drop: + - ALL + add: + - cap1 + - cap2 +sELinuxOptions: + user: userLabel + role: roleLabel + type: typeLabel + level: levelLabel +# user www-data +runAsUser: 3333 +# group www-data +runAsGroup: 3333 +runAsNonRoot: true +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + false, + &corev1.SecurityContext{ + RunAsUser: pointInt64(3333), + RunAsGroup: pointInt64(3333), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{"cap1", "cap2"}, + }, + SELinuxOptions: &corev1.SELinuxOptions{ + User: "userLabel", + Role: "roleLabel", + Type: "typeLabel", + Level: "levelLabel", + }, + RunAsNonRoot: boolptr.True(), + ReadOnlyRootFilesystem: boolptr.True(), + AllowPrivilegeEscalation: boolptr.False(), + }, + }, + { + "valid securityContext with secCtx key override runAsUser runAsGroup and allowPrivilegeEscalation", + args{"1001", "999", "true",` +capabilities: + drop: + - ALL + add: + - cap1 + - cap2 +sELinuxOptions: + user: userLabel + role: roleLabel + type: typeLabel + level: levelLabel +# user www-data +runAsUser: 3333 +# group www-data +runAsGroup: 3333 +runAsNonRoot: true +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + false, + &corev1.SecurityContext{ + RunAsUser: pointInt64(3333), + RunAsGroup: pointInt64(3333), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{"cap1", "cap2"}, + }, + SELinuxOptions: &corev1.SELinuxOptions{ + User: "userLabel", + Role: "roleLabel", + Type: "typeLabel", + Level: "levelLabel", + }, + RunAsNonRoot: boolptr.True(), + ReadOnlyRootFilesystem: boolptr.True(), + AllowPrivilegeEscalation: boolptr.False(), + }, + }, { "another valid security context", - args{"1001", "999", "false"}, false, &corev1.SecurityContext{ + args{"1001", "999", "false", ""}, + false, + &corev1.SecurityContext{ RunAsUser: pointInt64(1001), RunAsGroup: pointInt64(999), AllowPrivilegeEscalation: boolptr.False(), }, }, - {"security context without runAsGroup", args{"1001", "", ""}, false, &corev1.SecurityContext{ + {"security context without runAsGroup", args{"1001", "", "", ""}, false, &corev1.SecurityContext{ RunAsUser: pointInt64(1001), }}, - {"security context without runAsUser", args{"", "999", ""}, false, &corev1.SecurityContext{ + {"security context without runAsUser", args{"", "999", "", ""}, false, &corev1.SecurityContext{ RunAsGroup: pointInt64(999), }}, - {"empty context without runAsUser", args{"", "", ""}, false, &corev1.SecurityContext{}}, - {"invalid security context runAsUser", args{"not a number", "", ""}, true, nil}, - {"invalid security context runAsGroup", args{"", "not a number", ""}, true, nil}, - {"invalid security context allowPrivilegeEscalation", args{"", "", "not a bool"}, true, nil}, + {"empty context without runAsUser", args{"", "", "", ""}, false, &corev1.SecurityContext{}}, + { + "invalid securityContext secCtx unknown key", + args{"", "", "",` +capabilitiesUnknownkey: + drop: + - ALL + add: + - cap1 + - cap2 +# user www-data +runAsUser: 3333 +# group www-data +runAsGroup: 3333 +runAsNonRoot: true +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + true, nil, + }, + { + "invalid securityContext secCtx wrong value type string instead of bool", + args{"", "", "",` +capabilitiesUnknownkey: + drop: + - ALL + add: + - cap1 + - cap2 +# user www-data +runAsUser: 3333 +# group www-data +runAsGroup: 3333 +runAsNonRoot: plop +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + true, nil, + }, + { + "invalid securityContext secCtx wrong value type string instead of int", + args{"", "", "",` +capabilitiesUnknownkey: + drop: + - ALL + add: + - cap1 + - cap2 +# user www-data +runAsUser: plop +# group www-data +runAsGroup: 3333 +runAsNonRoot: true +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + true, nil, + }, + {"invalid security context runAsUser", args{"not a number", "", "", ""}, true, nil}, + {"invalid security context runAsGroup", args{"", "not a number", "", ""}, true, nil}, + {"invalid security context allowPrivilegeEscalation", args{"", "", "not a bool", ""}, true, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := ParseSecurityContext(tt.args.runAsUser, tt.args.runAsGroup, tt.args.allowPrivilegeEscalation) + got, err := ParseSecurityContext(tt.args.runAsUser, tt.args.runAsGroup, tt.args.allowPrivilegeEscalation, tt.args.secCtx) if tt.wantErr { assert.Error(t, err) return @@ -74,8 +226,7 @@ func TestParseSecurityContext(t *testing.T) { tt.expected = &corev1.SecurityContext{} } - assert.Equal(t, tt.expected.RunAsUser, got.RunAsUser) - assert.Equal(t, tt.expected.RunAsGroup, got.RunAsGroup) + assert.Equal(t, *tt.expected, got) }) } } From b0fb9f799b279b5170ce1c48f5393536d868520c Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Tue, 31 Aug 2021 17:21:12 +0200 Subject: [PATCH 002/366] Add doc for new secCtx cm key and missing secCtxAllowPrivilegeEscalation. Signed-off-by: MatthieuFin --- site/content/docs/main/restic.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/restic.md b/site/content/docs/main/restic.md index 8873c1ff59..096abda6e8 100644 --- a/site/content/docs/main/restic.md +++ b/site/content/docs/main/restic.md @@ -393,11 +393,27 @@ data: # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. memLimit: 128Mi - # "secCtxRunAsUser sets the securityContext.runAsUser value on the restic init containers during restore." + # "secCtxRunAsUser" sets the securityContext.runAsUser value on the restic init containers during restore. secCtxRunAsUser: 1001 - # "secCtxRunAsGroup sets the securityContext.runAsGroup value on the restic init containers during restore." + # "secCtxRunAsGroup" sets the securityContext.runAsGroup value on the restic init containers during restore. secCtxRunAsGroup: 999 + + # "secCtxAllowPrivilegeEscalation" sets the securityContext.allowPrivilegeEscalation value on the restic init containers during restore. + secCtxAllowPrivilegeEscalation: false + + # "secCtx" sets the securityContext object value on the restic init containers during restore. + # This key override `secCtxRunAsUser`, `secCtxRunAsGroup`, `secCtxAllowPrivilegeEscalation` if `secCtx.runAsUser`, `secCtx.runAsGroup` or `secCtx.allowPrivilegeEscalation` are set. + secCtx: | + capabilities: + drop: + - ALL + add: [] + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsUser: 1001 + runAsGroup: 999 + ``` ## Troubleshooting From c4e53b936540cd711b4bbb0b50b1752d003b05b7 Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Tue, 31 Aug 2021 17:25:20 +0200 Subject: [PATCH 003/366] add changelog Signed-off-by: MatthieuFin --- changelogs/unreleased/4084-MatthieuFin | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4084-MatthieuFin diff --git a/changelogs/unreleased/4084-MatthieuFin b/changelogs/unreleased/4084-MatthieuFin new file mode 100644 index 0000000000..c4bc338330 --- /dev/null +++ b/changelogs/unreleased/4084-MatthieuFin @@ -0,0 +1 @@ +restic: add full support for setting SecurityContext for restore init container from configMap. From 338af4e5842f378b64588944c85fddec901eb1b6 Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Tue, 31 Aug 2021 17:27:18 +0200 Subject: [PATCH 004/366] update dependancies Signed-off-by: MatthieuFin --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bcce08eb73..7b5e196dbe 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( k8s.io/kube-aggregator v0.19.12 sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091 - sigs.k8s.io/yaml v1.2.0 // indirect + sigs.k8s.io/yaml v1.2.0 ) replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 From 08e4138c16f8f042f72024ba8b0b75ffebe8c7c5 Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Tue, 31 Aug 2021 17:43:09 +0200 Subject: [PATCH 005/366] Fix lint issue and test failed Signed-off-by: MatthieuFin --- pkg/restore/restic_restore_action.go | 9 ++--- pkg/restore/restic_restore_action_test.go | 2 +- pkg/util/kube/security_context_test.go | 42 +++++++++++------------ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index f72923efe3..91b4a6761c 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -245,6 +245,7 @@ func getResourceLimits(log logrus.FieldLogger, config *corev1.ConfigMap) (string return config.Data["cpuLimit"], config.Data["memLimit"] } + // getSecurityContext extracts securityContext runAsUser, runAsGroup, allowPrivilegeEscalation, and securityContext from a ConfigMap. func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (string, string, string, string) { if config == nil { @@ -252,10 +253,10 @@ func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (strin return "", "", "", "" } - return config.Data["secCtxRunAsUser"], - config.Data["secCtxRunAsGroup"], - config.Data["secCtxAllowPrivilegeEscalation"], - config.Data["secCtx"] + return config.Data["secCtxRunAsUser"], + config.Data["secCtxRunAsGroup"], + config.Data["secCtxAllowPrivilegeEscalation"], + config.Data["secCtx"] } // TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple diff --git a/pkg/restore/restic_restore_action_test.go b/pkg/restore/restic_restore_action_test.go index 10148db68a..b218f4b88d 100644 --- a/pkg/restore/restic_restore_action_test.go +++ b/pkg/restore/restic_restore_action_test.go @@ -117,7 +117,7 @@ func TestResticRestoreActionExecute(t *testing.T) { defaultCPURequestLimit, defaultMemRequestLimit, // limits ) - securityContext, _ := kube.ParseSecurityContext("", "", "") + securityContext, _ := kube.ParseSecurityContext("", "", "", "") var ( restoreName = "my-restore" diff --git a/pkg/util/kube/security_context_test.go b/pkg/util/kube/security_context_test.go index 8ad72e49ce..f4b9ccb7ce 100644 --- a/pkg/util/kube/security_context_test.go +++ b/pkg/util/kube/security_context_test.go @@ -60,7 +60,7 @@ func TestParseSecurityContext(t *testing.T) { }, { "valid securityContext with comments only secCtx key", - args{"", "", "",` + args{"", "", "", ` capabilities: drop: - ALL @@ -81,16 +81,16 @@ readOnlyRootFilesystem: true allowPrivilegeEscalation: false`}, false, &corev1.SecurityContext{ - RunAsUser: pointInt64(3333), - RunAsGroup: pointInt64(3333), - Capabilities: &corev1.Capabilities{ + RunAsUser: pointInt64(3333), + RunAsGroup: pointInt64(3333), + Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, - Add: []corev1.Capability{"cap1", "cap2"}, + Add: []corev1.Capability{"cap1", "cap2"}, }, - SELinuxOptions: &corev1.SELinuxOptions{ - User: "userLabel", - Role: "roleLabel", - Type: "typeLabel", + SELinuxOptions: &corev1.SELinuxOptions{ + User: "userLabel", + Role: "roleLabel", + Type: "typeLabel", Level: "levelLabel", }, RunAsNonRoot: boolptr.True(), @@ -100,7 +100,7 @@ allowPrivilegeEscalation: false`}, }, { "valid securityContext with secCtx key override runAsUser runAsGroup and allowPrivilegeEscalation", - args{"1001", "999", "true",` + args{"1001", "999", "true", ` capabilities: drop: - ALL @@ -121,16 +121,16 @@ readOnlyRootFilesystem: true allowPrivilegeEscalation: false`}, false, &corev1.SecurityContext{ - RunAsUser: pointInt64(3333), - RunAsGroup: pointInt64(3333), - Capabilities: &corev1.Capabilities{ + RunAsUser: pointInt64(3333), + RunAsGroup: pointInt64(3333), + Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, - Add: []corev1.Capability{"cap1", "cap2"}, + Add: []corev1.Capability{"cap1", "cap2"}, }, - SELinuxOptions: &corev1.SELinuxOptions{ - User: "userLabel", - Role: "roleLabel", - Type: "typeLabel", + SELinuxOptions: &corev1.SELinuxOptions{ + User: "userLabel", + Role: "roleLabel", + Type: "typeLabel", Level: "levelLabel", }, RunAsNonRoot: boolptr.True(), @@ -157,7 +157,7 @@ allowPrivilegeEscalation: false`}, {"empty context without runAsUser", args{"", "", "", ""}, false, &corev1.SecurityContext{}}, { "invalid securityContext secCtx unknown key", - args{"", "", "",` + args{"", "", "", ` capabilitiesUnknownkey: drop: - ALL @@ -175,7 +175,7 @@ allowPrivilegeEscalation: false`}, }, { "invalid securityContext secCtx wrong value type string instead of bool", - args{"", "", "",` + args{"", "", "", ` capabilitiesUnknownkey: drop: - ALL @@ -193,7 +193,7 @@ allowPrivilegeEscalation: false`}, }, { "invalid securityContext secCtx wrong value type string instead of int", - args{"", "", "",` + args{"", "", "", ` capabilitiesUnknownkey: drop: - ALL From d17db327f778273ad9247a8c025417f2dd966cdf Mon Sep 17 00:00:00 2001 From: Neha Viswanathan Date: Thu, 9 Dec 2021 20:34:51 -0800 Subject: [PATCH 006/366] update migration across different Kubernetes versions section Signed-off-by: Neha Viswanathan --- site/content/docs/main/migration-case.md | 1 + site/content/docs/v1.6/migration-case.md | 1 + site/content/docs/v1.7/migration-case.md | 1 + 3 files changed, 3 insertions(+) diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index c547ebbb4b..9ad7b809ae 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -53,6 +53,7 @@ If you encounter issues, make sure that Velero is running in the same namespace ## Migrating Workloads Across Different Kubernetes Versions Migration across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered: compatibility of API groups between clusters for each custom resource, and if a Kubernetes version upgrade breaks the compatibility of core/native API groups. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). +**Note:** Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. [1]: how-velero-works.md#set-a-backup-to-expire [2]: restic.md diff --git a/site/content/docs/v1.6/migration-case.md b/site/content/docs/v1.6/migration-case.md index c547ebbb4b..9ad7b809ae 100644 --- a/site/content/docs/v1.6/migration-case.md +++ b/site/content/docs/v1.6/migration-case.md @@ -53,6 +53,7 @@ If you encounter issues, make sure that Velero is running in the same namespace ## Migrating Workloads Across Different Kubernetes Versions Migration across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered: compatibility of API groups between clusters for each custom resource, and if a Kubernetes version upgrade breaks the compatibility of core/native API groups. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). +**Note:** Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. [1]: how-velero-works.md#set-a-backup-to-expire [2]: restic.md diff --git a/site/content/docs/v1.7/migration-case.md b/site/content/docs/v1.7/migration-case.md index c547ebbb4b..9ad7b809ae 100644 --- a/site/content/docs/v1.7/migration-case.md +++ b/site/content/docs/v1.7/migration-case.md @@ -53,6 +53,7 @@ If you encounter issues, make sure that Velero is running in the same namespace ## Migrating Workloads Across Different Kubernetes Versions Migration across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered: compatibility of API groups between clusters for each custom resource, and if a Kubernetes version upgrade breaks the compatibility of core/native API groups. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). +**Note:** Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. [1]: how-velero-works.md#set-a-backup-to-expire [2]: restic.md From fb2722ffe5cef3a551e260d56e172b97d7aa30fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Wed, 12 Jan 2022 14:09:48 +0800 Subject: [PATCH 007/366] Fix bug to make the restic prune frequency configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduces the installation option "--default-restic-prune-frequency" to make restic prune frequency configuration in the previous release, but there is a bug that make the option don't take effect. This commit fixes the bug by removing the evaluation part. The restic repository controller will take care the prune frequency for the repository Fixes #3062 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4518-ywk253100 | 1 + pkg/restic/repository_ensurer.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4518-ywk253100 diff --git a/changelogs/unreleased/4518-ywk253100 b/changelogs/unreleased/4518-ywk253100 new file mode 100644 index 0000000000..e1b9181743 --- /dev/null +++ b/changelogs/unreleased/4518-ywk253100 @@ -0,0 +1 @@ +Fix bug to make the restic prune frequency configurable \ No newline at end of file diff --git a/pkg/restic/repository_ensurer.go b/pkg/restic/repository_ensurer.go index d80dcb6eb8..f1f4f168a3 100644 --- a/pkg/restic/repository_ensurer.go +++ b/pkg/restic/repository_ensurer.go @@ -160,7 +160,6 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam Spec: velerov1api.ResticRepositorySpec{ VolumeNamespace: volumeNamespace, BackupStorageLocation: backupLocation, - MaintenanceFrequency: metav1.Duration{Duration: DefaultMaintenanceFrequency}, }, } From 2734bac900df1a840fe65eec111be95b194226a3 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 5 Jan 2022 14:14:55 -0500 Subject: [PATCH 008/366] Clarify restore hook includedResources when not specified Signed-off-by: Tiger Kaovilai Rearrange for clarity Signed-off-by: Tiger Kaovilai --- site/content/docs/main/api-types/restore.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/api-types/restore.md b/site/content/docs/main/api-types/restore.md index 817e549bcb..80f7586b63 100644 --- a/site/content/docs/main/api-types/restore.md +++ b/site/content/docs/main/api-types/restore.md @@ -88,8 +88,8 @@ spec: # Array of namespaces to which this hook does not apply. Optional. excludedNamespaces: - ns3 - # Array of resources to which this hook applies. The only resource supported at this time is - # pods. + # Array of resources to which this hook applies. If empty, it applies to all supported resources. + # The only resource supported at this time is pods. includedResources: - pods # Array of resources to which this hook does not apply. Optional. From 2dd85c9c42b49979d7c1fae2afdd50d00baff257 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Fri, 14 Jan 2022 16:24:59 -0800 Subject: [PATCH 009/366] Convert PodVolumeBackup to Kubebuilder framework Signed-off-by: F. Gold --- .../v1/bases/velero.io_podvolumebackups.yaml | 37 +- config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 20 + pkg/apis/velero/v1/download_request_types.go | 2 +- ...e_backup.go => pod_volume_backup_types.go} | 20 +- .../v1/pod_volume_operation_progress.go | 2 +- pkg/builder/pod_builder.go | 2 +- pkg/builder/pod_volume_backup_builder.go | 14 +- pkg/cmd/cli/restic/server.go | 51 +- pkg/cmd/server/server.go | 2 +- .../pod_volume_backup_controller.go | 539 +++++++----------- .../pod_volume_backup_controller_test.go | 396 ++++++++----- .../pod_volume_restore_controller.go | 15 +- .../pod_volume_restore_controller_test.go | 2 +- pkg/controller/restore_controller.go | 2 +- pkg/controller/schedule_controller_test.go | 2 +- pkg/restic/exec_commands.go | 2 +- pkg/restic/executer.go | 37 ++ pkg/restic/mocks/fake_restic_executer.go | 37 ++ pkg/test/fake_file_system.go | 7 +- pkg/util/filesystem/file_system.go | 8 +- 21 files changed, 700 insertions(+), 499 deletions(-) rename pkg/apis/velero/v1/{pod_volume_backup.go => pod_volume_backup_types.go} (68%) create mode 100644 pkg/restic/executer.go create mode 100644 pkg/restic/mocks/fake_restic_executer.go diff --git a/config/crd/v1/bases/velero.io_podvolumebackups.yaml b/config/crd/v1/bases/velero.io_podvolumebackups.yaml index 80364ecbee..efd4f219c6 100644 --- a/config/crd/v1/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/v1/bases/velero.io_podvolumebackups.yaml @@ -16,7 +16,40 @@ spec: singular: podvolumebackup scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - description: Pod Volume Backup status such as New/InProgress + jsonPath: .status.phase + name: Status + type: string + - description: Time when this backup was started + jsonPath: .status.startTimestamp + name: Created + type: date + - description: Namespace of the pod containing the volume to be backed up + jsonPath: .spec.pod.namespace + name: Namespace + type: string + - description: Name of the pod containing the volume to be backed up + jsonPath: .spec.pod.name + name: Pod + type: string + - description: Name of the volume to be backed up + jsonPath: .spec.volume + name: Volume + type: string + - description: Restic repository identifier for this backup + jsonPath: .spec.repoIdentifier + name: Restic Repo + type: string + - description: Name of the Backup Storage Location where this backup should be + stored + jsonPath: .spec.backupStorageLocation + name: Storage Location + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: properties: @@ -153,6 +186,8 @@ spec: type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index b23a26318c..a606699208 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -33,7 +33,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YK\x8f\xe3\xb8\x11\xbe\xfbW\x14f\x0f}\x19˳\xc9!\x81/\x81Ǔ\x00\x83\xf4l7Ɲ\xce!\t\xb04Y\xb2\xb9M\x91\nI\xd9\xeb\x04\xf9\xefA\xf1!ɒ\xdcv\x0f\x92]^\xba\xcdG\xb1\xea\xab75\x9b\xcf\xe73V\xcbg\xb4N\x1a\xbd\x04VK\xfc٣\xa6_\xaex\xf9\xbd+\xa4Y\x1c\xbe\x9f\xbdH-\x96\xb0n\x9c7\xd5Wt\xa6\xb1\x1c?a)\xb5\xf4\xd2\xe8Y\x85\x9e\t\xe6\xd9r\x06\xc0\xb46\x9eѴ\xa3\x9f\x00\xdcho\x8dRh\xe7;\xd4\xc5K\xb3\xc5m#\x95@\x1b\x88\xe7\xab\x0f\x1f\x8a\xdf\x15\x1ff\x00\xdcb8\xfe$+t\x9eU\xf5\x12t\xa3\xd4\f@\xb3\n\x97\xb0e\xfc\xa5\xa9\x9d7\x96\xedP\x19\x1e\xef*\x0e\xa8КB\x9a\x99\xab\x91\xd3\xd5;k\x9az\t\xddB\xa4\x90؊\"}\f\xc46\x91\xd8}\"\x16֕t\xfeϗ\xf7\xdcK\xe7þZ5\x96\xa9Kl\x85-no\xac\xff\xa1\xbbz\x0e[\xa7\xe2\x8aԻF1{\xe1\xf8\f\xc0qS\xe3\x12\xc2\xe9\x9aq\x143\x80\x84Y\xa06\a&D\xd0\x02S\x8fVj\x8fvmTS\xe9\xf6.\x81\x8e[Y\xfb\x80r\x94\x05\x920\x90\xa5\x01\xe7\x99o\x1c\xb8\x86\xef\x819X\x1d\x98Tl\xabp\xf1\x17\xcd\xf2\xff\x81\x1e\xc0O\xce\xe8G\xe6\xf7K(⩢\xde3\x97W\xa3\x8e\x1e{3\xfeD\x028o\xa5\xdeM\xb1tϜ\u007ffJ\x8aV\xeb \x1d\xf8=\x82b\u0383\xa7\t\xfa\x15\x11\x02\x82\b!#\x04G\xe6\xd2=\x00\x87H%`4ͩ\x1a\xddu\xc66\xb1\x02\xcf\x03*\x91\u007f\x9aI\xdc\xf7\xc8f\xc3/FF{Fw\xb5\xc3K\xc4Π\xf8\x84%k\x94\xef\x8bJZR}\xbb<\x17\xabF^\x88x\xea\xec\xc6Ogs\xf1֭1\nY\xa4\x12w\x1d\xbe\x8fV\xc8\xf7X\xb1e\xdaljԫ\xc7\xcfϿݜMÔ!\r\x9c\x82\x14\xc7z\xba٣Ex\x0e\xfe\x17\xf5\xe6\x92h-M\x00\xb3\xfd\t\xb9\xef\x94X[S\xa3\xf52;K\x1c\xbd ՛\x1d\xf0tGl\xc7] (:a\xb4\xa3\xe4/(\x92\xa4`J\xf0{\xe9\xc0bmѡ\xf6}x[\xc6J`:\xb1W\xc0\x06-\x91!_n\x94\xa0\xa0v@\xeb\xc1\"7;-\xff\xd5\xd2v\xe0M2^\x8f\xce\x0fh\x06\xff\xd4L\x91\xa96\xf8\x1e\x98\x16P\xb1\x13X\xa4[\xa0\xd1=za\x8b+\xe0\vٻԥY\xc2\xde\xfb\xda-\x17\x8b\x9d\xf498sSU\x8d\x96\xfe\xb4\bqVn\x1bo\xac[\b<\xa0Z8\xb9\x9b3\xcb\xf7\xd2#\xf7\x8d\xc5\x05\xab\xe5<\xb0\xaecЬ\xc4w6\x85sww\xc6\xeb\xc8k\xe3\bQ\xf3\x15\rPČV\x10\x8fF):\xa0i\x8a\xd0\xf9\xfa\xc7\xcd\x13䫃2\x86\xe8\aܻ\x83\xaeS\x01\x01&u\x896*\xb1\xb4\xa6\n4Q\x8b\xdaH\xed\xc3\x0f\xae$\xea!\xfc\xae\xd9Vғ\xde\xff٠\xf3\xa4\xab\x02\xd6!c\xc1\x16\xa1\xa9\x83\xdf\x17\xf0YÚU\xa8\xd6\xcc\xe1\xff]\x01\x84\xb4\x9b\x13\xb0\xb7\xa9\xa0\x9fl\x87\x9b#j\xbd\x85\x9c\v/\xe8kҋ75\xf23\xff\x11\xe8\xa4%\v\xf7\xccc\xf0\x8b\x01\xae\xc9\xc5/'\xd3<\xa6\x9d\x9b\x06\xe3\x1c\x9d\xfbb\x04\x0eW\x06,\xafڍg<\xd6h+\xe9BZ\x84\xd2\xd8a\xc6`m\x04\xee\x8f\x1c\xa9\x8a\xd1\x1a\xea\xa6\x1a32\x87\xaf\xc8ăV\xa7\vK\u007f\xb5ҏ/\xba\xa0H\x1a\x91\xc5\xcdI\xf3G\xb4҈+\xc2\u007f\x1clo!؛#\x94\xc1\xac\xb5W'\x8aA\xee\xa4\xf98\xda\xe6\xb1z\xfc\x9c#ot\xa0\xe4o\t\xab\x02V\xc9sM\t\x1f@HG\x05\x80\vD\xc7`QyF\xebK\xf0\xb6y\x93\xf8\xdc\xe8R\xee\xc6B\xf7k\x9aK\x16s\x85\xf4\x00\xb9u\xb8\x89B\x13YGm\xcdA\n\xb4s\xf2\x0fYJ\x9e8il\xcc\\\xa5D%\xdcX\xd2\v^\x16D\xb1(ȫ\x99\xba\xa2\xc3u\xbb1\x94\xc6L\xeah\xc1\x1d\x81\x10ll\x95R\xaa\xf6\xa8E[\x8d\x9cqcB\xd4r(\xe0(\xfd>\x86C5\xe5w\xf0\xaa\xef\xd1x\xc1\xd3\xd4\xf4\x80\xf7\xa7=\xd2Θ@\x11\x1cr\x8b>X\x1b*2\x1f2\xa5\x02\xe0K\xe3B@\x1dƉ\x86\x8d\x19\xf0\x9a\xf9=H\xed\xa4@`\x13\xf0\xc7b킠m\xfe\u007fHQ\xe4\x1b\xd4\xf3\x9a\xb7Gv\xde\xe2\xf0\x19\xe3+\xfe\U000d8db5(\xe4\xdf)\xf2\x9fׂ\x97\xfcxR\xa2C\xfb`\xf0\xa7Xa\xf1\x89Ty\xc6\xcc\xf3\xf8\xc4+\x95Z~\xb6\x98rf\xaa\v\x8c\xb5\xe8j\xa3\x055O\xb7\xd5i\x1d\xcb\xff\xbbjmZ\xad\xf3\xf3(7X\xcbZ\xb8\xa9U\tO4onV\xe2\xc3U\xbf\x150[G\x9dbׯ\fd\xfcEڔw\xbd>\x85\xfaa\r\x8d\x0e\x95Z\xc8\xf8\x05\xfc]\xc3'\xeam);\x89%\xf1m\xa7\f@:\xd0\xe6H\xc7{\xf4\x02\t0:\xe6k\xea֘\x16\xa9\x19\x0eKG\xa9\x14el\x8b\x959Lfl*4-\xaa\x130G\xa6s\xf8M\xf1\xa1x\xf7\xabuA\x8a9OM\r\x8a\xafx\x90\xe3W\x9e1\xba\xf7\xa3\x13\xd9\xf1[w\xa0\x1f?\xe6fyaӶ\x1f'\xc0(\xa5\xa2Zp\"Nt\x15\xc3\xf8=\xf2\xe3\xe6\xfe΅\x12\x1e\xb5\x9f*\xfb\x8eh1tL(\xa8\x8a7\xe9]\xa2q\x1e\xed\x84\x01\xb4\xda\v:\ae\xf4n\xe08q\xa4W\n\xaaТA\x19\v\x02=\xa5&\xbd\x03\xbegz\x87\xdd+T\xe2\xffuN\xc9|\x066\xd3Y\x88ԗ\xcc\xe3&\x8d>ɩ2}\xf4\x02\xdcm\x9e~\xfd\xcd\xdcg\xcd^ls\xae\xe0>ڟ\xb34\x81:\xf7\u074bp7\xbe\xbd\xbd\x1d?7߀\xc4[\xdf\xc2_y׀#sݫ\xf8\xaf\x87C\xf8@p-\x81Ӟ,-o,\xb5i\u074bSp\xb8\xa9\xb8}\xfb\xd3\xcdj\xf0\x1d\xa3\xbf6\xfe\xcaq\x83\\\x93yl4\x19sQ\x0f\xb3\x14Z\xfa3Ͷ}\x85͜\xa7l\b\xff\xfeϬK\x8c\x94}j\x8f\xe2\x87\xe1g\xacw\xd19\xf3\xb7\xa8\xf0\x93S\xc5\x10\xbf\xc3\xc1\xdf\xfe1\x8b\x17\xa3x\xce\x1f\x8fh\xf2\xbf\x01\x00\x00\xff\xffHX\xbeK\x01\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_o\x1b\xb9\x11\u007fק\x18\xf8\x1e\xdc\x03\xbc\xd2ݵh\v\xbd]\xec^\xe1\xf6\xce1\"7/A\x1eF\xcb\xd9]\xd6\\\x92%\xb9RԢ߽\x18\x92\xab?\xab\x95d\x1bH\xba/\x89\xc9\xe1p\xfe\xcfo\xa8IQ\x14\x13\xb4\xf2#9/\x8d\x9e\x03ZI_\x02i\xfe\xcbO\x9f\xff\xec\xa7\xd2\xccV?N\x9e\xa5\x16s\xb8\xed|0\xed\a\xf2\xa6s%\xddQ%\xb5\f\xd2\xe8IK\x01\x05\x06\x9cO\x00Pk\x13\x90\x97=\xff\tP\x1a\x1d\x9cQ\x8a\\Q\x93\x9e>wKZvR\tr\x91y\u007f\xf5\xea\x87韦?L\x00JG\xf1\xf8\x93l\xc9\al\xed\x1ct\xa7\xd4\x04@cKs\xb0F\xac\x8c\xeaZZb\xf9\xdcY?]\x91\"g\xa6\xd2L\xbc\xa5\x92/\xad\x9d\xe9\xec\x1cv\x1b\xe9l\x16()\xf3h\xc4\xc7\xc8\xe6]d\x13w\x94\xf4\xe1\xefc\xbb\xbfJ\x1f\"\x85U\x9dCu,D\xdc\xf4RםBw\xb4=\x01\xf0\xa5\xb14\x87\a\x16\xc3bIb\x02\x90u\x8fb\x15Y\xbbՏ\x89U\xd9P\x8bI^\x00cI\xff\xfcx\xff\xf1\xf7\x8b\x83e\x00\xeb\x8c%\x17d\xafZ\xfa\xf6<\xba\xb7\n ȗN\xda\x10\xed}\xcd\f\x13\x15\bv%y\b\r\xf5B\x91\xc82\x80\xa9 4҃#\xebȓN\xce=`\fL\x84\x1a\xcc\xf2\x9fT\x86),\xc81\x1b\xf0\x8d\xe9\x94\xe0\bX\x91\v\xe0\xa84\xb5\x96\xff\xde\xf2\xf6\x10L\xbcTa\xa0l\xe1\xdd'u \xa7Q\xc1\nUG7\x80Z@\x8b\x1bpķ@\xa7\xf7\xf8E\x12?\x85ߌ#\x90\xba2shB\xb0~>\x9b\xd52\xf4\x91\\\x9a\xb6\xed\xb4\f\x9bY\fJ\xb9\xec\x82q~&hEj\xe6e]\xa0+\x1b\x19\xa8\f\x9d\xa3\x19ZYD\xd1u\x8c\xe6i+\xbes9\xf6\xfd\xf5\x81\xacaþ\xf5\xc1I]\xefm\xc4@;\xe3\x01\x0e5\x90\x1e0\x1fMZ\xec\f\xcdKl\x9d\x0f\u007fY[S\xe1\x87`F$[&\x17\x90\x80a@\xc0٠\x803U}T\xe2\x9f\x1f\xef\xfbJ\xde\x1b1\xcb\x1e\x8e\xef\xbd`\x1f\xfe*IJ\xbc\x9ft;!S\xe69Y\xd7\xe4\"p\x19\xfbbS\xe1R\xfd=\x18\xc7\x16\xd0f\x8fEd\xcc\xdeK\x85\x92đП~\xfa|R\xe2C{\x81Ԃ\xbe\xc0O u\xb2\x8d5\xe2\xfb)<\xc5\xe8\xd8\xe8\x80_\xf8\xa6\xb21\x9eNY\xd6h\xb5a\x9d\x1b\\\x11x\xd3\x12\xacI\xa9\"\xe1 \x01kܰ\x15z\xc7q\xbc!Xt\xe1l\xb4\xf6\xe8\xe7\xe9\xfd\xdd\xfby\x92\x8c\x03\xaa\x8e\x95\x98\xbbf%\x19\xcd0\x8cI\xbd8F\xe3Q3\xef?ߥ\xf0\t\x06\xca\x06uMI_\x82\xaa\xe3\xee8\xbd~K\x1e\x1fC\x92\xfe\x1b\x81&\xc3\xc2\xf1\u007fk\xee/T.\"\xe8\x17(\xf7\xb0\x17\xe5g\x95\xe3Y\xc5i\n\x14\xf5\x13\xa6\xf4\xacZI6\xf8\x99Y\x91[IZ\xcf\xd6\xc6=K]\x17\x1c\x9aE\x8a\x01?\x8b\xe3\xc6\xec\xbb\xf8ϛu\x89\x83\xc2K\x15\x8a\xc4\xdfB+\xbe\xc7\xcfޤT\x8fa_\xdeǮ\x17\x19Y\r\xcfrZ\xac\x1bY6\xfdp\x92k\xec\x89d\x92\x8c\x84E*ͨ7_=\x94٠\x9dc\x896E\x1e\x80\vԂ\xff\xef\xa5\x0f\xbc\xfe&\vv\xf2E\xe9\xfb\x8f\xfb\xbbo\x13\xe0\x9d|S\xae\x9e\x00\xe0)F\xac\xb9\x17l\xcaJ\x92\xbb\x00\xcc>\x1c\x10\xf7\xd0q\x04\xb1ni^\x85\f\x03\xd6#P\f\x85\x88\xcf\x1e\xa8\x1e\xcf\x02\xb6\xb3\x168P\xe3\tk\x0f\xe8\b\x10Z\xb4\xec\xb9g\xda\x14\xa9\xc5[\x94ܟ\xb9\x05g̳$@k\x95\x1cmŹ\x91g\x10\x9a\xf1>\x0f\xdaX\xfbS\xba\x8f\xfa!q\xb8`\xff4\xe0\x8cA\xf6,@\xc27[\xd8\x1e\f,\xc7R\xf4\f(>iE\x9eK\x19\xad\x1d\x8aX\x8c\x0fP\x03\x1a\x1e(\x06Kֈ\xc1\xcaa$\x0e6\x93~/\x9a*\x03\x86οb\xae\x8c\xf4\xbdMS\x15\t\x99K\x84\xd0o\x9d,K\xc3\xe8\xf4\xf0i\xed\xbc{o\x8fO\xc4G\x1c'\x92pA\xb6\x1c\xb39\xca\xd6\xe8\xfb;\xc6FC\xd8c\x97Nƺ\xcd\xdcHD\xe8\xc8ȶB\xa9H@\xff\xb67<3\xc2u\x9f˒*.r\x9dU\x06E?\x90e\xf1\xb6\xf0\x8c\xe7\xf5\xf8:r\xed\xcf\xf0\xec<\x898ɏ\x18\xe1\x18\xb2UƵ\x18\xe6 0P1\xcaTwJ\xe1R\xd1\x1c\x82뎷\xcf\x14\x8b\x96\xbc\xc7\xfaR*\xfe\x96\xa8Ҝ\x9a\x8f\x00.M\x17\xb6\x83\xeaAQ\xb8\xf69\xa6^7+\x8f\x8e\x80\x87\xe1\x8c\f\xd1}\x86\xaaJ\xc53\xfb\x85`\xf7 \x1c\xa5Z\xd2x\xab{KM\x00\xb0\r\xfaK\xa6zd\x9a\xb1\x04\xdbV\xaf\xb3\x19\xc6\x1f\xe9\xae=\xbe\xa5\x80\aZ\x8f\xac\xde\xebGgjG\xfe8p\x8a>\xbeF\xaay\x01\xbf\xc4lx\x95\xfe\xf9\xa2K&\xc8d\xd0\x18\xd5'\xb3\t\xa8@w\xed\x92\x1c\xdba\xb9\t\xe4\x0f\xcb\xf9ثD\x9cfvf\xdc;\xdf\xfb/q\xca\x03Z\x89:\xbe\x1erv\x05\x03Bz\xabp3¸W$\"\x16N..\x01\xbbx\xee\x93ڒ\x8b[\xaf}M\x892\xdd\x19}\x02_\xf7\xf9,u\xf8\xe3\x1f\xce\xe0\x1b\xa9\x03Ճ\xe6\x90\xf7ٜ\xef\xf8\x96\xafsÙ\xd6\xed5Zߘp\u007fw!\n\x16[\xc2>\x1bv@)־\xf8\xb6\x99\x89r(\x8c\xb9j[[^\x95\xaa>\xa0\v/mE\x8b\x03\xe2\v](r\x1e\xefA\v\xb2\xe88\xd3\xe3K\xf8\xed\xf0\xb7\xa6\x1b\xf02>\xef1\xdeJ\x00,\rߞ\x9b\x13\x03K\xe3h\xa4d\xc2q[9h\"\x87\xe2\u007f\xcb\xfe1\x1a'G\x8bQr\xb1\xc7;?\x11\xe7\x95\x1d\x86\xc1\x92\xa7\x03\x12\x0f\xc3\xdfӮ\xd2\xebM\xff\x03Y\xfc\xb34:Ae?\x87O\x9f'\x90\x9f\x8d?\xf6\xbf{\xf1\xe2\xff\x02\x00\x00\xff\xffTTw\xa4\x84\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWty\x0f\xceV\r\xa9\xddI*I\xe9\xb6kgSJv=\xae\x913\x97\xa99@DS\xec\x98\x04\x18\xa0)YI忧\x1a \xf4\xa4\x1ev\xd5Lx\xb1\x85G\xe3\xeb\xaf\x1f\xe8&GY\x96\x8dTK\x9f\xd0y\xb2f\x02\xaa%|a4\xf2\xcb\xe7\xcf\u007f\xf69\xd9\xf1\xf2\xc7\xd13\x19=\x81\xbbγm>\xa2\xb7\x9d+\xf0\x1eK2\xc4dͨAVZ\xb1\x9a\x8c\x00\x941\x96\x95\f{\xf9\tPX\xc3\xce\xd65\xbal\x81&\u007f\xee\xe68\xef\xa8\xd6\xe8\x82\xf0t\xf4\xf2\x87\xfcO\xf9\x0f#\x80\xc2a\xd8\xfeD\rzVM;\x01\xd3\xd5\xf5\b\xc0\xa8\x06'\xd0Z\xbd\xb4uנC\xcf֡ϗX\xa3\xb39ّo\xb1\x90S\x17\xcev\xed\x04\xb6\x13qs\x8f(j\xf3h\xf5\xa7 \xe7c\x94\x13\xa6j\xf2\xfc\xf7\xc1\xe9_\xc9sX\xd2֝S\xf5\x00\x8e0\xeb\xc9,\xbaZ\xb9\xe3\xf9\x11\x80/l\x8b\x13x\x10(\xad*P\x8f\x00z\x02\x02\xb4\xacWq\xf9c\x94UTب\x88\x19\xc0\xb6h~z\x9c~\xfa\xfdlo\x18\xa0u\xb6EǔԋώYwF\x014\xfa\xc2Qˁ\xf4[\x11\x18W\x81\x16{\xa2\a\xae0\x81B\xddc\x00[\x02W\xe4\xc1a\xebУ\x89\x16\xde\x13\f\xb2H\x19\xb0\xf3\u007fb\xc19\xccЉ\x18\xf0\x95\xedj-n\xb0D\xc7ర\vC\xff\xde\xc8\xf6\xc06\x1cZ+ƞ\xe3\xedC\x86\xd1\x19U\xc3R\xd5\x1d\xbe\x03e44j\r\x0e\xe5\x14\xe8̎\xbc\xb0\xc4\xe7\xf0\x9bu\bdJ;\x81\x8a\xb9\xf5\x93\xf1xA\x9cܹ\xb0M\xd3\x19\xe2\xf58x&\xcd;\xb6Ώ5.\xb1\x1e{Zd\xca\x15\x151\x16\xdc9\x1c\xab\x96\xb2\x00\xdd\x04\x97\xce\x1b\xfd\x9d\xeb\x03\xc0\xdf\xeea\xe5\xb5\xd8ֳ#\xb3ؙ\b\xcev\xc6\x02\xe2m@\x1eT\xbf5j\xb1%Z\x86\x84\x9d\x8f\u007f\x99=A::\x18\xe3\x90\xfd\xc0\xfbv\xa3ߚ@\b#S\xa2\x8bF,\x9dm\x82L4\xba\xb5d8\xfc(jBsH\xbf\xef\xe6\r\xb1\xd8\xfd_\x1dz\x16[\xe5p\x17b\x1c\xe6\b]\xab\x15\xa3\xceaj\xe0N5X\xdf)\x8f_\xdd\x00´τ\xd8\xebL\xb0\x9b\x9e\x0e\x17G\xd6v&R\n9a\xafô0k\xb1\x10\xf3\t\x83\xb2\x95J*Bl@i\x1d\xa8\xa3\xf5\xf9\x9e\xe8\xe1Еg\xae\x8a箝\xb1uj\x81\xbf\xda(\xf3p\xd1\x01\xb6\x9f\x87\xf6$p\x92Yb\x18c/\x1c|\\y$\x14\xa0N\x9bW\x15:\f{$\x8bQ!\xeee=\xb1uk\x11\x1cT\xd2\xf9\x91\x84\x13\x86\b*[}A\x8dG\xdb\a\x84\xc3\x12\x1d\x1aq\xf7\x98!Z\x1b\xf2\b+2),b\x8a\x05\xb6\x03Z\xcc#\xeaa\x88\xa7\xa9\x873\xd9s\x10\xf0O\x8fӔ1\x13\xc3=t>>\xf7\x02=\U00094135~T\\]q\xf6\xed\xb4\x8c\x87\x85\xdc\xc1\x16\x14\xb4\x84\x05\xee%c \xe3\x19\x95\x06[\x0eJ\x94[\x1b$\xc0\x1c\xf6;\xde\xc5Lѧ\xa4m\n\x17\xeaAI\x8e\"\r\u007f\x9b}x\x18\xffu\x88\xf9\x8d\x16\xa0\x8a\x02\xbd\bR\x8c\r\x1a~\a\xbe+*P^\xd4 \x87z&3y\xa3\f\x95\xe89\xef\xcf@\xe7?\xbf\xff2\xcc\x1e\xc0/\xd6\x01\xbe\xa8\xa6\xad\xf1\x1dPd|\x93\xfe\x92ϐ\x8ftl$\u008a\xb8\xa2\xc3KkÀxW\xaf\xf6*\xa8\xcb\xea\x19\xc1\xf6\xeav\b5=\xe3\x04n$\xcaw`\xfeG\x02\xeb\xbf7'\xa4\xfe.\x06Ѝ,\xba\x89\xe06\xf7\xddnDnAr\xa5\x18\xd8\xd1b\x81.\x14\bCOHޒ\x12\xbf\a\xeb\x84\x01cwD\x04\xc1b\xbd\x98\x8fP\x1f\x81\xfe\xfc\xfe\xcbI\xc4\xfb|\x01\x19\x8d/\xf0\x1e\xc8DnZ\xab\xbf\xcf\xe1)x\xc7ڰz\x91\x93\x8a\xcaz<Ŭ5\xf5Zt\xae\xd4\x12\xc1\xdb\x06a\x85u\x9d\xc5zC\xc3J\xad\x85\x85d8\xf17\x05\xadr|\xd6[S\x95\xf1\xf4\xe1\xfe\xc3$\"\x13\x87Z\x84|'\xb7SIR5H\xb9\x10\xef\xbc\xe0\x8dG\x97fz|\x17݇-\x14\x952\v\x8c\xfa\"\x94\x9d\xdcB\xf9\xed[\xe2\xf8\xf8\xeaO\xcf@\tp\x988\xfeo\x97\xe8\x95ʅJ\xf5\n\xe5\x1ev\xbc\xfc\xacr\xd2\x188\x83\x8cA?m\v/\xaa\x15ز\x1f\xdb%\xba%\xe1j\xbc\xb2\xee\x99\xcc\"\x13\xd7̢\x0f\xf8q(\xed\xc7߅?o\xd6%\x14\xe4\xd7*\x14\x16\u007f\v\xad\xe4\x1c?~\x93R\xa9V\xbc\xfe\x1e\xbb\x9d\xf5\x05\xcc\xe1^\t\x8bUEE\x95\x9a\x80>Ǟ\b&\x92\x8aS\xc7Ԭ\xcc\xfa\xab\xbb\xb2\x10\xda9A\xb4\xce\xfan3SF\xcb\xff\x9e<\xcb\xf8\x9b\x18\xec\xe8\xaa\xf0\xfd\xc7\xf4\xfe\xdb8xGo\x8a\xd5\x13\x85n\xf4\x91\xd6N\xb5PY\x12\xba\vu\xd9ǽũ\xae\x1c\xa8\v7k^U\x18z\xa3Z_Y\x9e\xde_\xc01\xdb,L\x18\xb6\x06\xe8\xcb\xc1$K\x1c\xf7l\x15x\x06O\x14u\x01K\xac\xed\x87j\xec\x1eI\xac9\u0088Ե\x01\xcfp\xb0\xbe\x16\xa1\xb4dR@\xed#̆;\x87\x835\xad\xd5\a#\xfb\x9ep0\xb95\xcd\xc1DT\U000aad8a\x15w\xfe5\x8dUؐ\x98\x8d\xf1ͽ\x98Pܾ\xb9\xb5*\xac\x14\x8e\xfb\xaf\x98\xce[\xf9\xeexGx\x8f\xe1tD\xc7\xd4`\xe8W\x02\x0eX)\x9f\x0e\x19\xb2(\xecȋ[CN\x15q\xa8CY'Ug\xa9\xa8F\r\x9b\x97\\\xf0$\x1dfh\xe8o\x87\xaa\x98$\xa8\xf3\xa8C\xef9\x00\xfax_i]\xa3x\x02\xd2\xc6g\"\xe2h\x85\xe9\xeaZ\xcdk\x9c\x00\xbb\xeex\xfaL\x005\xe8\xbdZ\\\x8a\xa0\xdf\xe2\xaa\xd8\xf1\xf5[@\xcdmǛ\x96\xaf\x0f\xa5\x9e\x8a[\xdf{\xc1\xeb\xda\xceJ\xf9KP\x1ee͐\xc7m\x82\xfa\xbc\xcbɃ\xa6k\x8e\x8f\xc9\xe0\x01W\x03\xa3S\xf3\xe8\xec¡?\xb6L\x96\f8\xd0\x04d\xf0K\xf0\x8eW\x11\xd0\x1ft\x89\x83~\x19T\xb6N\xdemY\xd5`\xbaf\x8eN\x88\x98\xaf\x19}b$\xa5\x86\xa1\x1e:\xd4\xde[&\xb7\x12R\xb6\x8b\xa2\xfan\xa2P&\xbcR\x12\xffe\v\x9a|[\xab\xf5\x80ܤI\xb8^\xc5}%\x8e\xb6\x1e\x93\xa2P\xc2?̽\xb6\xf7\x0f\xa0\xee\xad9Q\r\xa6\x90!\xc3\u007f\xfcÙۘ\f\xe3\xe2 \x95\xf6\xf3B\xe8\xcfr\xca\xd79\xe1̅\xefY9\xbe6\xed\xcd\xf6\x16_\xcaxA\xf4p\xbe\xdbM]ljj\xff\x98o\x99\xa3\x06\x89:\x1a\f\xc8\xf5\x8e\xec\xfe\xbdY?\xb2\xbd\xd9T!\xc5\x1c\xea\x87\xc3O\r77{_\x0e\xc2\xcf\xc2\x1aM\xf13\t|\xfe2\x82\xfe]ڧ\xf49@\x06\xff\x17\x00\x00\xff\xffñ\x1b\xae\xa0\x19\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xc1n\xe36\x10\xbd\xfb+\x06\xdb\xc3^*y\x17=\xb4ЭM[ h\x12,\x9cE.E\x0f\x145\xb2\xa7\xa1H\x96\x1c:u\xbf\xbe\x18J\x8aeY\x897\v\xacn&g\x1e\xdf̛\x19ҫ\xa2(V\xca\xd3\x03\x86H\xceV\xa0<ῌV~\xc5\xf2\xf1\xa7X\x92[\xef?\xae\x1e\xc96\x15\\\xa5Ȯ\xdb`t)h\xfc\x15[\xb2\xc4\xe4\xec\xaaCV\x8dbU\xad\x00\x94\xb5\x8e\x95,G\xf9\t\xa0\x9d\xe5\xe0\x8c\xc1Plі\x8f\xa9\xc6:\x91i0d\xf0\xf1\xe8\xfd\x87\xf2\xc7\xf2\xc3\n@\a\xcc\ue7e9\xc3Ȫ\xf3\x15\xd8d\xcc\n\xc0\xaa\x0e+\b\x18\x99t@\xef\"\xb1\v\x84\xb1ܣ\xc1\xe0Jr\xab\xe8Q˱\xdb\xe0\x92\xaf\xe0\xb8\xd1{\x0f\x94\xfap6\x19h3\x02\x1d\xf2\x96\xa1\xc8\u007f,n\xdfP\xe4l\xe2M\n\xca,\x11\xc9ۑ\xec6\x19\x15\xce\f䀨\x9d\xc7\n\ue10bW\x1a\x9b\x15\xc0\x90\x82̭\x18\x82\xdc\u007f\xec\xb1\xf4\x0e;Փ\x06p\x1e\xedϟ\xae\x1f~\xb8?Y\x06\xf0\xc1y\fLc|\xfd7\x11v\xb2\n\xd0`ԁ<紿\x17\xc0\xde\n\x1aQ\x14#\xf0\x0eGR\xd8\f\x1c\xc0\xb5\xc0;\x8a\x10\xd0\a\x8ch{\x8dO\x80A\x8c\x94\x05W\xff\x8d\x9aK\xb8\xc7 0\x10w.\x99F\na\x8f\x81!\xa0v[K\xff=cG`\x97\x0f5\x8aqH\xf2\xf1#\xcb\x18\xac2\xb0W&\xe1\xf7\xa0l\x03\x9d:@@9\x05\x92\x9d\xe0e\x93X\u00ad\v\bd[W\xc1\x8e\xd9\xc7j\xbd\xde\x12\x8f\x05\xad]\xd7%K|X\xe7ڤ:\xb1\vq\xdd\xe0\x1e\xcd:ҶPA\xef\x88Qs\n\xb8V\x9e\x8aL\xdd\xe6\xa2.\xbb\xe6\xbb0\xb4@|\u007f\u0095\x0f\xa2m\xe4@v;\xd9\xc8\xd5\xf6\x8a\x02Rn@\x11\xd4\xe0\xdaGqL\xb4,Iv6\xbf\xdd\u007f\x86\xf1\xe8,\xc6<\xfb9\xefG\xc7x\x94@\x12F\xb6\xc5Ћ\xd8\x06\xd7eL\xb4\x8dwd9\xffІ\xd0\xce\xd3\x1fS\xdd\x11\x8b\xee\xff$\x8c,Z\x95p\x95\xbb\x1cj\x84\xe4\x1b\xc5ؔpm\xe1Juh\xaeT\xc4o.\x80d:\x16\x92\xd8/\x93`:\xa0\xe6\xc6}\xd6&\x1b\xe3\fyA\xaf\xf9\\\xb8\xf7\xa8E>ɠ\xb8RK:\xf7\x06\xb4.\x80:\xb3/O\xa0\x97[W\xbeZ\xe9\xc7\xe4\xef\xd9\x05\xb5\xc5\x1b\xd7c\u038df\xdc~Y\xf2\x19\xc9\xc9d\xe9\xdb\x18\x97\rϰ\x01x\xa7xҿ\xac\xc8>\x8f\x81\xc5x^\x11!\v\xa1\xa4\x9d\xad\xb2\x1a\u007f\xcf\x15e\xf5\xe1BL\xb7\v.\x12\xd2\xce=\x81k\x19\xed\x14t\xe0\xba\x10I\x8d\x10\x92}\x13\xd9~~_7Rx-a\xb8@t33\x1f\xf3\xde&c\x06\xacB\xbb\xce+\xa6\xda\xe0\xf2\x91\xf2I\xd9P\x8fr\xe8{\xff\xeb\xf3\xbdw&u\xf8|\xdd\\\x88\xe0\xe1\xd4zZ8\xfd\xc2@EB\x81pzq\x9e~C\xadD\xf0\xae\x19H\f\x05\x1d%\xbe7\xc4 \x92S\xc0\xd9\x04-\x96\xdbcf\xb3Tm3\x93\xb9Ƴ\xedY\xfe\xbeh|\xb0\xe2\x14\xdf2@\xb2Øl\x9dB@\xcb\x03L\xbeQ\xbfz\x84\x18\x15y\xd2>\xf2\xa2\xbaP\x017\xe7\x1e#1\x01\x03\x96\x85i\xbf=\xa9\xf9-\x94E[\xea\xb4օNq\x05ra\x14\x02tf!\xef-\xacnP5\xe7}\\\xc0\x9d\xe3\xe5\xad\x17#\\슳\xc5(\xef\x92f\xa2s\xec\x1byX9\xf6\x90\xd2\x1a=cs7\u007f\xbd\xbf{w\xf2\x18\xcf?\xb5\xb3\r\xf5\u007f=\xe0ϿV=*6\x0f\xe3\x03[\x16\xff\x0f\x00\x00\xff\xff\x83\xf9\xd9\xe0\xf4\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85/volumes// diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 05c4ceb614..5503620c42 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 5b83e5a132..4edbb3046d 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go index 5900e673e9..777f78b08a 100644 --- a/pkg/controller/schedule_controller_test.go +++ b/pkg/controller/schedule_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index 63e3e13616..648dc68cc7 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restic/executer.go b/pkg/restic/executer.go new file mode 100644 index 0000000000..e89883e76c --- /dev/null +++ b/pkg/restic/executer.go @@ -0,0 +1,37 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restic + +import ( + "github.com/sirupsen/logrus" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// BackupExec is able to run backups. +type BackupExec struct{} + +// RunBackup is a wrapper for the restic.RunBackup function in order to be able +// to use interfaces (and swap out objects for testing purposes). +func (exec BackupExec) RunBackup(cmd *Command, log logrus.FieldLogger, updateFn func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { + return RunBackup(cmd, log, updateFn) +} + +// GetSnapshotID gets the Restic snapshot ID. +func (exec BackupExec) GetSnapshotID(snapshotIdCmd *Command) (string, error) { + return GetSnapshotID(snapshotIdCmd) +} diff --git a/pkg/restic/mocks/fake_restic_executer.go b/pkg/restic/mocks/fake_restic_executer.go new file mode 100644 index 0000000000..9dcae9574c --- /dev/null +++ b/pkg/restic/mocks/fake_restic_executer.go @@ -0,0 +1,37 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + "github.com/sirupsen/logrus" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/restic" +) + +// FakeResticBackupExec represents an object that can run backups. +type FakeResticBackupExec struct{} + +// RunBackup runs a Restic backup. +func (exec FakeResticBackupExec) RunBackup(cmd *restic.Command, log logrus.FieldLogger, updateFn func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { + return "", "", nil +} + +// GetSnapshotID gets the Restic snapshot ID. +func (exec FakeResticBackupExec) GetSnapshotID(cmd *restic.Command) (string, error) { + return "", nil +} \ No newline at end of file diff --git a/pkg/test/fake_file_system.go b/pkg/test/fake_file_system.go index ede30b0d1d..a659a5d912 100644 --- a/pkg/test/fake_file_system.go +++ b/pkg/test/fake_file_system.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + package test import ( @@ -36,6 +37,10 @@ func NewFakeFileSystem() *FakeFileSystem { } } +func (fs *FakeFileSystem) Glob(path string) ([]string, error) { + return afero.Glob(fs.fs, path) +} + func (fs *FakeFileSystem) TempDir(dir, prefix string) (string, error) { return afero.TempDir(fs.fs, dir, prefix) } diff --git a/pkg/util/filesystem/file_system.go b/pkg/util/filesystem/file_system.go index 6b67cf69d2..de477a31dc 100644 --- a/pkg/util/filesystem/file_system.go +++ b/pkg/util/filesystem/file_system.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" ) // Interface defines methods for interacting with an @@ -35,6 +36,7 @@ type Interface interface { DirExists(path string) (bool, error) TempFile(dir, prefix string) (NameWriteCloser, error) Stat(path string) (os.FileInfo, error) + Glob(path string) ([]string, error) } type NameWriteCloser interface { @@ -49,6 +51,10 @@ func NewFileSystem() Interface { type osFileSystem struct{} +func (fs *osFileSystem) Glob(path string) ([]string, error) { + return filepath.Glob(path) +} + func (fs *osFileSystem) TempDir(dir, prefix string) (string, error) { return ioutil.TempDir(dir, prefix) } From 48827d613d4a8c1b5019dcf6468962815972a290 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Wed, 26 Jan 2022 20:29:23 -0800 Subject: [PATCH 010/366] Run go mod tidy Signed-off-by: F. Gold --- go.mod | 2 ++ go.sum | 1 + 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 972a2a9ab4..60ee7f193d 100644 --- a/go.mod +++ b/go.mod @@ -66,6 +66,7 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gobuffalo/flect v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -107,6 +108,7 @@ require ( golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/tools v0.1.5 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect diff --git a/go.sum b/go.sum index 525808f44f..a7ab92374c 100644 --- a/go.sum +++ b/go.sum @@ -256,6 +256,7 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= From e4046017db584893cdb899f9b6f5ea0abf3df590 Mon Sep 17 00:00:00 2001 From: Ming Date: Fri, 28 Jan 2022 14:13:10 +0800 Subject: [PATCH 011/366] E2E SSR test add retry mechanism and logs Signed-off-by: Ming --- changelogs/unreleased/4591-mqiu | 1 + test/e2e/privilegesmgmt/ssr.go | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/4591-mqiu diff --git a/changelogs/unreleased/4591-mqiu b/changelogs/unreleased/4591-mqiu new file mode 100644 index 0000000000..fb68c93050 --- /dev/null +++ b/changelogs/unreleased/4591-mqiu @@ -0,0 +1 @@ +E2E SSR test add retry mechanism and logs diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index 8d1ce6940e..91af0f8b85 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -73,16 +73,30 @@ func SSRTest() { fmt.Sprintf("Failed to create an ssr object in %s namespace", VeleroCfg.VeleroNamespace)) ssrListResp := new(v1.ServerStatusRequestList) - By(fmt.Sprintf("Check ssr object in %s namespace", VeleroCfg.VeleroNamespace)) - Expect(client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace})).To(Succeed(), - fmt.Sprintf("Failed to list ssr object in %s namespace", VeleroCfg.VeleroNamespace)) - Expect(len(ssrListResp.Items)).To(BeNumerically("==", 1), - fmt.Sprintf("Count of ssr object in %s namespace is not 1", VeleroCfg.VeleroNamespace)) - Expect(ssrListResp.Items[0].Status.ServerVersion).NotTo(BeEmpty(), - fmt.Sprintf("ServerVersion of ssr object in %s namespace should not empty", VeleroCfg.VeleroNamespace)) - Expect(ssrListResp.Items[0].Status.Phase == "Processed").To(BeTrue(), - fmt.Sprintf("Phase of ssr object in %s namespace should be Processed", VeleroCfg.VeleroNamespace)) + err = waitutil.PollImmediate(5*time.Second, time.Minute, + func() (bool, error) { + if err = client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + return false, fmt.Errorf("failed to list ssr object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) + } + if len(ssrListResp.Items) != 1 { + return false, fmt.Errorf("count of ssr object in %s namespace is not 1", VeleroCfg.VeleroNamespace) + } + + if ssrListResp.Items[0].Status.ServerVersion == "" { + fmt.Printf("ServerVersion of ssr object in %s namespace should not empty, current response result %v\n", VeleroCfg.VeleroNamespace, ssrListResp) + return false, nil + } + + if ssrListResp.Items[0].Status.Phase != "Processed" { + return false, fmt.Errorf("phase of ssr object in %s namespace should be Processed but got phase %s", VeleroCfg.VeleroNamespace, ssrListResp.Items[0].Status.Phase) + } + return true, nil + }) + if err == waitutil.ErrWaitTimeout { + fmt.Printf("exceed test case deadline and failed to check ssr object in %s namespace", VeleroCfg.VeleroNamespace) + } + Expect(err).To(Succeed(), fmt.Sprintf("Failed to check ssr object in %s namespace", VeleroCfg.VeleroNamespace)) By(fmt.Sprintf("Check ssr object in %s namespace", testNS)) Expect(client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: testNS})).To(Succeed(), From b28093ede1e6b99a41f58bb923e856d152e7a9fe Mon Sep 17 00:00:00 2001 From: Bridget McErlean Date: Fri, 28 Jan 2022 18:20:32 -0500 Subject: [PATCH 012/366] Remove Bridget McErlean from maintainers Signed-off-by: Bridget McErlean --- MAINTAINERS.md | 2 +- .../contributors/02-bridget-mcerlean.md | 7 ------- .../img/contributors/bridget-mcerlean.png | Bin 220894 -> 0 bytes 3 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 site/content/contributors/02-bridget-mcerlean.md delete mode 100644 site/static/img/contributors/bridget-mcerlean.png diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 0c59711ea1..2de9607ac9 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -6,7 +6,6 @@ | Maintainer | GitHub ID | Affiliation | | --------------- | --------- | ----------- | -| Bridget McErlean | [zubron](https://github.com/zubron) | [VMware](https://www.github.com/vmware/) | | Dave Smith-Uchida | [dsu-igeek](https://github.com/dsu-igeek) | [Kasten](https://github.com/kastenhq/) | | JenTing Hsiao | [jenting](https://github.com/jenting) | [SUSE](https://github.com/SUSE/) | Scott Seago | [sseago](https://github.com/sseago) | [OpenShift](https://github.com/openshift) @@ -23,6 +22,7 @@ * Nolan Brubaker ([nrb](https://github.com/nrb)) * Ashish Amarnath ([ashish-amarnath](https://github.com/ashish-amarnath)) * Carlisia Thompson ([carlisia](https://github.com/carlisia)) +* Bridget McErlean ([zubron](https://github.com/zubron)) ## Velero Contributors & Stakeholders diff --git a/site/content/contributors/02-bridget-mcerlean.md b/site/content/contributors/02-bridget-mcerlean.md deleted file mode 100644 index fda4e4532b..0000000000 --- a/site/content/contributors/02-bridget-mcerlean.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -first_name: Bridget -last_name: McErlean -image: /img/contributors/bridget-mcerlean.png -github_handle: zubron ---- -Engineer diff --git a/site/static/img/contributors/bridget-mcerlean.png b/site/static/img/contributors/bridget-mcerlean.png deleted file mode 100644 index 3dbf1f47efd04afb8a5f96130274c07937a8d578..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220894 zcmV((K;XZLP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3>vvSYWAh5us}y#x%2X*n1pdI!Dy{tigJQpuJh z?6AsGRq>Jc;^uIM1Za2u?|-lRAO2}2o3mV7>7{u7v2HyM{?PpApU>~X+xzePr~mP% z@cZlT=LeDB6Thd=56b!c9tN-HUmvLUeSQA^x~uE+huFRs`u>0mzkibMtUrI>*S;4@ z@$-7HUn_e54ExjLeE*H>=P^Fl{>$I35{#X>UWzWB6q4t^MR!T8AoVo(z3^ZD9Q8`% z&xzOc`rpLP`M(LC|C`wP@qIo09t!8n_YLJQG5Wq9@?Tz0Kevg$ttkAu^KUes6QU%{fReDLYdyj3co7!wOa-oMv zv~RGm!w5H=_xlQqC0aa@mBkoWOz*XpYV2{nq?mq%8!PGAE;Vwp$Xep%__LPq?%Ut} zR%qOL2i_S27gHAh;g|boFa9ULelB#cN;d@GeqzPCq9V>RlsWyEcae~AzcI}(zVDa& zQ{DJKB{q@4{KDLLz~SfT5;KQiwUu6;6YtC3od2{&sQY^XLd3l@iwTJg_!3eMCHNX+ z4S_f|@-tX@Oco~@2sy_c+$A?D6_V4U?#*Xvc<+rleqIJzNJOS-DygA?NS0ZqeyTNU zXygPQ<(x~dx#gZmNySPorPLyf8Z}j`xt3aMtG$kvnzh_YtF5)(MvpxKVY!!HU2nbj zF}UgA(t}SA-Z93EGfkU$mRV<;eU3%>EL(Y%RaaYmjU9K|wEr%?&D!7M6s%9-zxrpS@3g{ZdP~+QULL)AEmo9c$C%JmHz(o?(<>7 z!Ao%rn#mnrxbzC7X+v?!nJ%nnYjJgtx=(LmO$r!lQ1;i71Er`_PXZAX`ZW>nlJgfmMAwL zpoMe8J|k4#$xX*3TNlFgm1ibwB+k}Op`<+1$j9aB%k-^s)0V6UA^^Rw9s91gR&E+u zNgsRtdLiDm%Aja~@!DXH?1wA9oicVN)oW9m+->+lE0C`-wsq!|}l zMivdp3Tpg1p4lL%Odj-1Aua%qi}$OwQ4=k+7FgdTn{BOWgz-)uie_9&JThdA<)};} zoyZ_RTd6+wR!$;1Z5k;n{FTPai*h=BDn})}-v-6r2M=ZZC0FNXD0RVdbI02C(ZPcF zTiVp-Y)3so-_^>^c=ADI4B&vm+OQ`@zH1JnI6fCuME#W6+sXzZuC3MOWIN3OF6`al z*43bSkf-=fSJQYPRW={XlzJ3pXH-DjgPsPXR37WB876!UpX9w}1ejXNjn5u}MaU>t z!9!mcjacT$+Xe1|AO)JN?2yhbd^VRB-}-3CQih#*C#_Gu>rvtC#1oz~!!f%of;x4G zw0Ji^A3#W)ppVZh(I*}Zsk0Xzs0N~%7eBEb%T9oqjWAf;A)q9Vl)4=%@s8z=wALtk z+X%LOnjTy!T%i#ueuoW7UR;txWG)t3m0IB6#*66ePIZh9jtU?HXw}{YlNhFwdkr%ss$tfCNoqP}-8*Bt zNQiNB(o?_;*7gark~L2tuL{y{(6fz&@&YvcW;ONlAFvpln5^NT!a$!?xAAh|9X-yr zsr3ZZ4qz)Lr8n^yYodX*I#7A!vKDm09S9ddw9+Ofwnzc$kL>U>lSCNJ^_Ye1CN6>m7;^GLLLp`3$6jDDzmyKuD}~Bq-04A(Lx<^ zUFuFXsM?@NAv-E2G=ZfRVH-u9b917a5U$Bnz$Ng=SQX@Zg?H1qtLJvnMnF)5!czq} zj|V(Al?ED?yiicxv7o>s6CaAkVovB=af+9i%CMnG#0zDHFG=>}+&C3-u$}eMU2C)w z9kpWvwMH`;|0do7jLgtm22paM=HQoW1I*Y)eT21IrF3he_w_<1e+$752c}Y6I5hDn z#_i&QBZxs)bZb0gPB3`&bWz(}l?!qM>JKWlRba2wm#f1kx|8}k>mV?x1DKc_frCd; zEhiGBD&ek6P&;7F?p*gI2pmU2|Bd4a;aZ*BMj8nXN}1W@NeZ&$WPbuFt{eUecb#!I zy1jT%f5Of^Q_C#FQ(&&47a&547LkEFL#)=c69wcekJ9I)q`C!xDnH4y8}yVH4R>JD z-tUyJJFe5Oqt;pAdb+0psUz@AYRwHVg|UkIvqVpb5z#vu-U4PqPQ?1QL)D&cJd_hH>p zp|cG{P{aG2W)&n$B@nj>a5lOuoNinRO$P@T*HL{xU9`)en+5Tw%Okc19fA77EG@D9(y9-f(-PNy6B*&jXlqm0RhIl=`G&3mUY6HU;pG z!&Om#Ew6S-`5zrZDxp}&7mLv+0@Wa&0OUyI!W0qgU6f3w8UrFEJ9sq1ZAplRY*|P~ zm^jQyM-#P7R@x{l3UwiDZpkk)w@z(95#|=y_C2{$uJm%gFnmDV#)QsT%|P4xYGY3~qAi@mnT2w+y+=K^!-@VWoY^1CLiYAH=p}Rr0 z2Eb)g1Q|%6ytjnWQ&a-SnwazBgBl*nYN$xr9SKzdDS&e1 z$!fsaJ#IV5%5)oSP6LIL7s#Tw0&NM%ITL{b>{R)9=+>S5DWS*>ZcmM&jef&xuwe`cEf{c1Dx;+XfZ;d&NqZ9c4sM_quzKUvl0pB1 zru1M%r{p|j96OZ#ggve*6D6%j_eV})6az&FD~)z1N`D`~2TM3B5Q}|<8R3IqRj62< z#N9<)pvfw?8K7iFMqyRxxJEsv6G6JR!T1G~Tu`*aRu!O}!WqxHs6YJL0ywUx8Ozcv z302XxaBd^uS{1j3If5_{#(`ox1mIZoG&)eYGl}+TJM0Bs3gb!d_Q!F&4eUU}qNb>L z`Nz-+%URHZY}7`In!=a6%J=R@d|X~o4irY`!?0Mcf&jZnYSfi zaYA2QY!RJYjI<27lF=H61R;3|nTi^~& zDUz}SvX0cjGnHw_uNH(0oQag>6{?t5sEvuvUlL_NKncU`)^P(g(c&I}1_?zVO~dC? zQ!tb2eSiZH3yK;u%3rMG^++f+u0`oB^cuhtQGOINLgUi_L$ehS&k*E8K4m=Lq&ekW zfNUK!1*uDy7#etJ6BCWbno67drpBIaa{2105;qtDg^T~lF}FIMDi;ss1nuY>>-wCqH%3S9<_A8;ed|6`xX+JMr~y?_Tb zv(elI+k{DyLfkMvb_tWP)tguHw^0qT6hw+(WGXp}>ymz+rDApjOOgqvPF`9-OGE*n zNECALfg-}{bDt_{j=97d*eQ6WLJJjV6taXbDJ{=(P2v7t0sY)4w%t^QMX8_`H00m1 zSs7=zkTD31RbZa$@|92lz3?A!BSnZ2qYU74o&`CAF2k(y^Sul(B9jOq8YFlmBUO<# z_c+U8pk#p%I;{fP;5mp170{ulf}&3rQynHi4-~0An}we|g@z!#3%I<^HJA+f_9jpb z1G&j7dGQkskAS(*6`p?%ltM>6Vu2G5bY>ZEK2IlNPEjpxn#V;S5EyDeC7z*C&qPS8 zaHKPyvI&$ohz>Xi5H@lXgf3_~*d5AV!Cl&W^N~`xSUl}$&jJB&A9W1)Mbq35V+yQf z+81I*DwQM&PrM@jGt2l5xhI>TWkyO;0McT>udQ6^-}D%CKu|m@-Lbpvxmm&b81p7lJ@2VpGzkaHLf*kZ4HW2$N{aAptkWmUTf|IwFwwLDFG4 zQY)I+B&i|_0Bt&uHc0}9U|5t+bQoso6r;NG@Z;5X1;P5Rk4)nk2!TL=7#2Y{t^V zg-w7wYeZ{79@q!mC{IH{%2V4#eT<0}5rp8H7D5a7;rTHt*v^Z5!48Mgjz7v^t%w%j zD8IRAnhvde2~8p7`|=AqB1Gm@v zzw^N2KbAG{>gXTMZ4UOKBxI_7k` zMGAN~#1fAPGSYt56Cx`}bZI)E?F?-S0anNZ+!07dHVBz!vznGtENBD5fEGjLaZPPK zvKT*)L!#3!yViK{6tv=7%t;F@t^RyoU;S~T2@g( zlT2JC#(*c-t_-|9Yuj?6WNEBa4sI1DM~p(i2ZSPOY7mt@oB{pIIniJeI*=X}GhE-0 z2@HDfqI8Ua;qNe|2p}lnjOU?|Jb81whCtKSA*OC?7Qk4rnt)~E(6I|3D#m<$|ql?ZXL^bZ> zmS{lgxWe7D=v1g zN9)i~=$hhHdH_DUz`sV^1fk)6EHz0yVgkHV$q@m0#H1Qu6C&?gS~BPbJv5@UKY%+i z5?Y&v@jO(kCtdCOplGJQc%fQ-BZ-2o7W|ngxZUP4S#jMiW1Qh5+%&XWnzEuQ%||tz|4i zdy@+MP3~ZJUp#3;=Y zNN69Beous=My=H>2{i^}Om_y$E7b$HObs~{KNmQAfp;Eng-tBW;6^TAf=EA z_Kx1=Z{pdb7{HJ~GsO$C4lJU(v7&b7Y)@#=F4VI04Zw61HF^Mi+OP>1sOAM*uwo74 zTpgP*i<3nL*XWQIHG9+s%3hkx63?k^QP2Qkx{%5TS|OT)J5V3xrcmJ?F4KI6f-6S@M50PGSwaw&0YN<*ouv8kSBg^^H;t+GoUz$Pz*B;{tS?cD|AES)V#n@1oZbrVX;+8fc^USp$-w zVt*)uD|6Yfd5BK9O`8h%CVC)XpJy`sB%jcjWF<#$VJGUQF0?IjRxhvDv!?dxB*KP1 zMLn$hrm(&&LJLxI*F{JkjoK1(O3gzmSv6T9@-bYhn>9U0_3RH>AL!dk4uT9iRK4P zhg6tpXLp8o!a>_VxeBfVC9k~%Xmr0y7|bst!0+ZJUutFE%FN9 z0mgAWI&u;nKCvs9M2x3qe^f2+%k&ICf@O3;h@POyCtaOFK_5<7ueTC841NwP;?vtFq(T7(FyRy&0euLkX*P%>$J#Wd<^N8@aa z<0+|AFkR&fbH2Sh?v^xc!_UIZpr*Fkp!Di09|6e@nxY6BXn$l*kf5i>hVD@y7Z#ML z@1yoUXYc-b?U{^Yvoj#=8-$1#3=%5et0@@(Y|P6$^)JpI)4Ot z`L{Ks0Z-M4lvqq++BQt8$p)pPdW7V0$Xk1qdVKB0z!*dY1LZW{5oUUiufU^0iZ_7v zumg>EZ8xmDAm+TS)W@PgB0oYUnkQUX05$E=nH}X1ex%8QL8)Mf#+}-k*8pOQpW!{E zQL7r>6$B4plYEeQMh_>ujvL$2ZX!=g{|WODk4ROlIJEG4Z^pXJO~okkBVR+K$jLt;+BkqG}Mv?*c#DQv}e}X z1c*W|EXk{;nRTMWEO%sOO>+MjSL=5^$|f}psqUajFRl$P+ z2Pn?vKT~NLGwHK{TgF?bu3Z?{c+xXjcqsDyK?s7unX0Ly^Js@u1kJ2g$yc-PsvpAy zyo66*7v7*g=`|Dv@D-;9m<{P+9jguS;r5TVY=QXE-X2Rs9-RaF8t?^U3d%W2C@!{|i|0cn^d zXiVE)Sh0wOn53>k-#W#HET?k0S&lSbBWMeb0&^U6k2r)quoWRaM}4h)B$U8iV+EcZ zF4iUi*dM^xJQVK(MIng5LR^Rw2FO+-gi+ZQiKyd?2vu!qO&yRWbFU;($t3zBvnXc3 ztK)AAf*5PNU|QTG#^PNZ6MtB+4&~7rJOCnr{s!8r=5L{mkibVu+=d3VQsZb+n)yoB zXiTL8gV+j`45DGmVIpo-?NiJUXprIHJq#7ky@- z1Dsc+O~vT|#K-b@FpfG}hff{si#)E-h59iSH400{Ci?vbxHH+Tw-L52n|J_9*R zb#%%-QQMP=!X$I1X}*`ZM%6A%&gf}t@H9+E4?!3mpT)n?ajG$p9$e=_iF!nU%k|Rj zChI3tB;Z&nY@lX}K(wz!$YXhDsdbc0dwWSIX>eI3Y7E_E2s9DbKzwSb2M0iVI>!X1 zfi_sWtxa?MDByt%lX8ZNMg@C96>zy4b(lhz328k~qCq132Q*QttQLt>ByQg<1i_fG zP}=XUsNU9e{_O2SXp@<+LfG1y1j})v$OGtsH;~5D-kH)NC!#ZFDRz{6e1;mJyxQKN z8>U+*bsksKK;#~EN!4}9yMT7oiA1?8ohj3X+tlVq%jeQ0@K(? z2Ffy>IOy^r0g-#v15m*sGO*2zZ(B3zT& z_~0_?T(m}BCez!R&Qf-HKNbPfdL1ElNvB|NbxB8$#en=2ncb$&Ps-_Joo3Z8@hI_B z>PM%9;HkWHX!jj7!%H`^27XblIu(P~59Of~teh65TJ{^-=@gaB*UWm%qoWE4RARg$a#7f=N3zg^J z59g6F+NRXzi}bXC$kL$Mw6AUy%$`VPND%nMOHu(kUzAnZOXpXLmTiU{ewz_oQ*D3H z{5;*PI1Cot>G{}n;i@5F<0lO*l#U+XJ;_gFX@63)E;NWXVl_$929IXi6u@GIl@vlj zQ;rk^O&e`(lH_nTFmxy)v#7ydo)Axwy9JBmgbjDQ6LsSYZH|70K*EEL2VV%`R;Uc5 zFF-*Z(HU8r02q4hX5%6dg+V*8*bzFMeB((C3EymcJK+ZCKkXnllp8#(XH{AY+|#a$e`gOk9U%nG#FY( z;LHCIY_dC!PRB&VQguETLMY-uyYf>j%qbeI8R$p!A214|ma5mJcj&ZVJdlkE2wcq+ z#g=1>_-vAcH_#T~z%fbMVkQA7d>MAEXqHUBX#0$yLcmgzz!8EFSrZ8G651Wb0Yjz1 zKJvSy*X1xK+@b>&S!}4`#wpwvz@-F&PUTPZB$VBCaBK3}s9`-aSx4Oy{s3E{z5XR> zk5;9?+HTq;1HG#D%&{#n$wu?s=0JMQ4s`lL$InbhibK>19DRL4n~*~X=%0Nc9uUFj zt6p3j8|FczQne56F!y)hV1f{+$RENTz*$_}05m&~J>od{m;^X#|E!tjK1D5i8;Jwh zE4QiIsjT~gD_I9Nl@rghF`C+dJo*uJ_!*0lx~tq!a!$wAZc^JtU<^wZ z@t{QgZ1naPQq&8b*@yAS5{(1QwyQ(6SlW9CKO~JiLhaCSP&FnNM-7kb&|sHzIWZ%% zq=&}DAKB2b7$p(4RsSqk+1e#%(R8&pUNx`pMzHj9 zJz12#j0hB+5=5xGm312Sa#1t@P>Pz;=9@+-ghkEWG{m)c3jRR+S4>E*G$=%aX}23K zhhGaxz_ST)g0Yb(XwxCNS@AMbH#$4;Hj9HckK&GiYiwIvrx17OnjeOY%MNlJnv0I8 zBT+ggt;28%V;FP>4+2eVBS+q(i<9{%c!-$R7Sqs4Jt~_l4IRTjTi=%p1p8=Abpupd z1OoXaDg+x&D+5w_P@SbgWz77^xS0gc|?^+xn`7Y1G0KOxfG3Z7~TE zZLZ^rd5P6)PZKW`kB&cTd`yUNa7l;y0BY7kRV{_0V*-I;4P%gg^UJx~;f5YH-&I|| z)45c1)|3dTqk~T%l+FT;ymb(5%>xOT z&Q=IZkI{D|88uSiOHkL}s%FWC?O4wfB`R*rz`+QMD(2_1&zhLZeFva)9oJ zj!OmbjaM~vqQ)rSLyCwOq)V2x@7=FJNn&qfc>8ovNG)0kJ@-MkIq~_C9?Kew@w2IK zNeWu9ymjC7+;ReDbfQR`ar!9026$^WTanf8E2^ozy##%~g-D<-hqhLcPq4eTS3ImB z%|=DccWUhFFu3DJH$y~W<^#7W0GdbH=M0IQou+!&b{&TS7OvW$03#S?X$jddm>m(J zQ-<>J*Y@y2ftHI-8UdaB3#-z>@{kf^@0>{4+DFpd+v^A2-FL>mY&nnVKIGE zj8@q?-3@@F*C@tmJR5P$h5Mbi1%1#WN#CkLBY`MD6FBou%j5Xq3T{C=W0SXb1G2{H zT!A#BoEF9iuL3t5??BVqZ!3Kik6`usvJf`qqC60^2!EYrX)VoyMAWWRJE4J;L{`Tt z;+PVJE4T=25L=8veRH#^nbcfYkU!|&fs);{0qOzUt1ljA?IKb`2&sMLI`h|$*AHVQ-aQ+n4oOV^^-HMmf)w+1Xa8fsSqXFzMCB#!3lP!&{- z5ELiPpecC3MX)rTY7RPnsWW*oUuQ`o;_5V4liQU~1w&e+AG?HfB|B6vT^ubSEk&mj zRlK7njCV@Z@bL0$+V|)q65_4n`QvSvN-|>iH{_Q$6t4*_idge&(1YTPNgTtfXVPXc zz4rDk38L3NV?t(^H?k&+Aou$Yfq|-kwD$0+dr8i%nL|g8YBR5!R)oqs!Yk@w&3LtW zK6QXnhgzccwUW&hm%jLiuhw_CWR>12`JBD1hS#a=?o4|MI$~rvzDbGEr4jNn1gZ)f z@pM=oT5Tl6puWiR`V8DoOXo#P&`7zvAc3cERzp&BDfk$4Kpbypu)GeA&#ABD-2(hV z2+bxcG+-}k`R;F^$0{zHcj(&#q58Gow4i6HM{2AiRJq*?{nUQ2W?M;$5J&QxJUhrBOIAI|W>e8W3eLELVs|K!An6y{l}cv}tW*(L1J*WM)~>f@D^W`s$T`=T@}yKrNze z^Lrs?9T7i`)gTF3N4}=7xgX7e@S@mOa2&-}txx@5BIx@bI!~y>0Q66K0Rsl4H+1S( zU#G=Iza42UmEKrWIyQ(kXQHpB zD&F2e;)IAG@;m?xfD64k`xD+5Ke&_*=OAEuIrZt8hMyhiyQl-eQd@llc~udz6PxIq zJ}O^h8c1QB@p*7uB|ssgN*nS(&p3Ye;lTlqtvP8PB=R@zwVIxh2!h)l3ewT`5V{8 z?I$Q6k$4#5Hm(mlZ?||D1js#Kz96^&cM*!;FG((!$NL20_vfGViy>*CnejRCKR@TO zpFe+|=fwY>hh}ESNZLb_LvYCD?(ScA_xrt9-}|{&x}Wd;LJqH&AUH(VmAkv{{a$zf z-21)P-M{+4b#H!_7Xo=`Bn4R9VEMM`@li?dOfg zi`JAxSf3Wm-;&j!OJ;tMQR*z~cb9I``d|AQF-TUGg!S~8;CVfxRAr3 zaiSaP@H#f~d=)(JKgR78C*36saKNqZ_>3aIe=t7eJVLaeYk_ z{igz{sz^OG@m$N&_ENB1?!d!b)lH8X7+{kuXPdUGJ=fXMnyr)*KBoMSBsQ4C%dF&6IE|9Te`VN zvINHQmi4GF>y*2?gq(tHr0V$U0w|%o#Hz1Z8Rz##ku%F$w*D|G6F^hfuzh5>J3){8 zPz2({c5@?SbdjLBK&RQfHfY`RA}CkA;w)WQM=7f@>O0*mPkf0q$PJ3;nncw5sB&3F z+`GrBTQYiNRky3S$8R6r>_R3wbMmR?1yi4*aT2P_LVOtz?Ukl8tM-bD9#$TYpa>XA z83YFnCOafqPfR>hibv1m{&b5r3fHHodX6+TuSEGKY2+2_Tr}~9J!#WOkOCeQb%NcDZHkt^mdX1P{BWz!K?Hxyl$vdm`H%8{_2 z6ZLJ+Z186M+$A<1A9YOU9ct@nW{Rq`OClWyCZJQ7ah* zsA`LQ^g)S(M0L#%lF>D-t4ISyr{W!XRRU2tb$O44UbU%$yIfEcZ!%k)GM=6;Bx zZcjC(+_PD{j=C=0Eyn>bke@ws>it#Xy!fuq@w@Ic15@3B(}+QOehvFGMDK&sO;Z*6oy!z`TrI!eHJL z>4kEJ0$9UFG%wkj)#KoV^|!I-@oGQS=-5J%s`(LfQ0)<{p+hu&6|->N3fV}hPB3zY z{6^xc3FjOf9#2&(Rbl%5i#7lkw}EjWy?z_4qs;)&1w%f6 zps4GIz#(K;qI(p62xji_6|`y+`#!4WuW1T9>7chQ3+*raq=ii(ZXeA%r4V&pv?b7ujQmk4m|FjwG{?N zeo@9JnG~kkn=LF7Xl2g^<6k^*AiKd9tNVn*s^0tNvcRqY6Z^gLm31Hm0 zWAY5(Q^QcV`3j(A3SS;`QSO`@LL5jKq{Wcc!xaVbW>b>ylj=jfkZllxQH~nbSVfV6 zwZjv>Qa$9|n@|8umWWXlS}~vYdYJRY>kOVrvC#SgeTbf-YtT`{FLe@EkI0^fYxsP3 z2kfT{IrbK$4N07CG-lFQp~;&*7YqBDw(fc6hBvJ08NCRJ7qmndaB@)2Av?eYa9YUv z*(aXXI^Rh802BsPXoK|$a24P`@bO0I_psR=+3Mg5Qet#RIpbPZz3wpMbC64v z=+_K>vmZNv=T1d5lgljD1ZteydkPOkK9Lmde)`qB)_rg6gPmsP0NstbqVhNaEE?;a zq@&ky)eV$4MXq<0=Y|UE0&Wa7xhp{RNgJ_3&VlIp^!HaJ{Oc}QzaOxXc5*Hv)pJF_Hw?^MQ17mO@C^bM950^*f zd-ka!HBJE`0BWfY;RNw=s#)-Q79rJ)*idGSSsPqeUB1=JVXgIIY6s-G33x5#x((@j zE}wtz#|ET#Sh&WELp4A-48{eb zEx>46$f}hydNNj(jm5D?wknGSGh&b|ifgh3&Z-=ZOvIE$@-)I#E5+F9^>)QLXGk;_ znt6e>4|q*xCrSkefoo(+do~>SfqfthPF2T(5wZ0U&GsOxTxGi>n%V3}Op=cIpH=s9uUa*(Xz~}Wv3+0N*G{4Mg@oJd8nn#7~C3FLgdES!}FXXYH z$+H6hP5^G|_pa>U6S|7BoWt7gqXU6;1HQG3c1XfOPBQ|4OC1?3E^}UXe=z8xdO%Cu zeFzI$YgcLp(BHju-YxX1uzL5^>=MU7rgN~2r5&_Ep;gPe+f;#MJz>s$ul*jfR&@Ij zmDdXMoSR+^w5f`2jI17Ai0W=mJT{T38pAC6K3R8Qa~Y#sa;b)f+BWmwgo^5`a?KdE zAp0ah0d;#ni;}xG5yG3vq3m0tMbdSTQ|1`G9*r*809BATgd3+_U8&wUk#NN=N;zk^ znw9msRn^^a9tsB#@HNt94p*<6v}gaO8N^j^&>rpGQ$IwtPVW=17y?`^Bwwh4b(IW+L7r$88YMviG|DficNDW>!U^i_S-hP9_EevV zCBuMmmYiIl_s=iTQPt*CtJRT%2|&**6vZM}5C8*2sDYw3RLt|Ld%C1&#p7nZD=E6b z@Bs2^4JAQ7ztU^Oa{y2&P*nQywaZs>$Zx$Ok0*NXL!x`=NXc~!Odn!9(vpVl-4F+m45~71LXE0;g zja^UzV6*AV17lEsJ8^bs7@|j0?xIk7(_a_PXRA~WO`*s|zZ!@4KUJZsi$Q{wta=^{ zUIaS}fO(Qz^hW5BOV~@2I3MUSbLc?w^BhSB?h4>oGRZ9#5i#+Aj%1?^_v!c0YOC3Q zy{{)1{k(P<-Blf4a^c`Wl1#@;mb_BQ;fd@AE-c5|94AkdwLTk2U7oFg5d1UU)gJbU zDCCN%XkdZ1xT0La7Q9WEy}C!n>}|};;oU3+F)M*VHuL}*>P z#x+_8`J`%(%L%%8VztXt%o3ycG%W=$BqbqV(HaEIrkz92&B1XqKWA#`<74r>@@WrP z-_|_mvCC_!scP&K2ztWm)^NC1UuQW-D>qsBCnz!pa+tIs;rC2xS(MoGU>ytIq{Vna zMiX?JLP##!xSMiKg<<{`-TOVctkq!uksg$DOQK^rd#S$|OTNfT26##!62O@iN#1{V2C-z(c@RK| zURg7TvjS*((tDOP=7kxn33)-G-UQd(N!2wQ8HipZBAeR@rq+RAf}_=smj-CKoAt7^ zX>xwfVC{U z!cfQryq!Iu#ax2TFJX>DQmf{Tvt0le^>`j@=2-j$ERvE?-^5AV^kKYEEPy<;9}f#TR+HC8N(I$Z<~I zlg9+tG5I;XUb?2FfqJr>wj8HU(1D}^p(z5Xn#!27C95r~ZFyq|x$%(&RAF(FClX1+ zU>o&>SYx0nTEpcg)&xVAYCNJl9SPvc{T5?sP?%PJZPII*`2TgOC!rwjwHn}8o312| zzVUwj?r_!R1YlJ4~4j^wwR|#G))8_01 ziZM`_Nc3fW3Jss983xL7?pY_qjlF`0P0`N7HZHhLABoiv=SBA-3*o0n_gk>^-~WVgMx**19EKU|-kE`QVCRu~GE+EtcGz z68%*t>2H1#8OkcWNlmYLw_vPkm!+Zr^m;yzZCKGJ>plrUs1R!ur%~&6@SA*qpiQ3m z1&@9NodcI0OI8qYVigfcv$O4=J z3Viyk><;qjzD;K+7{&{TZ+&8wtDF!wFly0C55yYGit1iydG<3wtm-64UlYO2SVobB`)@t#O7aYthm8ww-7J2AV$4XPnvL@7_n=fBR)E;Y5p{gn=p)@eNDLK z4fWhB6)adZ${9UaebritXzS2s!fQ=XSFZt~vZ%002D@f?mi-)%yq9HyC!?f=Q$+)e zRw6N9Y({%=Gi*wbp*aNT<+E@IEj7aF4rr}@%yzqEtyE+oUfHVJ+Vvd@G9_OszA}2A zM8j-g-#qHjfc+dFTjICs(*R%#-K*XTS;SE+)ANR?G`eb>QekWOdf>m7gZg*(H45Zn zl^2EORO9Zgw4<^cM7LInD+RQfijb~`vA1%T{MbP~-v>v;krOAC1#*~KN<~>J=@E6c z6beN~$j_w-VlSS9kunoqFuG#ZbSzbWCX>;RnTl5wYzA)-CDo<{siY95nOIz>rP>}q zvkkMM6_I8=G$+|s&0Nu4+uaba@(b7;@HeQ3=an8)|U?k(BP$epmc7q-E$5lXU~mnGAoFEzhu7>HJr z<0e}5)20_xHuJGKP5_0i#c{PrVYxdH0>byDMZ}xoq-r8m9TgjEF*GU=RYNNAN~<@y zz_?u1*1)^?NN61}wmzkqZq4codKI6d+AeQ|M9NFU3m+U_y;jUiN7#^Su}vomS57`H zNrS#Atm!+aHUL|P_og`bFG%_qK8@d)w)DGwIiV0}=vK+~EOwGt9}A`MPVU@1y!bLQ z7d6sHIDudv9UA!nw-HnkxPV1tl&o%fwuGw|wCXlalCCjay0$_q%`6r~79{i|K}Rmx zIVL}T?h~p<%5ZPvP?XQh=@%!9 z^pYtydZneq_?OD1=Z0^w!Q%%SYOH5$zv3fvc(%E!=T#%{m;=_+Rl%cR1Vi!gz)F$s zrn5E!)%WMKG)wT;;n}IPJ4iH|g7NqM($IZ>Jkxrwz9MCO>dW?m^3~d@# ziPxM)-?Pt~6tXI7jsmqys`8@&Pf;gXH(}Z4$4)LwTcB-J9s$MwfsbkcvvChvv1?s) zTSu8OjTdUu5d_qsYTCYk(!)qZfDJ4jmi%>&4AG$NG?tDnFon8vvv1?xAz>^E*!(Az z0R@VT=q5?t>7xb$;ek3V|g&LfvS=Nv)8J&;6u%-m$HW5n*-Hn>Id zKULm&&H_8M{wuk}V+K3VBgvmD9M@wK%|Vxf7@v+;9-~PhRx(88hP9q{Tv_17)I}ed zfmHZfR%gXk)!C7-vzuEkT(&nkii06{ccFWuE0;?CTA&tU-Q81j`4Xp(8fQX0tuT|W z%YqhR<*AUR)w>%irq08?`kTLQxLKP>$JD;8+sbS?X7m!}Rq;7gjcjJU@!es^#&hd< zZI9-Mx1p0%TMTW7xI%GCe#bSLRS&;-R*XK240_&(d7~s)PxK?yC;ATy+ z>-@RKxJ`VN2Dk1Ro!BU(ZnPgO?zt9$*yydTtx0|xL~Ce z7&X<%74;iCe}h&iULUJJx^FP+KXha<+`Kq4m>;$4PI_?s*2gER#Z5uR9ico)Tm@Z; zArZdE{klKT^EjXbmyYb51BXt@eG5xqOO0NuDQ@=H0vrBQL8p}w%{j5&Go0YBKMHfZ zuyU{)wIIwF?&gu2wKG|P)Gx~tPOFEtk4o0yo?PlxdT<3_u2ZSv$gj<=GacOWk&nHX z(Z2AM6C$bnxbaHPm}E_{Y97Vzfy&q-sJ7X&ajh)(ku*kLPn*Gw>W2VDK)SzU$ixBq zxd*UDu`Dih(52|z7S{}dMX+Cr{*p*;1Xu$Wcg0M_&PyWSzD51)LBmyL_gsOuWn-I2 zyIJ-9Q>O&zMWXYPKNn_S#R+)U0{_|Yl;Wf2!_K^a%; zq+cT9g%d)<#65uK;(?Uh7^a}d?YdvT?oa%WxqgmZ zc#dfL=rNcv7&|EU=hv~>u(%el_@Q5npkHuiQ{9VA9Q8NAB1C$NfI~kpf16Om@x>;3uD4vocFU zn>~sD((e?HW9^k*W*Ic&fl;{9fuN&DF4?0KyU_DU0zQxo{=K=S-i&U(07WZLJ=VH) znS@Nan5VUNNp9Faj?E@|9gLRdhsks=Ic2s8&LyI!SkSLG*{*e=O)4suywN<|OrzQ4 zL)>hHla29W@o7nH0#|$VI`p_k@Nqs)_r7N1t|z7jC%wSFG1p=#zwukj8(@izeL{6f znC;##FLFtFrL5ONEz90C&hEwbS~5=vOCnh^s)h-Hrlc;WN%H9DC*=x2FTFlsvBi!J zQxfcKHjmA$QqxVS&YT{sq9h~cJ5=u1bB0>e-|uIe5UNh)NJqHD@CoEtR?MZ_9DJq z)bb5(YsWjXHG=%Qf6sZ+>&4x#KR=J$pXUjgi{X%@9|yB$4a0eugIkoYO-ydv%eqF2 zJE1G{fdS7`v+juQ)8p~rd+&FK-{mT@(B*`Jl=@J;4}t+mS=JDaL`zHSr1pn?DMAS) z@V(YEEk^F;P!1dfjedJ7MD_5!5_9~fwki3mKC33ZDbX9m#wGyp$#Zg!-(|QWEL?U2 zC)47G6Iabq>J}vFp<;*&hfOk>b(V==;IRZ4^R7(eN>y{pnCZ1`8PAoPJ_tt=#}d+YIxujR|? zXiaY!RYESsL@Uo}8^PfikuUgBJ(jnbbm#=Noy3z{pf;67?&y-s$>C4U&ku^Yzxaa^ zQz}R`%_M*67?Zs@6Z_DFlG~*E3Gpx9t2`3&y8xswe7Rzz)s*Dd@0DHmAD!SpIevcX zO{ZO>POQfQQEa={B&&=KoOZXqO>b%*DdNYm^LA-JCG9D5VF^D^FY|Q2%N?QSgD{WV z!o&RZeN!CUu)ED_mV|_9X{q=ZYW*md?_g=_txP^vFGkYBWPl;|)vsuj&PNE%ozcA> zv{EPDm#garMt9cD+kUo*qg@AD{?{=}1^42zwNh&vyw!P;Uy}GuFvC%lild_Ew zHarzrI=e&%)>tbbPP2?o%1|zXILIwKm?GQHArt1rW93G~l3$%7ne>A9Wvj5R=hznR zrAO7B+v2`_yjbE)g!sD~|J|#*a7QGkj$aZOJoKLXHOH`A|AAHB#WdVhhC0xE)ll=) zg^fv?#;$pQi&s{jBX07*na zR1ssomixmHpJ|$E4())c`HD`!S0y1R^Ryjviso#zmCdHsa&FkBMe$mb9!!Q~_$?67 z7D3?TaxRt6fod4Qhg%<0-la+3SblO`OCh$;l$OcLBq(%u;FZ)eoW4CYwzF}%bk9Lq zOT<=@48dwW#Z#RUx-pk*#@X1e+VF=MGkjZwqTR}c^mU(+V+`#o-64_O=w*M&{Wz4J zOo-siXxJK9oK@psL&YtPchLr1*ok7{hq$KvqJV2gg36d_AH$rIyk=x8+JYtK)IR=} zqhWfdCH($f-jC%QTrQSOEF3Oh_Cm=$R#}*Hd>EWzc^Q}%%Ool~*SYNqXiE=2Er>_xiGqvLVUEb$$d3v6gP(z>|@TPbU$w!xBI!x5Mb!RFcw|5)B z7o9(E9l}Z2If4fh@K*VHnthXY-XYJxh57w1&;PtDE7>6GMra^pHDKptqa}yVJM{vm z?i966%ACSME3J;}Oy*UQlN}R1!tOmfbY~=28hZzY=UsL)D%NOtm2$O7?A{s6RM7>i z0ow(8T;3TT9k(=9$F(T6{AJ|Z9xiW>2W|&TO$=VBshxIa&yshd4Tqa=+X!1r_`KJL zm{T%cN1K}u&Scrb*%nw>F$h?;WGkRZY_Vth=eJU6=mq(5YchiK>2Ja zePilgu_M0p2dks;q)PO<#^Je?9NNTH!}mAjxqZuyb{gZ-FMhp_ExSs=4;X4yE5XATK!PG^?KPo2|gGBI0I8lD@Y8=eQ*i>0KWFzxEl~GI^hWH`kkjo3>u;sw4lDWLnHa0i8+@QI6p%WvSufa-X z*Ixf>|8qr^&4RzT34cHp?-T?LIVd#Bp)J<)}!pfDZT-U@f4gg$yx2193InP(3 zf9|9iwmn*CK6_m)%Y|$8hB#+Qd~0KetFSRa|$ zh-7{TEo-x`8m8S$#z-?N-jg>U)Eu%~eP2m{g=Z@+x|CXnk8*+zI~bzek^=ellO><+G zonUH_LuoTO{$!g}m;#pp^F*{-kMJX8F6Tsh3&2K-Fk{S+V^`W%;Wrh6Sd@sxr%m_U z+<%m{fxH74J$wE{q0-@Ax7Cym$JhrgvOzhYB%M0xp5<;FwP|oG*7w{}ovJWT#~Rzb z$C7C9$6%@@zFrz9PgAsf0Zdpeqd~<<4KI2@s{d88M5zmMS#nZW!q>x1>Mn+crvSVz z^LGKY=aigx`%e|`WJ`J1Xjp{Li4nrF)2quB(xVLB`v^~VvKKm+7ngo};^X4TRj&~H z%?XrF47EVXxaJaMst?D3H@do$J>J&W+aQSaHc(?>2aCN4b?*UM_|(6M6bHCWCcsI= zPri+d+15BqJ%+*-UjxQ(Yu$XB+vytA7{P^y)I5F5s&3K*Ct0E0gY#G1L~I>S7Z@^~o?_G}NWEvGn@xg1dz9ck&= z>DI$3Tps}U-j|bAG@h2@!c=>RUPXD6al7xPl@VnIgV*}p`({=RPvJ$Yp|Lbtp?0?x z@8lx&9N}_##zBWMTosjohq6~)?(E`HC}?#d>3x}BRiYt}z`X`jrAcn%;%60sH3rqo za0kloNg|teE__sZC5L=eeH{yUqk{WKz4(nQIlNJj7kBwx*ja0^om9Jqx~a`n z=W#0f)rMlW5z$Z(I)4Mso;}J%T6sfHyP8g5>+q^o0Jk0c>!Z`oh-{iP>~y?79fLBwwiDw9{gV{KAUZ`4*yAt&ge&9nIOf!a!eN*l9sO4W)}_7|6= z@8T+$?%P$g9{jwK@MpJ^hx;vZsNs z%WD9jfM@Pd~a#3{;l70fbukC?@M`3(G zL6NQ;!4old+wu@n9eq}2G@Z_}T-X-{HTmsUd!Ba~TOaLBk$!bP=<$Qwu%CMXYVRby zhzf6MPE0ROt(3uWZCKFw_5}gJhqDjmj_t;h^0hZ-9(dR>3hfyXHyJBD3L3H$e+TSg z!a)b+<_eTVF2tIsV2KQ5_1nIiY-YP@Z4kM%czrqs%yY5_<-#h|`7BfT%?db5q&!`# zlw^iB%kN8mvVACeM|Yj-ys9~y<5P^qji)`O;n~Z8_udOccvv zl7z};PYxWH~k0d>{)g16yn)i^DD+b>ry!0KhjoKi#kYF?`Z1%GDxo z@2%46_`4_7kg$|CzqJZv_M2NWu?dIMDXdBsWjeSHC8VCov2^ckz6tZ*Vf&1ho$On@ zfRJ0_C0nlLOFie_%;u+ER5+e$)oFHxWz$|75B+jP!1kb2&2O4Sr$q7PBHWW@a53J`Qqdgf z6ugd79zT*S_D-ayV_p5KL-g@n8xm%W&dsE-S)o=RTWVwxu za_2j$%`$i56khbsI;uou8^8DE=c%%W#O(P@^dwp_Rajgs&lM;)o}rVv_nHseQvBKE zrx5V1!1)E6Q!OU#B|yfAuIe!XC|HgS9A+ik8@(d6dgD1R<>OtRAlb?q<6D}o@xZe* zCtnca3WAu|!D&!8sQt>n8*<3XE8?UkfR@ip*?JVWQ@kAe1&)Qz;y7g5&Nz|MI*BK( zfzyFEQ*=`l;#$NWR}j;$T7z8MTSp+Up75#*ye!wbK;R|CaAm5rJon->q-CC_W>o@0 z4sX6etbpFb6O{8OFy5Rc>pjS~Pq*KM?n&2bNL$F`NRT@Jk*S;OXIB`;Zs2W6ecTBS7lALOe=p6rBY2M_RmmkjWfV1%#*t+{Ts&hY| z=yMViK`feZ@BkO^92V{_5~t%17Os}#1^pc`fvo}rLz>fnVC?K>P7)1v5*RmAV2S!) z>BUKeip8iXz9)F=1lU*N6H;g#9J_Q!G$i{Wap@d5lND-j<56;Ho6F5AA#7UDU^^dU zmaZtuMc!vNLG&mcR(>e8Bu!Geqh2im;R&C3*OYCwm`7VsIsIi(_2X``6{gt;wjwOo zHz6?LY6LJIn$t{HP?$e@1we}UYFW5I3}fC~LsL+zHlw_%RW8~XFv=UcU&};iazJxq z?#t&Vd)_{e94Q%1*@Lnf&n!e4JAgyz!Z%X=v7I;VPW~vX=woK&PXNMB* z_mY2qEyyHGx;2X$ki4NJI45R~Nb>4zxU32}Gqh_SUcNl9W07XY=~17;wvaYh_2!HY z9KijC4~KP5%$(!eY+WTZkk`nD3m}eN=&}GLRT1Ck=+Is1D;} z!Ik_1t9fwiAEI%zis&&j7tR44_k_|=U*LhHpOwANPoj+U`zGty}+G(vhYeZ5izh$*nE6b_T9Qz$p_WmZta$Ro@nWL4sOhANwuM>$WfNpf0?* z2jCJXwl8q=P;JQyUb*LIFD{qkOrDTdO(QKJi}$Pm@2M zoTkEm(tXww0Q$yjNJ6tpGTJ%6`PApnl;1l2E@1GlxTByLQ$-CdJb#Tt)x1n&-5T^nO);85qSFoal2EH zILDHTu9HUHxdsgemDk1bYRdiIOZQS%c{_6}u{|}Dr)t&>Rz^Yy)+a^Z`G#?6pOJ7< zY_OA#X*6fc{)Z{s`#Qb6BR4M7qXv^Rq~aje$l4Czi%X&BcgozHa=WoCXP@1sotN=oHCoFHrg;F>B<|FQW=2zO z&z;Qf1?NEq^SP*RpUi;hntL$rI0kXaS*-S64Mk_9Ravk^4}Yt0a5{>zY$3)LQaxY`^P#P8s$YBy+mN;`Q6Z&xzCv`0^oADgW)8^>lWOna(H5U(#5Acn0 z=-&kI%GClX8SQA+=|E|a)!fu&Y9;AzAbo+}8-U9#2dzBb*SbO5wt*4k?r!33PnKGP zg-1BaaM<6L_^dsAN0g24-m{)iT^Sm|F zy`^nGvB3_wmJk~2;`NpS3t0Ao)1eh_ZSrgtsB`LMDz=LeVLXlyIqVJTz>r{cYY^n=EbW_OS{8Zw8zclnUT>>wOZku zc=uG5*3!y#BLq7p-@#A~ZAkQ4GYS8kyl?*D^|B_3WgJ_S-Kx8ZfD8jT>AioOka=wu z`{obk#3;YXA=BD27HJJlcOz>$DK|)83&{d&47hF3UIfw}T7;DkAFt#wzFAt_QJk}& z?Sj0?z1-af+AO8qK>6mZu9)@y%PNb8YJJM}1^p*z<|c~!K)$hhS9bjDtmJ`LwKx^+ zPBg>8*b!stN-N&bO3imXlRi7cIi9L14&`L!)iisNOY$3Y@uuabpxFQd06r-s^W{{4 zvQN?VDmqghG--7yZ;54Mt^q5zDBHIi({sUNsn<((^dRWBJD?);Bw*`uD7V#_L@c_> zydITCIdkKn0ba_UxRE_lW2>+xuIy{Ctqcn(k3(9D+;iUVsE@i0<_uq6Dw8uA{wKDa z)WpU6X~;hw3CaEP^SLalBL3dqz_cBYt-3yMJ$+~#omHG#h!!Hg{HtvFIxO?8 z3R_>ek;pc@w~@@gC;yP5+AG0v0qIsc;BB0{@;Ld5z0s@P+J9w>6zA@r?KfQNj*3>AIoPtHwWDH)1$v2-8+7o zK@;VRB%b-6@mUwc6P9fV%Lwaa#j4b%ZYERD{Q~`}s_7x_*yrU}xFQQji$w2Z@X1B8 z&r0JfFMhmBBgHt<8J?E>bTMLOhwYq=EV1^=nCCAqKXb4O6X0y&&3XQ%io`S?=n}Y_k<=4PG6y+O zK2W>jWt}SNeJNwSqLB*NQbre>Pdaj@sZ{Z3guXXtUOrW^APm-MPI9#(M4!-|4qV}V zn)AKZj)8)hrjRxEl#-W)Y{ckuqqkP?vNo)nMP81Y(6`#I zpdnFJ?vN-8>^Q0XsA~N zW2KI%ltx;^*(SjH&RY^Yy}3Ff+kTA2081yU<_BZLcw9zYG$avcD9+7n9mQT-=r5~; z_sSM{t*5wHPPR-viYt}Mtu{;{VA*1^SW!OUG31{1EKFfc_>p;K`6A%+fO%FXdAM%$ zf^UhOYdHrwo1|4-^X`gVixA!DLKPTqN6OUKCt35BT)%rScy63#L!PC$JF%!^-Z4hK zzaJ}t(sSn?-t!WU-F4WfpE`nvV_f>ufn?*@(VEnh&N@S4(4=s^^d`$KT<^qQE#goC znxjN?{_|Z@k)<6>Ptd1aMnpH+G5ZxTEBv-`NNr6p;<^iKFv)Cpfs!7>J#}RMQ^4z3 zKFINPFVSWn2pScPAalVKm?@VWg+=u-#-*UF6Q$C@=9XC9h`YIu?S0&Iy+)(xXmQTc*zc9uO5QJwmDPV^XiFCYec;Bfeley7M~c!En&z*yOF* zU}o0eT8Cri%I-WxR+lrRhs-SR;HTHl*sLw{ITms@sgaFAHX6lztmtSjy<~+CWTvhl zolRBt;$`xbvmSaGSWT&SBNJ%3b-Ss6Ge3TFmDdHjHm8@8ncn&Q_Za-+DI1w@~mhON`swnGuj**8<$@)km39e7kc z&AssrBtsU`-IEg%Icm+FddwHC`0WTPykFuw3AJBfb_=@SuMf+2^=%c^oyN)lEV4f1 zHY9(=8m?-WGQdU(Y`Yi5*=T8&M1p!Uu)Hp2r4&UJ2#$ULU40^s2IF;;H;742E5A`& z>Y5G$XYI9@My1tykpo2@@Gi)UGc~z-!k=1m$U8|heCudupt8cLY;~47O zQZ%SB^QzbF{*ZHQ+TtuZ0)-Wni}D>8H>#;|HK;((j8ks1Lc3{d($ka!tq9^<>;$z` zvnteGRLQPLbwkR%TcRjU`4e^(qp(`C=1gdfoYj4GH|DiooUW+>Ee@--nD$##Sjir0 zWm=2%#-+LTOtbhYCR?pF80nxqQJx(#(hgTBz~BcXW1k5x95iYw8-1-+oIp92@ZY8h z8m;>bMT`Ay+e~!CSi<*y9e)Q!cs6KBQcdQU!NjYUB3GA`xK31=)kn4LvCCPecd(ub z%QYDf55u2J#`CnHCM5ir_zwg;lKg&hRmVQE$P9D(6>6(@fhH^r;aaQ`rUS8>S@4Nq z{-mt5!_K ztx6>0ZLRiRzig#_LpRc3*V%G=R-=_2xhg=rH#%qU=gR@5e1>%ii_~8>iL}X~L9dFf zqi#1fo>t{7`xGzgO=K?@TQr-pZ82A`AYe4ThvA8ZKy|l;u)TfYm%w&LYrV5LY`S7r z%40z1BF$)`^mT~#oRR)(rx;H@2y^16tr+Ug0O;S(W$})7NPLa{Y|574QB zO(!sy`FMcWc~m)| z3w!G6`a=4;*iZc1Z;(9?Ep!SwKfghtWng6DUXhIz<*~psO~?Z|VK=ip-fL(q47Hr^ z+9m^*$$*PV^9r91eJ$qDYLFbeAxjy{U|oVz)z@a4Rjf4dQT>~%!w|*@t9pcFGdRj0oG-o0#CEYl zy7W$_oRCulfnj@V4X7Z#vg=vC(-pa*PS*`spp}zt>+|({cpfwSoX@@P9uKGOxj{vE zA3A6YuiAJF_`V?6qCNH%DhL1i}NiDmL9Lqo7ui%4~b5duwVdet^PHb@| z=y9Aa$MKyj9Ru%kRz*{0cq!+O?z#BZ)x%HoL$kF$@UK3)J}f(X||>3X$ zpe80|GwzEgS^Z@CPse8C`~5!r-%Cvt7FR4w%(EaK3zf8u27dONdX0vif%MLsT@9gG z$egZ#M~4SDs+DyksWyCq@WGdU%U&iS3sZ;!%F4pl4lh1GBbV@bV>ju?He$en>g}2b zZ8xTL$Fd1lKh!C8vSv{rE2h1Y;#b89M0w(>5Fe^BcT)L|#MLhD5pfJU+JJ6)+PF(i zwp{v_HO%%EE|OHkEb+x>aWv{~Ree^KQ1vpqSu}E1kW}T=Enluk`Fo`sp@R_87raF; zVe%3FLGcT`x!}=@`8pFOGEV~os_B}!N-J<`m7(M=%+^lBXUEvid8>A!DiW=yNugoA z6Ah=Zf*_+UsS233ocWtPmzI`xHalNFJHR$As&2mcx&Qzm07*naR2-kzc=5x$_YgO} z0P;DhbqE>tSGt}_NwVHDo8^j`Mo{10N1w-}YvCY5?JT!#+HDO3zoOM%Y#((++So!3 z1HSYd*n68MD=kML?F{0Yi@)D*q^%O4TM11xl6GPxY?8xyAmE(pT8bF=Z?`G6)LtuL zA*pVeGmwf>(~FDEbanV3ofrMEg+oJP*Jq=ztNsPL$` zUeg{oNYfksbLRrlaX%7!@kj-QLOvSi2e+PQWcbjzH}yXWpit z@q~u_rl@f3o>z?XwZ*S|DP?3sDXzG@cA7B3=QQ|!yC*c6TMe5d(uO>mU8wh$qk7ip}FZE`sU!MQf53fzX9zV z06Y4F=N_;|W$(QqOxf;h=#?v>0h z6rVAFDa$CBMjYT9+lfZmZLGS-`2>qq*O=xr7Qot>fB{Ej0=s_2xo68{^+Q)I^or*z zz>~Ioe*mp?y@JK6#4NuD%$I_%aH&ot%#q5k)eyAbew1fvksOXadfQ zok2>sEdNj%*%E)0g?h1eMnYJ*C;F9ZJGlXM#<-UrFh_N#c=$x2{C=yT(;1s+{B3F6 zW*(Zm2r6drpP3q^5}i^y^}{(?N(1HKW$!Vnn?(*@eBw!}ZjW-ju+Z(`y8Hq4+P8$6 zblKnJX)C$DaJPTzWrQNQBz3nRm8XWGLo08tUX-=VW?bT49QaP@##TA;fdz{~`Io6-8krs%}Z6d zZMnm=?&We`lZEFuS1R!i2N#DRcr&*ps{>U9|0?gT~#oHY=)3 z4P8xy0_4u@xTCyG^Xx$l8D;WB6+##3+~^j8TSd?cTq=9BPy@D>p?__99!G8Q*W{pf zIZ_q;&?Z7v>fuQ1a*4KmU9$RCX;LJk6lyJ>)TSNt8PSL9_CsJFp;y zSwzuDu-y6EBw#ot^0cioWWivX;@K;_Lh`u6um{rKolEZv`ZMqUV-vG7HhyRfm6M=V zgwHV(z$?tbufF*5Yr**;Nj|faB0+q_=O-Sg6UTRXU@sMKWpp--wX8AUa-HSk+|=}vv3^H$=7~@kQ+_z7nSSDls_9y^dL~Q zzxmLH)Z=B0Y~N7OSPkt>}Rwvp}C1rst zfU<`sc5KDCu6DEA6JJ9Yn*8hLS96DMha*5y%30BPW3WnqUSEO|YgEFA-ur zwh@TaM7AnUMO4DCu_F(u<$2_mX-$dAq;SCf`}HH)Idp#EX7-#X8rIMK#*ummGagH~ zfn$8ku4r}b&2TUD)j9ZM&(#ei$mREb+{fkY;SXtXrzMHFwRoJMzwAm^5|dW$Czq=- z`+%Z;O9{txnZt3P?49ypdnvEH1V{lA$#+eMM?H^q@dNmM!i20>MP}U2sJ|JlJ2Ltm zeBVyqR?L%!&utEGX_wpRB)R4{HblB(!9JUz@AHT z`Y*JhF#_c-V3{LyX2{CZv<~LvwC2*%N-@sr>C+oCvJ&}6NK3Gv9gU|C3o@h2k*Ni0 z3%jt}_1uBD4~0F#{uWkLp4gH7BD}(sShVBe)zt76baX_pLLylzrSVG@W|>hu8@he- zj?llk5u?S-cuNhGHL^jhRD12k2Kkuj%FA|ff=c{;T*r9q(P+)#?%(hA&$XZP7@l)# z8Rrsjnp@Mwzi*f(=bAvCa%#W)R+1yZV|)&;bX@>G-!DFXjN&=x$LtIkRnm981Phz_ z-1fbi8@rk{z0F16g?$n#7O4|4ese62eOWeFl;gePKa~q@6~gp4AnGSO!l&n7*m;iL zYx};Rr9FDI5r?mwDhAWvX4ysje4m^U9sD5({*y^nIoe-RpTE7N$nDE+lgcn$*b)3% zJV6FrWrev>vYHG$2VOT$Y8797QIZ)nW_NWwmxdC1g(AGJS4r(4@*z%$glbgiE#EDs zco8akx1u4zu$kxk=*zo*`^mrEY&E;&BvOU<+dPk|fHjULBosLlf+5&VML@Rny3rv7 zP|k*V&>HhbI&Nvfzcz;^(BQ z%iZbML9^tj-|y#pBhXCbsbziOy9S)}sWSs{{od@UR4V+3Of4;xGS&6aN^aPhKH^Su81cb?{S`o+x9InN^>;lc3)IGM6OkmUkuWCho|(I)qmvPJOy)9Ac;{9zHuZ5K=jGdKmpwWrmn&XD@Id_CC`opNxQgl=es+O%xsO3zPSyW@|Hd`rnXPRCcR=D<)&avv~ z+ZWF{arcf9H*u^yQsN~|IL!_u#uyFv(J|z`T8(kiMMtfb6~fdOWH-^MN)0aJ=foOi zU9cFZJh@4oDVxmSX5@K60M&n~meEambQBM<>qY}cj&lFeu;Y6{IZ-F0O4jJ-}RZ}*fP6cMhC}+##r6OP_vP2tXO@>SO!sx5^ajx#FR+A!~;S&)L zlHVjzMf>d#|8c+fOT+zq%;Ly?jCQPLu|Hb61GB(8WM)6-ufP7_>({^j_51mJ@0I-i z{P}bI`QDHF*8sn~IL?1sluOWfHfFOEJ)5`V+*(4t&A`_qQkSW-#C9eZH#0kX9`7g^ z%atqd{pw?;e^Q!7@RM@cpWH+KWX;0;;+!<~Uh7%u->X9}m~o%{G0|55UZJOWYK$-c zOtq&tMcwK;0r)TMQzDGiN7KKvUIsEfH!t?#!Yy5sbzSi5?nl9Cn zU|M?1MQ;7oM7(MG(n)A*24gy+PL6D{`{=z4^6%$6Mo3&10wRA1X98J82($1MDBsU_ zOSsVw{u}B_gB9k2f^^a%A$o!qVwZOnY=;0^@7rXoy&B94$x7>FE*wdnJI{1#Nauq| z-7)AICo;B6)h$~MnhHBYXFF_$SetbYXfbA9DP(wF{-_&^l&SeWS#H|5uVkf}*qS$h zH89|-tmtGm9k$6ZvwYUfV)C6<1IKUo)mYAT9flj)>e3fMxwPkrMT4R zobTuB0n%v8g#2j51DYDN_@w7$m&%F?IzcNeDUH>SV&4-b-?0%@{ z&pC$2V0QleJOV$TkAFT8zu@y2?8lBBlFtL^>tDJ5`D4%d^SK^eh~n@2*Zp~($MWp= z8>km0`TgGezvp@WoWJ<rf^mEQm0zH&3b1r?9P98t~okuOz{%tv+TACqbMU7 zEwNAT^1FDr-B_2AlGE=-|=Z&II^xo zWA-EHe?R{H=lkL3;qhZ;=Ntp=`u(5(xv>8|=ZS;;9GLNQ@!!vp?8@XWeb4zh0C^ma z^Emu|zyJIFKlc6n`ST;V@d=iMX)N$^=~TnTXvi}AH(89D?e-7}TB6rc_coyz8*Xwm zV8Drhap8VHpzJd&v2Nsk2$2u)B~AoT(#@<`rA%L76n@bDsoK?Q$DHN(Xm-kMw(xxr zzz;CAeYS;ecG$e_w}|d(pYXx1(KtEj4zixFWczP+4&-EzfiPb_tCCu zsXeyG?;_5mAJyzj^n0}zJ|EhjWHT4j9jlioEkNOjSI3enHHq%lTBW2cb!M9?$R+MJ zN%(#~kcUf0b*XNoaPK!WeIK*Hbqyl@$Ih|yoX2(V_kKQqo7sQ;^#}I26T1KB?++a3 zp-V=X>yZEVA3Oj31Mq&o_xt(h`vL4Zk3IPO%&~1bx1e z{QJGvWk=6BJO;sj{z&@gpTGb9pMQS-`s*({DzmwkutmRR2B|h4n1iXVj9zYP%d|>+ zoPRMlwTY05Cuqc&ISINuKKG-4F8D_T{(fCLz8MufNLz%Sk(4eZ8{;oW*5y4mw@+Gm z28#-r49^y#7g#S4{L6U5XzQF~%*nl-dt-Z&+;3V3>J&U-40a)*iR{SZvv_6JZmeO` zo{bn1-_O-}qGNe)UyVnF#`{T~R?!55ryew4a)n?B$ntUN=4gzcnutCv%m7&BGnvn& z`B)XC878(Hchm8>^7Al3c5iX+nrstB$v`Yq37GpDwio(znp(yaYTuHI->Q7Q$yD-o z4J`RDun{yfj0pXWKp zlH&gPzy9m|_n!yyKR-v%F?0XAyMMo=8PECa*khmXC*Hzy!f4f`?-Bm_{Bxy0fABwl z>)3OQAkSl@JuU~YUjolD34Zn0Uw{4Y|Nigy|NQO1fBp4eMN~#0!gK~Yq^mX-+UDE>1Su?YO&Am1c98xHwvDNH(W-tq=k5 zLZ@|b?RfRJt}mVy!(rK$QAKSEzHwii=jS|jPNhM`d(5=i49iq|i9_o4WgPaf2IH-GblPh-R(N0_`(9DL$gi{c3C zeZj=_!k|OB$HulTXtWKY7=wYRSX&M?1z!hl^}#{)W7Qdzs#FEBA*qxWae&Bb+qRw0 zlZb$k%!-Jrco&>wc4Wo0hyVn3qEaMjDYq>+?`k|xNf)6yRA&zIRZ3xTDmj_4NSHh`_m58r$a!x7rDR4V=MB_{phYy7Qy$xP zW9MovOFeuI#etv2Li2mikkcVzrcc&wDiinz4s*WZGFpm)s3la)}8WJ+I4;wKP zwS_J>Q{?a$ttoePgE7O~+R7KzmQ;Iy*8;Fd^8gU2js@qe0DFAfPng9iKA+uh{g}y( zr+YmL#^-~BpE-m0tdb9U&t@-}wCT|t-%l^R8t56VIo139G=Z1cr1jHZ^%I+A&fh1x zw@aTCt%;#K#Gt`-VabAlS9XpK{b;qJ*aZ75f~+Af5EVkZ z2${JW2my$Q_wCq%H>YSSibXRZ5;C;h?988$!u)P9VwawvZ)YmJtmDP<< zVzoESB33fui`{lTwOp;j011q%dXai);EeS&nz3Lr^>28Fv7^8yb`Bedclg&0fy^TS z7Bn*?^31_|c8=H)5fN5EDlM%1^`;n+JJ7<5bhU3*@8RM?o6Ssh3)Ua#`-N^w8b|d2 zu#7pc+Wengxk!j^y5bhfWN(hzTDhs4>fPNZC^Uv%*hSu5x88Xp3ae703()G;i+Yej zucU3oNZ2}_p6s)ksW5z3J+}O(3b`FdG?C-AN35qCRHlx?lA(iuMmkmjuhng9#1v*f z-}`r(=hAP2x4cSnW$5~ut@__&xdupP_!o+|CM8H@VR#p_CC@=sT z5twNaAaqP(MnJ)N190BEU^(S0Ucq+fYmNpqv!WCr#$Hi(v1p6+(f-h?8a`b1wfKsF z=)}yWx1ZtB(~^NWw;Sz3(yZBJmS8kAnf*v&e(|ETsQ5LIXpJnx<$9VB-jQ3qWS2rK%adlhQj3}_7}vgx6nllu0-Huzf)Gw2y1)L|=w89&aS$~PG#h9#G6 z!c%%5*c_iWsRuU0Fj{MQ4&b?IM2Ki0h^m^iY|f{Y zYzeg*8;RCtQ&Yew!GwXDi6|m@=MW&}61=}kR7Fsu)^MU~RupD5EjdLpG3E1msua&+ zrdLc%#Dv-Gd0tGeTErO9I|3#!jnPsnTL`t_%q5G$dHsn%-Uki zWGaomi^R?;`?_9024*%>$F7KinPcXI$zh3^3EZ}|=Mrcs3Xq9CwFDS@_3<>(_&{;$ zI3jTDR9<#0-HkCs6YaVlC|znR9vdkdQoWYLZrrxJUHS;2Q!sI?AE`^LUXf5uu8FV7 zp>r@cSTFUeqHdQ%sh)#UD=(qupl0^YvGdHtOvJ66rAGIn4hZcen?4j=BG|ZwMK##N z(1uK{Nb8fU$Y4SYskW110Es|$zk?#SFoS3k(XS|k=PIr zVMAs1+mHsY9Q*cdM&sFXAlOh74BtJ@h2V2Eu;GCxS*dUvr zlGSob1A@~K=}^5SO};SD5naB}?1ytLr~{}}mN?5dkzp$B~ zqwwO!#TkFo?zEz!INHb>L5*Z;EgHh6-58B`0OsL)0%}%(&h~HX4EblnPyI`?7&`TP4W1;VrZ|8fMkJaG#fE6 zQ+zNi)&$s1d=36ED#FQHo;L2O?!*^8=pyhP4wx~59lS{(9DbU%ert#FV2Z8jcAXT8 ziUa04waW`71MP*@jsMKT#Z^Pm!bPVRz77Eysu>GfD%Xf58W`y=(P*!vNn(TntOy#B z14Lj%F;xQv-9sS6l#;3v5~${qi4e)FiYln)lrAPjVDFr2NlA*7D}r;321Q~~sj+v2 zqzc5rGpXsBBN&2ZeMZ8kDtJetpawCO54?_jMxeoF&8MJ!p_&mQVpH+W}s>@CFhigkeHDfu&AmO07DnrN~I<_%t`7J@=48{*-2W9 z5@9r8BZ8E>9-B_h(BGJ(uo4QUy}rE<#X*Vf?ojM5euQ7w!1))^KdkHdB(7fh`~mzlKTQVV1sTit2(8>GPniHKO4 z0oqtP3dUId+0B|^V~S}jOTA;e2@t7h#h%!{2P6E5TPij5eq>ufK-$S#6S?S=9U^NIfMe_L<~TI zY$ziUSXElU;=E)|F5jZHfvcL=TwJxZoUv z;t~l@0KhwK|Cr)IL^6qy`{uA}j3i5o363LluE@RZmSrFE!q9;Apy!z{a3@zAKn17}YQd zE!-5OeRCs~_2tphPI2w~h+6fC>45sZv|rG80sOY1VIYY!&YL&srFK>C9JTi{dlv(uY~AYy1Z zmQ*?$s$Y0fnAPS*pl$)$Vig*48VDHCsAirbst6Oe0&XsqDpGp50Zc`ZU<+P~NGU2c zuLcP4*!Gl@C-xgBmCxsc8Jydmk7p^8B@wU>drVQxyo1d#6W0XbDsQOz6-FvjoJZHg zE{H-Ajj>!;tl!eUZJYO3PS=%EDTD%AN~UxrAKZO=7u8&ncMQN$uJg*_x_%rX*9*bA{oHgIQ$(be+6u6t?`~sSnSTOe!Z=fpHQ zH*GORlhuGT1E2v7mRC3_s@6XgT!M2ZO$?SK4eE%tL4;WFxNB8aLl!#S83Bi)a;a5$ za2Q%LULA1i5(3<+*Pa$a14q&od7bkBgAiNe_6!h60b8xxwhb%J6JEIf?IY{q9$k;z z9Oi@dHS(^fFOBPL6xNk05)yhx{V9iNRis`o1`(AULkOy-0!ZXs*tShe0Wimo(L@WQ zeSZE>H9{&gdB}+d;Gf?;y z_6tKN(HI)k)WIX?W$xRk$4Nn}VkYkkVM#WtG7FkOs`a#L)Z@zI8+uS8Bqqn~n2Cwm zRpkm*+6FQA=pw=uI@(j`bjXUD4Xo;87PN0yZrFT4X#GJ`5m8msqH3n1s-R*^4oELu z*XXT!vhaxc8eV7x0PYB4G}fNU)L>p~h6#*hS9=8?w>cWw8u5j*rl6&=?$KW`P=Wo? zK}gjSOFz@Jur4)emUZ|-;HXM9Y{g)mmf}>F8zR;H^OA^-Q@v%@>$PsdSQa3C23lRQ z-Jv!Xf4%J|Td2FQIde95ytXU=8%?1$&9sCyR92kz@E-tB6MRY50Z|QMV|H(EZ#kYR z#!|8w5c|3U*Q}Q?feGNgc?I3Q&ze0W5us&5Gs(|WBxh5LMa~#Khg70j4dSv?gZ-y|0?U}DBBDHoMuhJ+4T=8zdVYRg zSp^LW;v>eQwg*oRtMys5Xv(=1CUVY)^JF6D7__*sBe{5LDaD20oNo^_0w!0As2VXh zHWC668F6h^wU|L+?{H2v7Bj^NYksl3Pn_?tJPi2>sP++W9nTN6_PiC%Op6v2wA>SG za#s_y<)A}EDChtIgsb+j9a*@LP!^tQS2Zk^%KB);3NbdWREFLljuQ%zh>^G*Y@Js+ zZm|sy*#npVLq@xTJ~NB089CT1(BTtreZVOi5cH1_*_(AP<@;|U0h1gq<#xEZXg-LiXz zfUNarIL!EHH9~JBNLRclOUYpQ^Us7}OzfBq(Yb(TDoL$) z?}8&kbfg049YR4hWHg6~&{UF1b`H)frBnz>wc#bN6ip-*Au|N^fymKc?niKBK=;Fm z>UCwuL~Lkqp2K&LH_wjYN~Q`$RZ>wUA~F*K`~G(S%lkLce8#AvVs=F>sxfK3 zdzqL3Vk`gzA6PS^gQ_-o0M3V<)*7NLIKA6oTxEcht@^rMB9{RhjY0RUdUry7m+Q%;H zT&#z~pws1?!*6CQ?4xetT4aT`XsS_sHVsH6&r=P6!bSWxindo$Xsho;OBSh)d?I$v z6Vb74XG~}qf`6P(LKag{5R(>p#)QaVQitS(bieO)wxB8=jr7_X6-`vojEG83w~b>i zDXMd%;6)^*B2t*yc_K&m!*9$08sqin#{;Au*(#z{TKBkO&V`BI4)^W81s@Rk^Au(} zj(sEk^XmaX1Xz-GyJmSlz5rnhN9|uXFV1X759o3(3P6M#QxS=|42$7=w9ZUW$=;|5nGs6|@pJ@O^6-%m6IY0)8i*l*?SBePC$1C+HVj8eZTPMXY|C;&O z(1X`jJt{2>toH8))TeQz5KNXpbuZ(B4_jJJknsp!f@r!vzLKH3GQRz^5->tpBHQ{@ z*M6Q{H-btJadl%YHo9;_B#_!04^-cjQP_xvpDp6dKCBc7?4%{?p#cjPavXl$C|(s7 zI5_qVo{30iNHn`QgNY+p)^SzL?SK@t{Q>PuYKnIP>Z+BBhqvC}w&_#lZB@^~sH!TGODrfzt}Bax8L3*% z8Id;cy(dQ~1)pE>k9aAlW6Gi_XO+Uu2uP+m?BV;{T@=nF|MzeCN~f7IlV>=#$8Fn) z@z}_R@a@~d4A&WRDQ50U`FUNAJrQ47QYoPJcEBE3)QTDcc?QQ$RLykvp&*}E%;hX< zDMyjAZ9AoS+~1Ixnb|o+CKFX5W&mTyqN-BE%7_t>NlYBFin7tj+{6X|z}KDi3SXWA zQ=25v`b^SxnISB7R-fzlj!DD-ia`Pd!X3bn!VE1Ya;tEu4o`{JK#`YuJp3nj`oOQY ziCCjdz%tY7=0U<2Q(*0(r@+x*Qr~}`%wd-w;ar=}u1b1V{h}#)wFv^O?zd^_!xg&J z?ko4Am$lK#)C3Lf>v)@G=a@%ZqcknWsf~zhczrkZP@jn=U59SN)7Ooj3;MIq@#|lM z!(ylXK%yS6)1~(90O~|Cn}+eRpKWmDW?dRvkR~g!?js$cTxQMiJtpM!1v2(vBP4A8 z@1c9INfd1eStG{Bprnge=FWB@`~n&v6PVNh!25PnlTuVc$WTm7qz;uGQ8ffOc7~{? z&Ic7WQzSICBH2uf7?^I}BUnkNca&rC&b%k*cOU%M*XMPmC0QU&TLJ%_q=tkfn8RZUETqEL%nZSTwgkd3Tr^xMKy`&6P;d|Jyfv{i9D zt;JxBJ#1{yb_e5>+W4!stVq~{_-eAOK;_Iec48BLfYsQaI%YKjRLLTRh*d~cE9^+s z$P68sR%bDZDiMj4+kQ_u<(y2b>^Av%<|NaTQ zF>egEZQs%SF3VzeMEX(w%v+K zx2W)%mNM9jHviwSe>(!yRw~+{`e0As9-6CVBxN`!d(9-Yv_WQTJ6%0fBO;oH zp`d9rU@RV{qH0R{X_#0>jn*K92FM*rfXkAuK|{6A?FiJssAKG?ZGUP=hA#Nrmf=|6 zGFjiGz%Y@wbi67@G$Wlz*gvp2I;v*0tx|{s&~HRlq6R@ z^5%oVC03!vsMc)BMHHDhFeFqfW>8f3Z38oAuPQ3VN-jAQ!0p&di7}>_&Obj2n3?JQ zwu$Poh4=gYiWxbO+Y#V6c2)cR_aED~AN%%v%H#P=Ddudd1{7RyjtH4Hgy2y@kGm0= ziaN)C`}N&3$C6aQF#yWvBdUO^I#0JfnDN({)XY>xQDZ4t6c9{{7&FG4pI2n!5H|09 z@IIxKQ+~hQVv0YW7s2_<+kHbQ?10dDj|3w6d|o-}9%3qKyWRKgK=P)HXxL)v`lif* z)m;fVBh_L^z|0_OBFH?W3;G&vL?6I{cIj{-)5#xSbM^WY6Gtvfy^}}FV^YKb3&w_; z>bZh_3`2kg0MI!=Y0z;-d<~+4CGn^S!}q4>HJWFMP3w9L{5fnHyTaK9cf1EXjCjP+ zEcYXo`pT*+(t7UDf^8m7lqCS9x&3B~wgf3G`Q8SGRpMtfBfwi0`7m7TOWfpOo{UXD zj!(Ask2Ji#Q(e{PP2Fv5!p@0C83v*_JKSEAGSb9c(U6|$SZ^Sp&KwOuMpYibccTTV)joQv!n^vixld5* zrw1mgniGnOBSvCCyKrT8t=WRdHlR*r6svq!eSI!%+>j5iN-U6d|SS`TRsk zV1_lH%9O}G&X|*aeqIIme4gwApn1PZF#|hpHvoN}*W>Xll8@uK-?>O}&IRus;pUJ? zKOawK&PmQ(etbMs4NcW_51yI4V`i>F_WKr0q?GKqe82f;ERQRz06Q+CFWiZ^!1zvDX^OMNUkgvBdbXT~|7e<9>g; zy?aLpq=*WtH761Q2}Mmo$&i^ugpka!scQW-HE!lKICptFE`C)o_@`hVo1AhSqB&f1 zI~ygXUb#}FJ~c7dkpr~6)FyfmkWnKk3MwJfl6p3ChU?~l7WF&AY-U~^HjC*0rT!W8 z-j+Q9t37Y2trj7EpV-W{Y+__=O~dX&SsQA$5x)&X4AN;@HtFQuF!w?+U)R4YB3vE-oKXXrZe+hH+FR}@%7C@)`19ZR({Rcp(tL&iR) zbA2{I{W(knL|sR<2!If;gct~sy{DGplw(TA7Mvp!6*WRAr6!k9kql-@UX^O?VWKU7pzy$|fLq-eQFd^*qf zx7%&sJUav_nn4Pq7BM8Zhwa$h_KmZs{M%o%$e%wRDVG$J0mNdYCSqn*BpIlvfvI;K zoC{kphs^GbF^M{$m?9zWn-AVO$0<=N>Eo2!wiDs*9RL>9@4=^BqS*7gqH6SFeu4zTVmhiiX;Fpx6IqsO4 zDjQgn*OdAB{B8GUi(jhOkeWb&D!c(A%z#Es={#oZl~>@9Pz^y1$*7H9$0lk`e#d2y zT*qewu(GaTMU1ZU<{7?#|GDqP)@09evU8J&a^z);>=#e5uu}i$(}rm<)^& z+Ysd#07JD)4QX4?RfV-D$3B8EGjxP8MIsMiVuiuK-QED;@%Uh5MBbhIk`us+R9D@| zpf$+_%%0cjn27WBe3nu^9v|2F zc;AEndVZ8t*l&OP<$oil^Z88I<5{xzynFVZOIC$~CV%_eU()r|>d`S}&~FFtzwD*h zBbA)&kI#=>J@)`yI=QvyuHhQ-0CbwRnS}= zJcE&%A#sJJRBopssfaB;^^pmKVF{pyHJCduQvJHO8784!3sHLnco9w6ZYCXMhQCBTiJ~Nm;$(NN{XR=pY;LD6^HOn5i=Nqnt6!IJaaJ`s z``|%DYSG(WLkaI>eb@VTP`PYwUOu0D1GZrxL-m!O#bfuJ4Me&c%Czu?; zTYK)owK&H?v2m=8A6Ah#;#K#(c zHg~XOFE_AmrxZ-5>l}C4n2Ya@7&c?J12h`Qg!G05v z46FL<(cAmfCTFZ|eP5TBCHQ2byUG^-3GAFKeSpt(g0s*S3iG z8an}7`)xmG4KMm>Km`#XCS>+bO^Db`%q*9}-h&x}=92a;5NOPD#VZ$O_8q55DvC(g zb;cMKkEyq;!~Q+i5?5w_q}%f1B_C?bzD-fwTQ6d(@hT?k)~3lNx` z4}J?bNBHsM_dlN!Q`&t{;9}&r0IH()%py`s$|5I|1y4B@@9CHK+ve&0ZGT+(I^+As z?(UiJ8T0)(V$nZ8pMQK@pymK~$L6UND{2xm5#9Ds%%11v$o=_s8R)m$UN3D?$x;wt z^X$Ff_l=mIF%y9blv0Su8Kx}WlZcr^X3Rwt)gt^biz(gi_v7|Pj*%Fo`UU_AwV0@y zo6Q8*O|L2tA-u|luv&Vxih}VqccdjSRc6`@LGMeZ8VS>}M$mDiW}u~rH63Jit}HDZ zt{b*CU1_MpB`|cMqT@m>Tr4#|NfVX{&yo~BnXC4nkO*31gE*n0!_ovZ{bDKH5Wyh` z7z1T&eDJnUSI{LBYTg?Y;b%3(Y>oLg1T?ljQtSqCgqOgfsgtiEj0WRC;XW=xTM{G< z7Cm13-Wn$hSBLZD6>uFsO-)1F3(RSL6Cu$oJ_k;%%0xT)y($WLBv!PeO;^!G-Sf@f zc&OQ1bF9|k#a$0G;W1hls$Hrz!g_I`B+puP`QGb@O-lv^5MvMES*1{~F*WDvTg_RB zoQT{vvfzstn0Gs=sfhuB70IH{^E2h_9N)tJ?d_XN_JKY>uTm6=@Aq5EQA_;zI*H)p z>mnoqCx$6Kw&T4Ee2Lh3@4mgi6WHJX@sH21XVHAyHdFiU`4ACgLL}#$nlclh16D>< z)yE}{`4QzzU*6$J=-!P2{`t5*&m7pcEd)d)FJdWX0{ea( zU-8PtocAA(=oz=YJ#R(yA=$;e@7uoZV32Y`L?kvq_D)qw5kQn!5HW;6!lIHas%n+X z`mp=`AR+)#RWz)IByP2IP3tryl9~g;q|<6M?DwxwxOLs#_CYT>a5~+*)z_tlC_z|NCecX`aI` z_VAZ;cM-czWksdo&>{{SFa{b6g=O?bXbTRSI*-0NGyyBG6$lxd|F0TDs}h;8W_)7{ zA0k@^(k&Y77oLGb)`gtz-$6`!#Xdr`RUH!~yl*LA+#@ArKJDIXtyUMUfw zNqL@85u6YEalF5OKaQiuN~(aAf`B5W-ha+Hs$Y*m7Ac}BYcBdcQ?+0s`cfd~qN2NJ zW(s={g+D$XKORplP*>H+Ttqzc5ke_q07a~b5K`575>pnvu9S*+$AnnKfKWwDE%;Cr zOtnUt#hd}aarN_q_rBt>M6#JV=LyLakXm*TjA2xD@zVIl2&>F)DO<)sXqh3cos+%n z=K7Rj#xKCk?E!2?59{Hi%S3yS941$Sx-4P*zHz@U%+f{$>+~V6zn>bOirUpp<4VPU z7R@)~z4qqTdML_ZtIolBcX|w3im7*e|rE!Ue<^Kg#-zvbW4sdl< zQ8Uzxn%;~0WM>*XiN>a{>y=tOS;f}SP}a2$ZM<|clLfI~>(&-L-DF%Z6&wtiU8|l5 zM+|JUdcXYcsz+r5wNWHYy$RVMA(LU>nlkonM*k>a#6%4G>j=F-s_Ca)!9IzE<5APN z5jFdu70|P2IN+f(IWs3wkBbw#AAsd;wH&P>5nO3pd{uKL&S?>~NgNHG%= z)f6u+Le9V4-i&PXUbQ62&#%wVuZJNx;^PSCb)8qfA3Fn>Sr#QJDJAcHO2xS#QV=MY zavTRC{PD*hSI#O$rD8zv{`OXi03kBR>uSOy6$U7(+2$}v;x`pPzl3C z>=-_uQO$Peg7?5!0Du3yiWFx4wuiSp#3G;1_!YAmI3j==mZ)#{%>b@viaD1O_u!o~ zc5rMVmy&WZlPv^djY)FJL^+#&zr8=>S-HK$M8u^OcARp8=f`o}&4Acx$=6qLVQ2QH zRv(tC1=}cqdd*9!N~Tp#yac_pjr5?$t&PpX2u1~?DdAbS$V=XE&nmQHrXr9Lt1o&Y z16K)F0dzB-Yp5Fg5ElCVvKm@YTLCa$VC5_Uz8Yjl8Uv09l%Bk|HVc*b*?k(cMhrLM zq%CC4QKK2C)tvTmJdFqtYv==;ZDo@^^1uJbf2Z-mHKSvnu;ZkeEvIhtF;fey#pyPz zr#(>{pS3cj_SY?6W<+yzT+}R1SN+$3VvH|_>r-y!`YgZhXd!5E1BPpH(_f}e`P-_P ztHmW%50K)L4L8Jc)F~M&!McShwYVM|?(SB%(@6SW-cWn;(!%s=i9?9&ylXI4HB~`! zIh~SI&MD_ygWbQ~?|*)LRNO&IQi^Q*{+I8+5Q8H~IsWnIkI%2?m2&$Am~&1{m}0q7 zDMbKTMPtsQY=A}am-layOO&)92bg|-ekQf&`6wwBwN%V|A9Jdj@asH(e187+=a0|N z#~+`MACEH^6}4RS%AnvC=*s1J#UfS;s)Ct1=b4Czv#1!JDaM$wexJl=mLFeFLh#JT z<`KDwQ*S142h7yxFKoXh67EifU7Kv9h;vGZmYm~+vTQ@?(w z4Ss5hMgAN7u6jdQ-7T*ppXtc$GwS8u>9g;SGO3Z~ zQ>tx9qw%q(c$~y48YDTSj$G~{N$5VpY2)j72pG=i1qSv{<<&Tx43W;Ai=4^Tc3p>L(omU+M9SORg>PxM9(ivb2OAKvS{C9%2pbxh>YqP8Zu{}ak4H+E58*hD;JlQ4J{|>(2>0XIHeYhS zVpP$Tl5;F(&N(J@fk2@0+K=P@dOHTX1`LPf|X=&Mb9Or=Ez2m}GY>gi_?nq5zvCLS&>Q<;U0O zFYj-`lPU_BnWY@Tz!UHLo^n*tOCmEn$2CTP5rYfZ=jmrWRb|`v8aFFaJIZRVY5ivF zphl(9N6t3;`PIbwqC}kRt^ONmMkh9bIQ3Mn>KstWxsA8M2t64h=8S+4hRPa|o9_e= z0GVc3!U(782sCP&0E)LBbq~-`7vGR-bGZMgN2imzMw;^7HS?hR$Csb35Ly{sWixeJ%hbN z&&+1Ry5Y~RqPjv}=@~eb`|U>&4HS@jCSf-PjROT;OBCvlN6?kAzQtZit z^rBB6`LXRn8#VA$;lrr1>UC1(uE+}*9DxvZAc^V+$?Pg?TqNJNTfDNC3@U)O1unTL zfSDi?s9jm^#{uvNVLR^kWAibl%1p_*0H|mIP%_&+2^j(v$$&u4QO#szCnYg&sKCU# zbHwaJ`0?@axGqA{QbFz*wyj7Zq__8XCP#!0!7*b|P<`{>k(NM3G?{=(Kyn8&npKOO z76p7fDW;T*xe%^1e*OIqAKY!*YlJNjU8!V=jC33U6h9vqbGjc}aPIs2EoOO~r>L1J zi8^+KRz!C1yyukDRdjRi*aK&dIsf+K@%?Rg45m<@+Kwp}$*MJVp_HOU%}nIEYp8i* z@87-?LA_kbamt0+h`4{jG*FCEwVmyH(8;$rMu-LR=z+X}KLsg2~Cwlrn{?S8=CjLWn z4rx^66ON;!)}@|AVFrvX)@H1J;I(BKhu6|7S`W{mi6^UnyBZOD&)0P!ph+ReTI4#j zNMiyPwUkPcoRg?pi+f0!iO%>`5i!}EcZ?$C`S{8yUAc5eKAb7Oz1?Ff+XeyON$MBA`en#LWjW5Y<3zs#|c6E2dmR z2)BK^@7uA3ugCLBxtM`T%~~yLW*LD*qSj+vRE*~;@33;AchAOvd zCC`xF^y`?~Jv>nVvjA&W2vM=zjAXsbp@Eef>vw3LS*%_|e7)GLTXufVg&$(qpVAWs zN5t$!*2XpcdY}-MegF2$``cSg z*YoS^AHV!PzLt>(1+eP&K+c!sC zQu*=u5lhY`r^F?c2Tw zL;n58=f~$)ydog&+kW4JBVmNF1pp91F^xGpX77B;S+$6Y_pXS>l%D6w#9Q#eGZCpu z%EF8QpsI-IoU4sn&IQf9W6xYfQz^T508m7#x^wgHOi92PvD%b_bL_eHn=0bG_s*M` z)evl&Ic+sa!n94!Dz>ygskEq6I5JKg=*SajRRs0MiRNRw+SHcD1*|_!k^Agr@#C7I zk}dNv9CFT=(1Sjq>wC(@eFN*HYZ(F)k*yHXg~K%vpnYQX@=u*$NyQLb&!fZUFd8v| z26acM(zk?lo+Tn$im0lVmMTAd$naBo+JvwU>E(1lbyu+I;$?hs@QwT`>JxN@naTGu zlN>3jy;JI;3I>X!cA@V4ZW{TQo_T8in<2ATwJp|h@sAHEv>}rAh7Btsn!3$Gty5|G zz=4M48L;-TW}ppuWD3S@;caSd)u&C_-V}t^y*Gn&-~2nc$Wm-X9wMTtnuyd6+__M) zio)aT`CtCa-^%rcCPWxh0^-ewc%|p#d0yA;er)?uuLTvj9pOAbJp2Fo|NgH>jJM^m)rctj6mPte<^ka4#yFGe8}x~bHu?r_6`xQ3j_?=dkkAJ zYSsq|&M{HS={@W|(0Rp@ixl;avxu4IB&hPg&fm<+9>Rb5*MC(hKR!Nx`+xo(ygx#? zza7Dmr9>VBxyNNG>y`D4kGI>+Of45Fc?-@dr&Rv<{Je60+m6jSK8`QCau!$T_eIq* zAu=(TT&bj#wtYLcaOM1Qru*(a_#z5OhPZEg$uWs-n=O3|N%qqyt| zdS|JN*7Kzq9H@%6E!6>S=x5#}Bn)Zf*c0~X(Wa4b?bUXjZf|`QW`+#>I4;^= z8zVoPIbXv=j7wV2cml1dqisAz)x6%k@wRvjU(~ym|FXuh4cNYcG>WWASEshZj%GSw zz23$ok9wd{`}BBbZndzZ`oS@*@Qb+uzHd4GTZ_Wq7&&e8L6r4&;qfB)?_1G?RA z_uJiv%~6eW$NkueFvb+)RdP-xJ0fDIq{vi-N~I_=Iyz!WA7}aaAOF{}g)MA<`}NmS z;(4X7e_Zd!4bidZZ0XqC?LY&(B3|do&N=TjgNk{FytzLg<%&sE59b{7?p!pyQu6GM zEnF$88IU?gLsX#Wm2aEBh5d0}pO5P=@3)9pq$)*$*%`?5O1EQUaxteO7c-xl5RmBO zug*EQ6Ng+f1DFZ9K+HH(Rr|C+o3l$0h?$)OQxyeOWL~nLaRR!c5omdJD%`1KbHkJ(Z-4i@>8 z=C>)Xjgg1FT9#X4t+irEVT2=3s~)Wpu#NOLQ;O$ZUWHhW+E8l5K#a`i9xm3WcK8Yj zvt}f0T5d!D;NhcN;ASIHay(y}0@o&r#}-6dgGSZZouOMuSYKOw_kq^?1=g6$Jh6_w;$=_ z>+A76pNd2-7}!q!k@9(6SBikPA2$Y2Q}3Nabo++v2+?hOis$?N?Qehkw`1RaVgEeO z+rFQVuNdRDZ3h!Gi2*S&dGfe1Wfd~`<^B7y{aQPkA~}`vcs_sp`Sr(-FGDhb7&D-~ zZ63p(wGbfyl>(}KKi&)xO-e3e;(aI}&+|l+EikBiT$f#N?7QpF{D1xS{`=#RML~1$ z<{bwI&fh;D4|(eS7D_IvEw6^yiD}AZBVa-?`uKXjza6F^st9zYxcMzZIj?lzkKngT zuuz~2BYxrSIEZL-fykAdgWpD0E&{3uVaJQEllhqE1`b$x<3}8e{Y$)WH{5z>iNavd9fw}Lvn>%FAa{7)6e+EGR|CL%R zMYuv#n>oZ_Hjg;9|D&SB&Mu#BBz$S!`ii|Bj`(x2gw>kUv2xbugG(e=2R^;1DySXd z%R#d2ZpT2qTXbGt8R7`8S|wstP}()JR3rYXWON$e`=3fvsW}W1&8k2&?B8{8gg`oJ zJTE!FLxe|ld|*6cF)K4v(V*2@SJmr$eEj&Mlyb%siAvEc zX7>KCZ@;`9J2_&6T$1F%q*g8wVMm_ne)oTS|E~Y?R+QBA$LIO+d1bL5A3wgnP7+Hg z$DHe1WA>hj*{N!l0y#7Baco~-XSpN<_m^+qKF{mx`DAj=)uuh(!xp88SxgBbm>Chg zAA8F3IIsJ$sbDUd$TS-fh*HW*h@M#uVob-rT}c7eYTg8MRz2hScE1~NPMHW)>Fa#D zKYu4eGjm}lc10!drXs`vK$%fRfvC?601+6WMl-fYpJPy!BJ5nlNsUeT>T6l}-EALc zh(t{>FxT(R_o8)RTYp4~AVMBmM?l0fM=%6X5Z5yi5!ohOYNRgqxV)wITkENHdNpmx zx};>Ud9IzJHB*9zZd1GAdRX>4!eTW8EK@6NiBxDKptBx`u7}}_&=y=Q!LZ75Vfnz@ z=Ujc{J%sgDS%qscH~9=;+EYiZ!%LSrPk-jLDHqN^0|%tB9$;FXi?|eYxLDQ4AJw57 zgE=_lQ{5Wb9fmz1pmspOEzq@oUs}fj-FuB{+VyL7LENr~v5u{WpPI2k(E^5^xZA1C zQ~`{U1k}_3%{v#E9V1h97unb2aXWSb+;7KK%5iLxGc%@~u9(j2+P1CaN7(k9W6TKw zQqCzxG07>GBIM9Bd#2!+fwnzte$yiRu@f+a-G{x-F2BA$J|189;|@q>b{zM={rY_i zUZuqJ|MB%E{gouymEWDr%-!DrAR;odvWjH0yEpn4ERy>C8a6F0q@P4etRZ8-@CJ9c z;a)Awd_ZKiG8dVNd_X(^?&fzm=YM8MWI*FQ6VO!V;2jg*@3EB91qWD!QG`tM(bG?V zcp!F)KfhdV^OUalv1l~Iahg(6$8L@}Wn^UrRa0j6a-MR$jZ?Z$L-77^m{K$~@lG{Q zDLN4~m|`qt?7Kh&gi-?_Q_S88ic7g5T2kvBrIY~cTcWv`FFJHSBcQL!EL=>CTGd&LR(Xvl zqm{0*U*x|lukgKebSKG#Y93li3cDfwIapDdw2jhg@>|=cZe=_yR_^M|Z`rMjA!%_oz-nJW&?=Xw z%BxZ<#I&mRwdP=$k&!{prlqKJPD>Vs;G}5DIVV+d?&UHXPMj_;@%RhI4;7 z+{Zg;A#$hTIM1_q_vypa>2xgf^!D;RjZ@O1#YzD~b>3M{H-xvhiz$e3--WIZ-b>%P zX-Y-s*VkLh$vYknp$pyRYG1zHUoY1#Fq)MTQ?&kc7Q$R0XS#zYuiUA$+OHUF$FAN*fY(FEc1W^K2erkx;RdUw2B&V=i#6y66$rQ@NflRQwUL3< zIF`299v;urc)wn6*V{d7A|N94!XN+!%p9}DJa(=V zalicXhvV_^KmV`)6C*gm>uvn>)2Gw%m{a`r>tEh37a&H&*UR$GIZX|QYlH*F*Ca<&bR61Zt+%!cyuLzT{ml?Z!4I-A^ah+)rDIl!m{vb$+_EkA?=tio!+} zRR!>1`)&Q|XI`D$s(>sTh+#!YY|Jd!A(8)kCTQ5vh{3j;x+Ruo0XpqQVD)~iuI06J zG9$Egg|{ZwHY{m*0KP>7G)2eo0S{Pnr{$9=oGz z84g1(**g{yM7vzZ%l+=1>%Akyp$m-Sgol0rFvolvhA-c~0Kk9!FaM}&Y37`AxNW?@mMa;dEfvg&vtP z#B{w!|I_L977s(X-P84Ui!uHB^@SMwzNcc&IU}6r8BubG*oR1g5!IjyX~? zQ&sRx0IXoDnye6|m)BSvS#pcJ^G=A;#bjiqAkv?EhtX>IgZsPXUvDH(H zDYV%=4YC#|yf9r60M%+sX;~H3f_L6~MKUYixtud0W&i?ue!b2yd+))t>$>M}FYFkJ zUtTW%@cZA*X?h)t4}Ay$wJ_uNZ!ga;Z^hs+442C|#XA^iDZ%m6X_#X&wJ>ytL-5jx zI4uRi-dDO;xD!AG8!(lk|T>vPhXq;!&GG5Dg0VbM; zt{eKk>;B>Ar~ly}f04RAW9}@3)+CHch!8 z(%WtJT&sz69J-k3>&x5io*o{KLBuo<$H&X%5>pz-aTtb}a!e74FoI_(CC@1ZV(&Ns z+S9wms0NId>V#1&GS4v7o3xrRKZHoaf+`mZ-fkR zLqzc&Q~?0-Kn}lw7)@!8#V|2D+{fW87A#ZsBm}T};r88B?K4^|?m~Ll#>3m3xH`|^ zJ^f+J>#cE^h9xUlF-?`8ncmw<>cK|!Nlr)wO9jj9`a#>6B0Krm&KvuFQMfGsY*)UJ zEji9w@ucj~x&vDbXH9^vf@wU!K3;ZsUhfKMh@& zruqBx%YB?L_t^lvcLXLv#I+v+Go0qh%(zAJ6d^|d#Ps3e!#v-od47C&DtUhX{w(5B z$y(s$_0@@YghSu;eTS;I+wFF}fBpXadb!@`Y{=0NNamuKaeR4u`|G!t=a)AT4&MLt zbo~22{Pdsx?(^Z$y&~@kI0nkz| zLkR76s%7Z95PU9q=)4vyrl3FsA$U_8V-g}3sd`{yo?~PYCoC+)Xkey!==+pYQO%}i z#waOe0ALa(QPWa~=%&JM=zCQaqKbYZ7G_4~91}AWtvhw91&V4x0Ack{q?T>d%rPU zEi=oAOsyG}%+yQ`-Urs>N<6lzzpY+@qG~G@D{HC#0c{0kQ{PveSWEUJLgWhZ;y?cJ zkBu5_Yl0WPpRnWptvC08tufgAE|db-Oqtf+ExC=`v9fpz2zHRWUHT5}hV|7wwXFcw z_s|SXn^a?AqHPKa*gIrepvGc%AOy0-+_Q5n)DIy70Ws1_!=}1Vr1p1hCsiY6CP;HU zJwAod=XrX2eMvE9wSMRyPe%nU#d6N3W$1_d?V1#bfJs=yJJ*My4?Po~4#$s=A4S}p zl5>tgOU|X35LX51JkMS5u5)$VGpC|Pfaroh4M*o>j`Mh*QcSn$I*oHoYKBGab(~%< zx4(XUd3kv)F&;YiU;g2j|MdIMAyAr=0?)-vjhQ;ny%UmOB`(w(BBnA;35q($ z05pyx5s&vNX}OP+Fgp=d?Ygjm%g{vF0Hzp+&@rJC5@7|5DHDZr^Ap)`pF#uq7q~k`YQdLA_k|s9T^$@EEcEttT z_Dr|x=~~7e@5BPT&^;vMWgU&}?t`j3Mbwro-NcSoZ1L*?3&n3G%-|2DSZlwQ6iO?l5;#gdxr(WQDlRDyScN}?M8w1m($NkA z;(z($KUD#eZfd11Bc~R2OFsR(mF5E3SqSv+49%?`BYYn}i`%INyK`loG1+2gTxg1$ zVgdhF`L;b0_9$yxMe1w82^-~sa9@ep5;V5>5s5Z@kX6J#Z3%2h%em8@QwT)WU5t!G z42&%<5I}7r=5U{;KJ=XvKob^1ECra!F{d13%5#jYae08jSN?qr0a$8e(NHl%kB>hfqqHQgqI1(Yhip zCC@1{)w-E#DNtSShEVaOPJ|J&7G_F0dGT2lfJ!RD-1nV$F*7I90>OmURHy>@iICJv zQAI+=HW#K^CK&9XR|`MRwu^bOqAlZ`4dJkuW_D|?HM(iV%C(5$W@cFkkg!a3w?2gM zhbJ@AyI4ZnM+g4E!onZ=!DbIMH2_rw+k7_bNNoSrY{6%hqN=4HbgHFT(PCz$)mDVW zg0=pqrDI`2W4vA; zJfORP9at%)o2~O5=b>#QaO)zs6o_U4g*8-i^Ca4mkJQlexMBfo;sDrUJymMXW?Qk! zv{bF!O4e3DF+x4OgoF`H)d_oICaSNI8t!b`dyi0@3yf59p5sgi;zXFV!1HCi&N;>$ ze8{B~Q)c;(|M<(>^=2gwz3)2D46m=RAD$jQJf803n6l;+L32(snx;67<1~$vwJ1hp zLM9155Q~ue@bMD?{Pgj2-wkBOB+i9uk{@z7g-BEt)E-Yq@4XX$J{=DI zF!bTjb?4LZ!_(u(r>9RJKYjk`> ztxZ{NHV`qVj6`0fns5oIc8F>SX1SEE3u+3W)d(+4HAySRDR@709TDe}5y*-I=th^5L8z2Qqa))Otoh4-?K8?VN(MyY}Y-$dr~jwNR4G_I7vbR zZlBwt%XUz5THyk;haSAIr&gxZwl{37LtKumW--$hK7;n-!M7Y(MOD?*tf-X*uUwYh z95FF7)tiQyg$0C}Ju@+r2$N7fu-0@hXjX4nNLskMk(W}5*0XKVCvBw;A-qck+|fj# z6{`!vY(+RM5v5DBwqKnzd_o(&?_f8$Q?b(f^zDsjx&fx&XS*(iF79BeNbC29Yr(Z0 zot4;7KT3oQhy+AX8$?3mR&#+=NsO5YG7=F8lQ1%pK?x!RR$q^xDHqkuNWq7k^Yi64 zMHt7}h0r)n$HRELWmRUrk8{d+yWUmp!{cMl6PTWcj?u<(e7jt4w{aXNU>^EVnci#XSFg&aXxe*bfJKaW2{995$95B&@=$t$B7U-=T%LdOmP;+DHTxbydURSN^#62 z8spy(0aDH)n6ipUDi(7FFz;MS*@?uQyWp!Iv}n#bABKJy24O~k zn3EYesYYijC7S_PcquF+knQ?i7*K1)WVk)&+fIIU>SH4Tyn|XUvcLsHVyj?bwZGt& zPF24!6EO>s5Hm3|qljzW6%!Iyg)*)%Qh;qty^G<)9piL~J8g&KdRW!|xh%Fx{2Rd1 z<~Xcf;STqJ2++=j>ZfHP7Gh>*VkhiafVd72!E|y&O*mWzW@2tPmdoy9S=iJ5fIr?8e?!S2t(f?^x`JpbtRRT z@%nH&o965N);ShF>cXdpF&3~G=Uj3sInL#NpAo0q^_o&X3no0pS=E6X#O; zAXdt4oCygGQYu6^P0@>hLEjBA<}Y8r^`YyJ2O(0k>+LOsPgI9ifGk`igqH>Un&hVK zd8bQAXuITenPb(Aaf9v3FtchgC3Irc?7)a*l^C^3 zAJAd51CjxV?t@-&%@EzjRk*PvRsqFAv4eMa`$8W4Ap>nEKW(u{)tj@Et=Ybxe zeV*%{Be*{(*SC*LO8Al_QAv>0j5S+mEU&@$@J>vEKM6o)=|CtVlP6rFR&V;`sS@#%c% zecyGc<#rzdkg;R(YWey5tEM=ox%c6C?7QS|cz?TGG#jHcD1kyh41EY9VZ2|1V+Qjf z4963cK8}9|th5FX#EUnC2W+t!S!s z>+9PUOe+KLFm$KW>BIRHgcTs=^7Z*;jOp_0xASojrrw2PcX+LB!Cx`!v;Q zOUV--f@3a7M5Hk};oiGB=V_h~Axts$eL%!HreO$03z%K5*XP$47V@W~6DG51n!NJ> z>WFuDc1zq{+8nT5gdwciaDW?Y%T^e}&ZYA1danz?H3oSbf)VVUGgd+G1WX(6ija&L zi0d|ch1Bo#SGAMG9caLQv_S7;fme0+rok|<|DI@TE6@m`4SbQ1R1}Sw(I6L)))ymE zQwQ(@0p6UIwQ~H|fBC0fOx4;K6YRyiu=2>a1j9vO`abL$fov5*y&G07D7!85YO^%s zeo5Kr@{1mQd0OpgXSmro?T1O)iVj?Q*rtFdgLQMj`%S>Ih+IC`YJX3y#IvQv=PH12 zogg9-)HYD+I%Ay&S&C`5#YX5KR+EdK|D$q82*a8CKJ-)`5bsOjO*Kb(%9 z$tvRr#N4(w_NaYy}mua#2639&3xy!Va}k$UG4@OT)! zxZb;=>x8`%?uWs%pb-)thVI<=>?m~IoKlLBh<(=w=a3+H*99?vIhApq6dGi~%q*sCpq|-O8Oc-uA*%`^GixaZpr%C3 z%rO^JL&20%s02Y%AyS~KyXfK z6G!wOV7dBls(O93!fqj*i|!EDmGv@-UGvAcVf{X#dDCjIeH#%FG7+Q z;5hi;MZ`(dWH2#ti=SAR+bbD!nc?iqtED5v)(WmE6sB!MXuIUp-R#A7QSDl5iG_mI zHE#?>J8pj z1)C+k6+esZZ>e|i2NA6;jp8nQa(OG$MawizLgvN&^zayS%%$WwXTUxL zVK&u11l6fz9H)^8axp^dIzPwc#LYPp*ypE*FVC;zoa$t;n87^tU4OfmIYtI{-W%Y3 zoX{Z)gP9X4IjLGUwNwbu)R3^`EX192<1|4=BfO1s9~=P&=C_nmF2|vtes2FXG(i;bYf{n!nwy!|_lDST(V)CcABMZM%hb)gCnP0NUC) z*1S8i4J$xMibk+!jc{|Q>@!lAW~15Nkcf#{Sj35A=S7@|2s&|f!NynzLCZ#J4Iq6F znE8#L1=h3!v)z4$279dYCN_QFUF}j#4GB<*2$YbFjq9;X3rZCL8Hsr%)hvF&Wn4%6 zuYdffIuG9;AGQTy5bd&GY0I76EdX&hXIx$kzX9Fs!nSe$d)%u#ZO==FZ7jcwQCjis zYY}GKx^_P&gx$M9pj|z;m=&5`XkBQ~GV)(?C|h*dGK?guB}DsoWLgwhyPgrj(3F^w z0Li2l;b1K6gb0BUMYx`HLRi$U_wsx%QDM4I;+z0q?>8$zA|D=4L1-NBTC@wn`*6Kn z-`+0Im$&;IQvp@038N+FqADzX=)CubZa54F=emCA&&R_Myz~BiJRA=F)8ohC&^u-) zl+EUOKAn!v3)P$oM5$qjF?(jsiHL;BlN&;3ruX~ZdzW)ObluSTz90I&>pC}dJzAah zQV7nm8)Lj(Z%p`fJ`P={xs20PPx;OVFfg@P$~eXtvk!irQ!bfEn7Nc}R=R$OsT3`# zC=pibp%acNWvEnQ@J{BGiBQdi$qZ5{2Ij?;QtI?Z4Okcm#km-x6Uikz7Vn%Y5JAc% z_+Vx+B}XJ)<~ao)<~TF5^HOJ3^Bji|j(zWa0JCYFhtMN}I9`U84PZjBFS1sEd3_4g zE`WHwd|{XPyCul4Tk>tIv@q{@r&J&$VisW$=UvCnJLg;oPQ3HZdnYakJFXQ>o4r?k z^G@FIuEN@5qqg>ON$oN@^b*jhFz!f!@Ft%ofeCQDbQp{<)NCaytt%t*|{gemw8mHL|Q2DD+U#53)fvV zz(#d}%!_0cYF=Ku5Q=7G6mdu>q@b-vtLygrtz5_SmoKmPSiJYDDnW*>i%CCycyOeq zIt&A7x!vyHzWvAZ%gb#}Q=GJvy7dyrg3^aB1UYoWvG0z@)8TM99*+-?4~K4GGRGWz z7rY>%7IWS)bDn1h*n4-s-dMQiDV3ZF$g%kIDa})GjsQJ5Eqdq%C*1q~_5SL8_x1U; z?>vdGyBPsGANyEBigC_)=pclE2&I(Qw_EV;?_TCxTG$oe8 z%()aXKqNJ-_!bd&pXWY=+Z@I5LI;PE(y{MSDN{;;Nli6t@Zma*4}4fjj^DmNKR!Gh zk-T>z6w}o6fThKen=T1fV;UXW4+PKPHAP z^Lj?Cu|9@Gzy*P*Fp?@RaV+)3ZmI56tpjU#LKTF_(x2^d$L;W0wgH6u$Rc2avW7sm zs@4`97wG2h+#y%(#~8GRgV&unY8kuNONduPWdpO=_M=_d6S$xgsyMyP=$p5xv0t#A zR#;bn`#i;!16EC6Yi?RAfMI)FSd08bLc@jWZuJDO!jWYnzyO)6_W|3q3&;$dkf|a9 zGKmloXNCLqp40sLcAHWbA{I7->+Sk>z5f39zc{jzi#WNyy?y!i{eGYCQ_PwXK#&u) zUF0EnC+>VYdnX?s9{Ro`lHkJ-x?UV)EV39?&#T5lBGobUc@mp%fs@s->iy zvm>dOa+&8LbfZq7*-VMRIp;h(aorGJFIN%*L+>4^jd6~U1cgNbl!Zn30Z;ewcDdXh zjz5;3)u ztIaPCD7832sDSbX^k565ef`6lvl9VI+iTW!Edp6tb-5cEZxL~sy*KmiFlPD#Ky_2t z>$XDy-09e13jqLVGf~-^Q@{5rvm_t<7`EVG3V0v!2zdSw8pF>7F9+pMVZivXfF48 zb|OTiR+I$5JWDq9p657gky5ObVuYGaSk$cd5`$XjUr} z4S%b|vXvaO;QJT>NQ)VR8AvTft6PK!c?r5&+;nyAkA_ksMVEX39cHzOa+foc7BDgC z&K78Bz*qrYwkE8?O7+|%yc-43*3;?7tU}na9@+sxYl7__kx@Zo$u)C8A9*Lf#J^>{esIi1fZW)|mb?MlQsCo2kIEaFeYc)yGH3iH#) z4;8l}EDRlysOdD`og7k5x9MI73xpC}her%u|ML3E#AtEU>|-A6QZNC-C1qa(^loiLsbm;3$O>-7-)aTtUt^!=1|j?=@jSJQEv zOF<-_V+=kRsui8adFTT&=2VDTwT?d#6||J<3C$%lI?zH~*5^6BR8( z2)FyxWP_?g>{xE&udg0rB`pcjG-2G?iLT~DB<9)D9g*;P1LL$gT!HC<6-VDsh z5Vb-Z0XIj@J1FoX_F1$}>&>IIT$!}SWiH}C00pZhe%q$C+RBSK$Idw?A}mt(Yll#>g_fD9GZt&idYRb9f1fZP{ zMfEz)IVEB+03iXQl=9FGb1E&G#=s1{7hMc=j$CfBYN)2f6jK(aI*hM2ow|%iMkb0m zshW3eh{$NB>%gHX%rSbAoQpU{q%lU%?CNkGY>vr$_w@LPjB!px7<@oPB3VT{^(xTS z9gVio77)OiBC4s92!Y#rW}y_&YQ-nw+GG+DaC6sJ$xP*j)Ib1n;=H)v>Twl!?qcVXrVW(8Koc_@u}6KR%-xgvGqH(eG@+@7%DA7PY*5+@sFxr&!wmwug>pAX)p>@r-{&vXF zmhPBnVdN|fwdy}KGh&xZ(e7zXZ5L`?#13es152xd)AI`_a=T9F#J$`&VKb+5p zL*J2=l=W~t*8C>ZA})lk6Yq&toLJGC@8n1fl)zND6lLO~#fmxcP!y4j&3RW+$|%T=cYM^;Io-SDym9=1_C&%Nc7b5V6t32-EYrI=C660f(8E`TT|zn1W;m28(s zwSvN%*tvPA7twXyOT!YQXSD9EkqM-#&g$B58DKG2-})vMTHswjD!gg+_~0())s}RO z_nKCF7wxj=(ybW}^iJ!(Dn1YuwQ7Xw{YxbE=v;&cm4vs+|Eup#7+_xIw(yG^~+ls!wUd&ThZ{MT16&wX>?ITx#Du zhu{dc6a$2D%Kz7YJdaVucTOBLI^rPi_4)hPZ{L6Y`h2;LFV|6A5P)247!JX8LvWqA z^WpG!zx&-UKYjXeejHBy@p$$^(|zoE5f%oBF^15gusA0y1u|g;Z`R&^K}M?)(Qx!J zMpMO7nUrK|YD&(VS+PPHiP;PpL=cNvO3?t+iZB^sF%=|~UW1txalWLIN~vKGj0kK+ z7`-FJf+pnVlBfF^QxuUdgp^Z_5x%E%xlf(*B67Lj?&Dmn41FL&LCz(ghvEC>R&pYy zSPCa1#G+a>F>}_Eb7rQZrE?*2Nht^K3>yn5=0t>^$bw6`I{46ELI zOQ%?4&f7$5I|Ay%o8C!uYW_IEmXfy*Oy`EVs>Cj=CSs!H{AxSw)n8I?;BC$L{@i-s z8R9mKZc~yid?R#|{Z9 zud}`y8-}&X#5a61;ns^S@1n^P7R>wFmo3BjEk9>15Diz-R$bu{(Dw43sB6cV-%7DH zBM=O6%w&X4ovY`_QxfQMs~Q7b3R-*|yEus%IT@;py*w{@v$~kB`UW;dDHo&;6mlzr75H0}*2^CjJf@iXZm8r-Vs3s> z15nMSAhmB$921dqV#UmbG>_Bm7D7*i%0vKYN|jwltZJ%&h|={|REVu;eE^zs1|Xj2 zan3nCo*ocsPH~*4>-Fj!73JDAgm7S{>-}a5gmk^%5Uuk*IEM&yzw;68av!5To=(rN zuR5!$9s7=n6l@4?PX2la1XYD8rQkghrJ{kP6F28n)DSV1)P-=p-Mk4JWG&vr>?WiI0TG+`5+;Rn-PE$`OUDSf9Is<`A`LJapD0ga|Ey^qr6KeNs_7tX4PtJL4cO+?rLL(6$Li zu#N6ExEh#&(h|RFfCef+WUzpz_tkt|6etnZM?C_9mYlP5f~1;lnx>rL%k%B^K6~#8 zfzghKLz<^Q|LHHUZ`Z8lHclb}V5O9p6CyvIj$Zijbo#rWKY#x4^zre0I-VXLP9bY2HzU zoD){iAR`%OTy7zzlCr6#6iM9i!RJ)waVi>vbB@V@#hi{^SBk#fZ!wk5b%y~pWkVdg zVT#iQboCtCEwaVSNnIRc=n<(wUJDkW2P z%r)_pMbtnD9n+j^np-D+JaJ zeaR|J(>#6o^5q|gk-nG%PP$nJLDkC0)n(zx?lidYcOX zFw;+;KBD2j|3Cls_2n(5e7%o`6mwG0IVK=JACAZV@VlQr{^57OeE#Xv&!0Y?Psh{4 z)6fr2-1YMM@buI+1VkcEi-xYNOchx2YnyKY83l=@8o(;v8B~eLRH;TS7?6lIuD>A( zi=Z$Ak&w2U)imdva?ZJ8I3)yOQZ0zc3RJN3lj^1V^?+H&M_E>6BYtBMq+WM zRpe=oh>23pMa>D1)10%`7Rfn5LJ=8bOu1Bdd2N46Eoqrc(RRU?;+>mP1~3vP+eSFV zoK2ayf_uFa1FKlRoC*`=lATB$-ZK%2+{ZLdvk2d(sg!~SC8vk;IfPIJ7X5HAfR!`5 zL{tGX?XY=Oe%zRKi@=?18N*W>yi)@jZJH@=_0#$hXfGl}CT8bY#5pfDua>1zQmgES zs;hCYV|?Fk?ncJzf9=7W+Zbz`wQF-eF3v*0--a6^ZstR4=0j{utyV^^Ag~1%N}H~A zlhh)RY>gGPQ0_HMq4`Xf+@bc*n!?x0oi+t;ojR_2O~ZXi+3t^M-J`X+dyAi}0yzT( zT?4|FoK;)rVXgDsZ++W|sHjlKbWP)6X0`xg(e~LsyXcZFo_3kn6ErZGOxjPv>*z{9!ou-UC>Q@o+lzeVT;J|vU8#4z6Sl|W@%eJiS*vf_IoG5uYQj=9*OtpW=fxSAXi%rWJ}OsV8)hRcoq`s=TM z|M#8uGNstkNNi=YBO&HG=Rt;@=LU7D3tD))?S0Pe|1Xc}Eso*6{Y;lFJmCV6tk5IF zotb9C-5J6=h376^0)I16-)^s_6J66+LEByaPAOUkz_3%C@AK`=R-D!vjMnVX2)d|c zSM?IDYy2jVT5dc3AOHD}D_M+S3u;`cG7C9s_p{tes8xWmN&ENVLA&0nmNW#d-2O2P zWMxFarg&Hc@bC4swu6@LZ1J|(x7Uhdc{o%l*P8SUEd#6;MhgN0cxzOvFag@Hy$-bq z7-%Iy*9f#`;=%=GxGRtAn=U!mx8S{FaV14WG|SJg_y6tx{`T!{7NXPnd_Emhn*Z(J z|NTDAbBt4*=aiGSQ_)`G|-XXmAqLI}=D z%}DTGQY!T)sg^@uhNXux}c9N@&;oCaS zUChh|0bb_Drm)pi)kN4-4fmAF4^<-C$D>`&)PIY&8pqz^VUM2D<0OWao4m`^;OFX*k~NF zqE?eZ#Yq=BFr3G`8nDnb#{c-s%a_+1vaq;^(}_`EzCYh?cL1EHXlD0m9=g8Xz{33b z>BHv_Psd?6AC8?1r_=d(KK6&>Fbst3oF^nL*@YfikOhcDJhCJ4NcAHX-6a5^rJz>I zha#~7Su>V$d&=ZwB@q*Q0d_3T5RsV>2r3L$q4}Jpabi`=G+8R86ic~Ea@Yxx5B(v8 z(0T9JvjoTqO42m0is)hh_&i8Tt_19lxii|XllZaC-u$$r%)rQa(wmQ{vsLc;5(Ri7T z*Xf6~o!$~0gF6_(GU2u@38y|mt>s!!i)3F0&9GBS+dgxBH-mnMDp>lveRpSz>DJzr z8oPo1I;6ID#g%O*Tdk*TMeo^GT&(BM%2cTW_j<=KZ4n@mZKNuoHEOK353PEu);XnB zYhY_RxrYDfR{bq*Qv1;~xh7gKBe8{6^-ecyw)NOxOPta28Ae*qA#3YFU8sq$8Jca` z9xU*gHRW_Y(pRgGZIQ6sm~f~1ZAv+9^BikPS3{QeL#$+KT?ju0rI;#E&Ec#oAmQ8X zj)W!KebRsb^JPwYKK4%!rxeF=nqIG0=en2@5~ZY__x}H5>)o0pH?Cyeb%_8ntE#(6 zNwddn|Nmch%+A@iXND5V?y5>4V!5A(6#*by)Pp3F#p=8Ofpu~JeDRO{p{oD*_Uqrj zeYxM3U%r0%<=dBEfBp6C%iFpv#T}WwzAON&lv1jh6XISc#DeBS-%C?9r?{3PYF3Ib zYXX$i96;YUL?5m#B(T>lC#FMABoP#frSC++R*`_TpMrFG|}RwA7B+9uE<{t>v~ZuBK*f?`B>~`Ny}f#iY7@d3~w1 z{_FdXt#y#@@{eD?=JV{)wjj(z%@72nb;kHclF&e4pqMh67^Itp&`m^i>s`%2`Dl;2 zln|;xReJ=<)_NA0dvC3`M0o(RxA^mq?>~P0Kmff3!ps99rX#vuXxY6S_ogNdd?d`% zGI~laVQt3*c+Pa}$o!gEf}&R^{mj{}pu{?z3byZl)veBit<$7wtYlZjZBqiH?CpNB+?O3F37>`cUXDg>X@ z{ZEC|+#!rZ%btQ*uW$51S)EYk>~xPf=yQ3%Ct$t~+UtSP;=$!7mAw4%yf{(E-^1xO z$TIhm|6!_znRt;N=hR1EKZXp3>Y!9JPs5X%Qrfn6aND-`_mBVg|Nimcf4oCPtt{(W zth~Q}?AzXZ|M>Xa+6IQZe}DfV^&h|d^5x}TynOre?SKErKYso8%a^Zj>$;kmkfxsR zu(z)2G zm33L)UhZ$NFJHfYbuaGq^?v`)zx`rlF?qY+MdXhkAF+3#tV?~l-Ex5P*n02XM3-7D zH#jp}Dl3v6;cC&rfU30K%^ZQ&TPfD|mYVW>9J*NwkOL^S?yc{wE!9PJ-+M56z>M48 zKR(;3F z{!Dny+0}Fd=@fFSQWaxdeGu!$2J<=lMvvE%pUmZ5>BZ-$PqL27+IBc$8UW=EK(Oz5IJ3byC6n;K75c|GA zTKn?$daw0%tKZ(f{Nvl#uV22tzP;9UDYc}PAgnC=*+x@nd`u6g=l=|`5US3}Q z@#_~s6tgcc_pP-bACGNs=<&C&Z>g1Rh^;jM%Ti*ry#@lHQf_PM(Q}5}BV4q%o+g)| zsaYdBNA3LCHZx6WWicZlraiiul;SO-H%3Gc1mbgV|NQ-5+qRqgwmn9AF(9Jqo->=g zSoFwDI2waddmNgq>z#Zo`4Jb@G_P$N5BCV?#UHsXo6%c8nVWf)jvkZWClwL-Ty-5c z^%O<&=bwAZtQR?N?(s4)wFHLcl_U2}RD$S{x0$K}n6r}% zGEpJ56kFF6VMPy4vTly^O08ZNf<`&APjcg9R;(0d=3SRktCd>njbJUcxYp|T+ZyCj z*SGuYT5I06Z};16t+!=)yS==Bf4?p3e|-Cg2gJBjER@LN(;fvC0izz9VW+1jq^q=6d1 zw)cPi>-WCzCf1^%&joEsJz5##k$%XFyUgU-nSJr#4TQN(3DqfUH~{tM=;~@cJa8;ylz2E= zp2p16mG^U%m?Y&Hc{n~bXimF1mG;n7(2i`rBZv7?5)Es-oXW9_XmjGs4sqFehpRjf z-7Y;VFAt*e{&yOFq+O};; zDkb&pwzlZszP!EMZvXlBzyJEnFR!n!wN@Kb56!5yR3xdFEV6=H)|(~^$33zBRu(PQ zYEcS%GpMjj#>5FpsHwOEAq=x3T0||_MQNU?$;G@1!Du6Awz^90(IsPJRMkuMQaS(* zgoPE)@5QYsBOh$&VQQY|W~zs(FH3oSduu@Jczt=j*PE$6_D3z{zOMIW`SSXaG9DPN5|2(T&Ggg zPc!e&i=~$b2Jk$K{=#r~6nqyjggt4(3hRLG>Bab$R6l7G;D134PkTB=t^Ezn_ zV-P#rtb;wKXL8_0Iy=wM>ll8<3qBLS&TzF;mCg%X9aDZHgixo`akhR`rHzM_9$&g1 zMKgpk5_B%s@yXITj;~YSFQehQ~TWhU>q(+JWYq90FF3X+p zfBwFyDuK5zZy+E0wr|_EZ9QV&V~;3aJD_4qy{*f=y8rU!?H|AXa(jI#rPO5|&`9cZ zUWQzR1O3|{|dQVCRB{I8Gynrg|%l);iclAmMk*<~* zamDJQX5yx$93e`U;*T-{@5-_W0uiF(OD$Fg8KB;#Ox4&L_Dx9DqUvK^5O}%Wq0-Qc z+x_-(TV8sLx-7LWYpGw~zO)`+UtYevEN?IO>PC_G{n2{3>bKXoGQzYGJ!%FK2vyk- zd6_JwrfWx3dhg@82k|oG#t_}wUTdl9Jwioqb)g7@WG`m9F9q462RcQ91h}{Mxo`jX zzyIftKYs5KLhaEa`Ybe5c+6ClAfOmgq?Z^6&L(N*AP{pVeO%6S(TStLA4igolLEpM z5242i$fIugw^O6v3$ML=gmcr{LC`8Gcjj8VHDHD{Jsai)VLYDaka z?SXE^mQs9xwQDK0`08rKy_Vvx6!|~D{rRIs?^xD)e_2gTsjc-NAKSLKAceZr646U_ zFxFbWyuALezyIUww_lf1ym+m}N1v5=Q%e*Qd*l~x=Gn%2U2LRam_Qg|W)uiBdKZc= zq^3$ol;~=T-WY-YXpcv1dyeH8RI#CLlA)o=jh4AlMBOvTckaJ(PX`rc1Q5|1A_NeD zVElkgkcgWh!qnYOO}@OmK0Y6+PV%;{ulE;3UutpprPN|YDPLc1Yth&Ht=7_7|J+&& z6t^$0_iR)idjnW&1qQBe3lQQ7G8Pfj9?bOTV7R%eM?@|5Xnl}PC>@dg!q#^Y?dUqx zZ4!{JMUS2%`Up_${qy~g-~ZY7os7Jj(POT61`2)B+H#0G7=Kx_;JTHXL7_SYn+9)9@|nGJG|0uq3!5>!<$V_>Hys-D^a0^k4qjJ8-+*fOxqrzkd7j>$h*WTgEdxg#;(E+78|9cvkeLgn22YVs47}Z8K*KBO#lJJ%t{@#x)&`~>!Qo5qG-*`%}b60qendU z34BU#$;b_&8P8O18B77y*!O+gUSHlmJ|DF#=?Ywzo2kFtZnt&)^77Kr&HPrEd#$%p zms)>(eC+#1;@j)%eOb&bz>l`sC^@C5_2{C~qSaCXm}cKbiu4}U%32D+6u~FM#Z)5# zM2`7DifMjoOIcF;(KE&J77}lJf~1aGpK@KRNvgm# z|L0lc#Gg8o=hNj3Jbh{dd1+{PnScFNr>K_!&$%uCNtAVVe`kky4zuP?ZJ@876=C_W za88Xzc7z;oovRA}OkbE3;L!`(v8p)#NIoT0n?6zNsr32R7#yfGwZTqD%u`u;Y9o%t zflY^t@uwZ+>jJs%Bie=vHWqj&TT46B7o zva6eDhzcSO&m<#+VeVtKlJIL$5mzg!MJT;B0oiu#ozc`vz2ED)cy(WwJUIvWP~BAH z@yXs1FsLDl4S|ZH6J6EEY38D4Yh9PR6w|jauRpd=E9G{-=bcl$u3p^ie!CM`0JT6$ zzl&GX*ZWOKHT&_{K&t5LZ7uGi-1gpkEOh~MjldSM2h1dhAyozfA*vyfV{P;h;nsU~ zPvs{VgQh~7TGG4Sr(!0kIpL=6+t&B}$B*}qj~}gdK`^P1CSXTaq2m}z;@m0GCUf9+ zJ&`!ug2P_J=TW;HdaKbM=sf877Xzf9jwSs|+V4dmbnjRY4vz+5Ds|U zITO0P*)yqBpx_m?H4K4v0^9X+02~3%IS$PC8mUtrp$^BNgQ-)yOny{h!Q-r?jX(-M zh1lw|B<$x2<0*8MB+$>Q?OCk^lI8f7PopNFjf_<|Q^W$zs zmtu8Qt7$|)>l(Sgb5$h*(EuUf^{z~0m#8_3T=OQk2m-HF1qzABhKA+kEkw*~YOHhy zWdcawiItL(P{}Au+MFw>&uIXw0+{~X4 zK}5tb;Zp^oNP3~EX$M-52oYg1*;>o4>(AeRZ`&pWdJxPCGHZw-iJN;g;hZt3rbcNh znGVdAo|P5W`2P9wp5%1o~xpl3|T$$LJ;A{RT*obwwGd4wJF?*l%k*D#40tR#ceBhMiH6zl7;136HtwAn4? zse59z#$1NaRE>ON5C{-G%sfr3I@b;y3?Aui?*Y~F{{DD>^sUDZe*5|*0zz!tW7{6T z|NcA4kIxTMiY`g|`1bY{ZNDwIZ(qKy%VO^1b+{+g0@8&bj``~0ZYN1G)#5Yy5+vOc zC1^qyFZt|3Kq5pS3@=sDMa0alRx68$X<4kUY3dp5fq=Be9=WMhVc$MoHF~I5B=KK_ zq}QsRUqi97P!$Y%V&+_|daY`OfRJ9?s@JvFssz-en%`=5%J(1dBC@zwFIh)c^{;QQ zfmmw&a=#Um#Y-t=?|ZW9zusRLcNN?Az4yJC5-^h#E_6h#l|V5KK*dxL5w2z;fSBsu zqqCc7@1YoRN96c1f+}_^RjC2)d*35KK+t-K==b;UA0O{M0?7P6AuO4*86iE#R^WI> z=JP-=NP|&2VXCg|RAtV+6w`l{r-n`KlMZjk?5%ia$tObTtm%&NkUZahXUxyfdWhqg z)MJu;oufa8`dx4RDcE?%kJ4U{`eLlQ>n5 zf=Xn15Bmoj!n(i@_)7_{Hvde(W5UsqvwksM4s%B>qI6PB-CcH99h*G1Tz+ zqUW1Co*v^Q9OHIe@_Z;dU69#1YU_lF$aqR$mHL22gsKS9d;hP0{Sfzk-(TL|lA1!q zzHM!5k9{8{ZiG4!z1Ff8Z+rXY*I!@X-qv+ZNJyGzO_Y$~Lv)Rm001BWNkl?tnSpOAW|@pGVzXLirnU}nqAgF?)w zn8zas3KB9`bTlYfYG`Qlvv^ z?yh38cLvsV{rdImZLQnhYAu-uyw+uLzj^6x$NPJJ+D8X?W&<2Y()>q^{w*Lmm(6_oh5iPgCqzsSCh z8+I0ClxtRk{AE%hpZ@D9((9aK9p~q%Z2wulqz8uTw3I*~(kEu<5MUib2Ij;Wm(Eh3 zMBT?2c`_fStI~9e8?F#A9jHxSkK)TaezHjFPjH!>8BN3IJWs$gbx=%vfNsXm=pmZT zzlmc}nSv~+o{FxRaj|;+I>$+-^E13?84h9H5VDW;dk`fU}ZV?M-8O_UM* z{l^C&fBx8dh^e)`)mrurH-G>9bo1WODPx3WzHMDa@9Xk%TdTVc7>*C|N%S!d0my!A zw28RDxkN+&qIZZ0f(lJ*a&>W|scOk_vMkkaH!GzqOIdGPYXD|N-5^j?Pc}{uP3K1s zA-xv&MAZigm=Hkgs*=w_8S4?v)W)Rln(6K3)vfH?9uZKg7p+CjXv!H(SbMS)xCPTmD^I*h;X;6dRrFsKmZYdq^_5=}X7$aZE}uQ)uxe32bae9(rcEV;5dcGk8I@}8rUvwV$NT5@`Pgzg zRF`VN$B*yoW@c3F(M6dpWRHMARc`C;?d5H`E#~f}CdE`3V-ajV;@7j8>fLHJH7iBD zh}W#b1{2#%I|RsDO%tLZVpd6tVniXBM|4Kd8Yd4pMDN=ct(%&dQN?a+z1nlRny1405tEiZH?+{Tpy{%=bM#kP6qE|1fE+hhJyED^j24u|hT*}SZJdmB? z?nND-_z|Mp(Yu)?D#XPo(t+W_F#`yowf_13zCAvN_9{)e6aaw4*l|%V!h(xp8As%tl|!(*1`Z zsYKn>OpE8@OjYVq%}d0N=t-VPsU3Q#k;0LqN5;Na@t3-kQlo{N)n%zmt?su{qW5K4 ziN3B&?_E`U4~Q(wQfhH8Jpx22mh_|+eH6oLr4)+xeB(fxZ*+zS?|U;dx5W5Z?;QaK zvsxM&RHX=`NAI1341s-bZQp-<{P^vkzqQuVgf380r+k^m^KUHmXGMjfN9Ip1}KBY6v7W>m-cRj&sVpMjv4Lf{z0;#u&e^2D?o;)W@p7?bzTnWww@bebGGw6xk#nZqnxgBauhupFTcRL&-OrbT3%J@Ww1f|e)2Cu)$CZ<+WBnJj! z-`e{Rwopl&fJ%?LcwY0i+?Kbmxp)++OOX(Xa8vCRq+MQtAc6@6REgfCN5-c~;x$XL zl8XWXL3A~7b0Gv!N$?I*aM>R3(f538Ms%f=#Y=G_(0dk<%dNhyH!%9%+`M>o)7x#? z_x)C@3#+-8(prxmfVkS~Wi1X$_U~p1SVcr`@G>G$Nvc^oY*f_ENERzw+Y^{IaYH%& zR*-={6KX758004cs3bm60Q~XWZ=XMYAWOKhpN<2aCg7RGa0w_qAG$orGFR))Aw$=3 z8)9nEIYr-xK@>4O!%Sn%lVmzQ6(^xHr7;k_wqU#r?@lM)aYpHEzyuvhK!xcFj`Xit9NF_RL?747_aE~YX$90@;P8* z0kHQ-@%CXnAen490D&ASBf!?@Bx`jGPC83Uya20ze@I_8kO5 z5?I6D#7swUwKKXz7qL!gv6N_=s{)yKks3-j7f~;jlf)YclF@s54YG&LgoskyOrvjk z7$uY$2%-!35jsUpF@a4cgn~-md=bRHZ*Er1ipt_~(ud*AeWo{M2Xk6!kAU zzN1s5o-r@{DShkW3&zA@9`T)L&BZGP@WL_IX#^SxskkV`COLw3#NHg#7V7bRjnUOS zwk~1I*=OZlfF}j6Qf*4db#n1AYP_T+jHBWrky9RTYZg+cqW1(~95IK~b8R`*3GX*vQ|C1L#)53-$aO_#e)dSAa*uZ_s7~UNZg0bE^#4Z>J^1y6fCvin+ z8i9}Z$NR^psI`d6TJh*`v&XŠ@tZ6D3QnW(3%p}I% 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%0A3l|j<1~-s$VF1uRd6+Vq?EFmTIoT|%I$93 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⋙FDVMt6Zd^6xQc6k8DU{8O zOKS=JYPSOdGfjxvBoRleSfzu*G%f~^a?Zpd4U{e_t5^`+nILn@99JJpIZ*Ez5SUYD zBF8SP-xL5-A_Je7re$ut)!5=oecy>CtEwl8ds!B91(&nb~oQbZkZ+C}7IJ8U7 zo83<0$YfIKnUfjphAk6Gm&^$bOe7uyYLIhTnxvdKQOGyUZ0WM{IhhPlRdXo@hEbz7 zh>0%&nRzB&PaUS5*iB1HDms*`u~fy_E?k;zrf!~6R#jDvcZkNE-h&FP!%$mmITwOB z)V!NIdd`KBT9=^=qGqBg=RhhWptukxa8LtNiOMPh8K8mZMB7@KQ)_)%=FKnw(eZff zT@bOPG@YM%Uzn0REz{&&q_yp43u?o5H%@I9e_G`K_~(Cq`{tc2eKXX<)qbcObVR1B zo11Tc_3PjK#qahvccSu(4{z^pc7&H|p@YS~;etuHZ1E6+0}o&qb|V5m>mn|9BXY;! z)i}7U=t*!_Rb7Xv_?%LIKi~J26uN4HpO3N2Y#uDASML*9E9Ctd>Hx!TcXf5M*=#bi znzkMucteC-@)uux_1FLAufF-^7x(wKx#W3So{l4@T!*3LRj31w%hKQe^wYz;hdK<~ z&4w`A60By+(&lNty1w1-_c@i;rt#@rO1ac+XnVD{HlN)IFwb2uQ5TV}^DM_{nZ-CK z(@2Y0tUon+9*$rpW<&wBKxQ^DBwCR>F?l9RnZ|i^@I)(aDO7DL`oo`p>IU2GPDK(Y zF-wU{O=jM@o|eu;B{MiA#PGBC@Hi1U_}p44sfk2}5QDqn1{ZnXx-($rghZ5y9WWpk zg)ej}^28jDb-Yxh)Ih|S)~9(PWb1uy9b%RP<_Hm$TJdKHy^B2?PS(4Ga)CktasmesE8b&h$Y)#Z%O=?bv zNC+a0i86B(l5=9eTo({9aW+*<$u6bmP-jO?&fTYF#+0JLA9sql&O0Dc3JNn*CPX(c z(TFYsa}rS|9G9hTs>LC>!uFkk+)%|har_1mL8JwdVEr>^Z~#|dmc_xP+q|^ambQ#} zGYp%Js?F2*;^hYq@7~m55cfsR9Z!?Id-Lx1zyEK4_)q`W-~RTu_aD669kYSraerfQqYZ4q_n~ zZqaz7s7W+fBRWrn&-AOX<~)ZPF6q>Ztv-mO(SJsa`Fq$_>C)PD~2B%>~@s()!XdRDiv^d29XQcK!m2v9Sq$84krrKxlo%aU?fP&vo+kowcTSPg zN1_Do86AOMY5J94Vy~k9=1X6{c8(MG*i) z;Y)z=6}Hw_-8^{a41l#3Mo&zep$?T1y{Qlpn1@z&5mEErdzW5HnWo9i5D8RcY=P!z z$UvDG?81Otold@D|J}_zQ39lu!X{>b6re`*ko^&{gCi$L0^~LKY-VO6T}1#OGz2tk zB-hh2p$JXqA>nqj88#bYN-2Tc8Z2_8R5_Pn7?8*e*4UD$su3mv%!#Fod6+2x5%e-e z7PVm*axT+6U!prWO;b?MmT7bY&LLPPF>?ZD=FChKmBz#r;CG@`iVX(lpjJ=vN(`g(5(cDzk!E?^NwP6_0DY8`P*q6TF48f8lplGi#)3VH^l)#si zT!}E6-lC=k(k(MNV()T3o$D~f5i*VAwwB#?JB{agKHq)t>Uf&m{HNFNe)z+`{QkfH z_v6ExWqRCZ|L*(myV?143ipyb4s~cE=+t{B#yo6x`@@&tes}lsqXhohs~h4#YQ87q z#p=%-#2tUefN^`D;*8i;$SPoE*hUsNkT4u-*g#sxa*_OJ7bQo#L=|B5-mc%B0e-NM zD_;y0@z2{TSH;z5fk^wSo2#q4It)zQd+)8sbd5Q^d~yH%_uqZ|@vHrQ$IL-=s6)BC zIn=_vE$7qx=JEKaA73AjXX3Qk?+^Q3Nd?@`eeFzZd5C^$}YhqZ)>B5DTH(!_v~$bsAlkumz$7$3>L zYM#m6+dMl_myU!Kp)7EBX9VUX(*OMWtuybpyG%4r%YHZ1oJ`al&2;X1v)lI8Hnk=o zQ!OP0CKwRI1f?q4yMd-m2#SnL6F^ikW{xD}x+HQaz?q27oV0bs^+tWUVVfJo%r`~^EAwcbLIYt!=5^Z` zw8JiKnYA^YCYdLfj!504?zY0*4Cd1b#&Z#^OuMTCL_@0NoHJ2EAO514unNn+#Qt_4N$Dv{}i{S>j zIaA6=jFdPh3ZW{73M3_TSB{Gfx|yvmDx!H=VvDa4hP+tb&Aqp7(&G&&BC48mZms8B z2@^P)84zTIWobjnyP-0Im@tv56lOJ5H!}-%UdW#i1M!FMsA}hNy1u#YZ2|P-(-R@q z&8BR&=hG-%+p_%YAO7DTe)u7|-ygOw?(gnCc(p0z<;#}{zQ4Zv;q^P>v=VsgFr7~G zG`T@eb$2*yhT#`qd{uY*>s@`Z-(Gz1@Ej>Hyo?%tw!kw7GYKLxx)C}7@`bk@XWhlJ z8Vogb!x%mPEbR#htEc`sSq1MmjnCo;SfLC-sfilVCFZw&s}Gya&CT6*vneSdfb>O7 z5plQQe)IL0fAhD0^UH6)y1Tt%#>+ERnG@$6U*_xAub-Yy1b%hc){+T1^L##slVcvoc{(GdQmeZ^zI!`PV;*)y)K%9-3b?2)B8zG&X<0fE zI$97dfPlyy*QP&!wcgC3i(JBr=%z%FOLnzq&wC{IC_Wwl-0a=csdc@+-WhIjs+Bjd;y6dbXRn# zSj1aDlE_3r=qtMz%+#fsc2{-j=t?>BW&^`!xVkl@Wtw$7Zx9B~n_*WsTh29?L8O6M z$~l*cK{rK0;NAXeo|iC(i*$Eo=HTLOw+B%#oR&5l{#=jAXqbq4Yl+$1rfDvvs%R+# zB05@^UQ6VVv#nMrGc^NTx~Qs{QA(xccwPfo2%5}1F|YfmHRS^rhc=j*sD`R1(vn0J zsSZxudQVIj!@Do`syoj3k`Q5 z9w~IH>z;9qVy(wbG70Qhd@!!u^~*I1p6&DR17Od@h4;Mo7)`8e0bf6T9T0$1zQ6n6 zW`9#_C8TgSAY!TI%dfut-CzFZ>n}dPy}e1Cz|mKMwYs^goX7F)!x0d7+s(@lUhQ|= zs5BC#-uuJxcsz}}!{O%k8olyEzy$ zs%tJhPSd-`^JcpvinRo7hrAh5B1D6y^W0spcf;EI2bZpdDO3pHFwc!r0z_440fAy7 z!b}`K-Nfk<D#oY`Nk#y=ln7iqB1OfY6ZH_9sh9{b zQV5p7)?1)Ex-%toNC_OVlne$EJQ_!4&Y9y-AYueDN|G;RddSZ z??<@iiopdmR{&Ebf`F!%T13Rb)HQq-p}jG1CYt6+z|}n`7SY66OrzK0j^M;8iAns_ zi5g-oougTZ625mZ>52$QsOlzmIvz!BS(aMsySI<-T!%pv0PwKewPiUzymR8#`-RJn z?R;X&xt85#cYJu5rn$LOO6oumtX>f%0e_!h zUr#asj2gCG;-|RV@2{_KhGAgNX5OVcz<#s)=9_Q6`|jJ1KYq18Y=gLL2&$_@Sj^4f zd^-N?5C2lOJ7#VYdHxg^>GS#ghkyCQ{_1MG-D_7>HBmR;?GL4Fy2x~Ta@Cp(n9kEQ zozJSijOXYKimHT2N7YoPc|qc)Qff8RrT6$<5u!>*!c0(dA|wY~eA#X}K>^3RqMCsvpK8-DJ|xA1ngaj84#Gs4G2k9+~K(vj!1J`R-qOnBW*UDU5$;0Z-$ZxRZUIJJg20N-RyKe zcagBhb?GZs$kfGL%+ypwRMtXVK&3)rv34;`q`9iAyGcA0T@}>803uA2%I4-iAFg<} z?GDR%I=y)d)4WZomclv31S8=VJOxN9UQ-5G{}3{H$Nl`~NrgN~z?l!|i6+rc@BHw`OW}7{31Q>)-zN z`(OOxquq9U(OcUx&dbsWk(m)e&9AO+zWD6ZZ6fUp0B}m2$x~izBoG?a%2nbQ}p(rQ3efPAO4MV93 zTU#il-KK`bsJG?ucs4UIE2ShPR0D8k&YZ;5RG3-B0G$y{4bX|etgo+5S9I8wngF_V z02=36OaXzB%VTX5aC@JxZ?Kkeocc8P$5U>yA>0(sCFfiyU{^U&VsOly z*LEvVghvt~5=xthm~)NTt+ZyMeVUNifiKXTgq*s$n0M)h9*L8%oFQUY0|zEzV!x1u z6SJF8LL_8F6>Y5}#V@#~cvMZ*DW$eZjY=c9iN;5HJRUjPgfY3g41|4s9InEH)_Y8X z5~m-Xgj*8)^-GCj&9!D(FOty?hr( zPAYv~&eJmHlto~k=D9a_c=__>)6dHLc#C;seI7ZAZ`ls>7X9n+ae-x@NV#2g;2G`T=5?=7jVDJ)mU+dw)(ehQbdWmq+ zO6emanWR5<64Kl$*RZ@>BY05b7B&mz6lL3``-bUHqsj;Fi3d&AVC(mRj2 zZW+9_QQC5Rd>GGT00ul3330+p$DD9QyAcRExw*G#zS<2;7nAn7<+A`jyBQ^pYBcA} zrbI*(zX&6&y_U=@UFLay{pR75`@>u(t(DEtdoP8;7w_l@WQ3&_?P40!DCgjht*Zz`N|ZAv2Eekv0^+*cuc7BI z}^)d8}ZS;|SRgBu2Oq7F!8+VN7!k9D3(Pt2)oS{G)j!^V;8=5RbdnOaUs zQj*>?qI2AEnaCcG$KE?p-1)$)N4NqB+R}KXkHYCZ9rjnz@@u{CHydy}pR`Ha^T6b0 zl86LJA%rB3XyejK&1X(bJ0ki0LAbyNe1VtAln) zoW}DsZ1(dw?ys*mhpQid{PCkte{p&`j;Eb$)vH z_y6#ZITzdQfUx@?Mc&O_nmDW`ieUTs zT3U@<6uKk30TQ@bG%L-G5D+NXhvxALWb+mBiO;$AwZ;RAzJeh9l3@m1v4M8EgT-YX zSGhePU@GP6>TbK)<&@m5_eRXGUcLJ6yYD`F^>Vw}u5BD5cWBFE8dh$crsYqs-x1SL zN-kx)+YYtJvOGLK{`mIc#~Bk>h6L(TG1P`puvNSboOWzHf%p{`dC|wC~X^T15oMdTh z@XF(i2X_$F{b4^%Ga{NQBa2GT6#-HZN5IP}OK?Csp6AwVSSNbDixIf@4v4MG+v9jR z>=~hyQfuXukf=#dDXEF5xvxkwb6`%0;Dmt_D9j15_m&tB+ihmtl;Un+m@pAy@9pmP z=CIw~TwSSZNu_Y|SiRV)tDRe)TN8k;VqjgHx`~>BA3pf-)vtb&uC8WjO|+XiCSsye zvE=>DectT$cQ4BIeLmdIYGQDH_jY>w#?B+FRi;{pS}QVRv|myJ;#@bX>Z-l9lo%1n z4Tw_8oHBFj(o}?)xD4X5a+%Fi-MIPECd8CDGZQCbW-)byoYK-3aHk8Gn*dYJ7olD- z5=_lREte{yIYqt3bPXM%Io498_mnfzO2uNrHDS_)fOF!M2n|eiTL*Vf%uMuLSBsbr zzW?9{R{--a=7@|K@dqs z@6s3<5KYa{4I(_}UF3A0IS-tRnG(_Y{1ijjR7-EYluhsLPe1;+Eb<@z!+-q4zx)fh zUTrs5+u`f)zWeN}uhLMT#)(sgR2=;3_O>r$pU>WwQgYpHk(oGew|gYIy1oD8^KZWW z^3$tbsW}Zf#g;fK5P|3u*A+!$feXG|MIi=_=I$2AS94GUU~o6J^n9y&zb^dQ1Mq?q zy_{LD5s&kq~Zp8Z9H-+}xE!%Oacta|AB(xXfiTT@pJ zvX%5@p>5riVvO9o)|#7}ns(5dvblE=C+cEEi8)hB=8lOs*Ee^ce0u%j10sHUdMah$ z%&FF#Ga@74gUBMY1fi2wUP z|DO|@V_urB)+=%cqf5G*IqI(f03ZNKL_t&|a-=JbJ@T$*2p&mk2M0oP4h$+&zvwfCNzY*)`oWv&)ecl0yFfe?W36!shJsRR^xjjN9GK zOjY^8JW#{>QdJ^=ATtr+VW#@N&ofQqkWyr>W%R15?2uJw32 zU(RO(C?zisZmX@r6uP8l%}j@YW;)m0LK=lsoGS{7BON|TCw-usa>O0D@vh^|&4aPtI5T_RHd1jM9LGH-XwYY&QXh z)q}&qVeQ~Q5v6*RZv(wQ#kv^#C=sd!=N+OW3|7 zU)lP_{D4L9Zs8gK_FE$dLkFpbbuteFE{JIk9;Dp&cidvLx|92n`s0oj9{wu-S%I9K z5!`OK__`oD9m=tKDlYH=XYTRv24qQkeWFx9g(O_y%GCC8^$P_;RDPc==c6)z255OH z7>-cpXUS!2@7kx3p-z)|UudbNi)eZ3=_d)1{@A1|dsJ+6z56NDHQ3_ur+kc~2-)ih zM~D1kvO`(H75o^HYbE^##+HUI#9?_odie9L%phF(ZDSQFO(74@K~^S)MF-=VQ&k0; zX336Ik8WWqq?gw6@a8gE3(V&n2v94LEU;aO3I9hTOFM|L=H~68jr`=089&#B0&+v+ zmi5K>LT7|rmV)8!^Njt~dp8`PFz#;f09r@Hu%c!{g7+d=tHshfCz zRRMlNA+!cvovjPLzDd;7;=+T4U6;przpg9dTCW4cgd!nzep<9|>cj!~)j{R^FcR;( zk)}+YB?Hm^w$|Ldf3`~qTe#pCXelyy?3s3BuFN!~R1uGysduCkcs-!To1Id1@VDdq zx7W8I>}2>{*af^;vi+i~TGOYfP|M4WpQWY19wuw23l~ex``k4D{$2BmVo+2kM2@OZ z8jDRFHa4h}9C3bVqLY~%FC>%Rik{hCS@XuI?=z2`Ponk_Ek%}KTGxz+)H|sGuggcWLinKy5Pu8Z zYY8Jpu4ydT&Nk~L`=h{8T^lbfN!RM3S8IuPvHHK2`u)o@jjf1PR9Xw`ruo*pKVnQ` z1N<`A1ZCyk35sezgNd8X4NM!O2jJ83_YtW*fhIrrrQ31tJ(_c|L*x8(i>9kqU>NhX zPU_0c@$2hc>{7INm-5*m8(M_9WDwWT@2@+>KpU09Q!vROcn zddOY>3;$acIyd7t`qUMcxd0~MR2^O>9mz5wpB>LG?BCldF19y7r1(?m{oY-Siqi&` z{%N|$VD%|931P(xUmYxEUlGu=IFr2%7Wh)$nGo%go?+rFBJmRK{4Ifs%CPo>hiJsFf;@ zIpQlNOdkR5Ikk-mAGKYfyO4KVhXp!|dn;-|Ky2u<4O=4y3JNItgLO(kDbc>24Z)e( z-ovBj8}~09MFTSQx#tg+!+q})uiaR5#O3=+ zyBMe0*ie@kKTmadoklN4t)+|MO3KmtR?%6S#tEnzwlU(V`js%Rp{~J~MPC1p7R=5G zo-gKC9i2Z$EsnI?EGjdQ#g5WGPrv;+{+6`heTD3pIF;8XO9#zmm@PF^lA4og z)pWI#un||uMhf|?)EswOC8ZJ9$vdGP;TgOh>i=m$@cPVeRhIbbVuei)NRW4sEi$C1bSD0)}R!Mo4QSczJi+q zE2mGA;DWn19@9gNR{<4ln$G&T@0vbOrA=C&R#$3$$o*^SQ&;rt8OQ&#Zn__DuBkj!8&2Cskv$IhUBr>%W992a#*UU!BgtSR#h-X z1W?8rI_YANnujT=@+>A@hM%nw+9OJ?cAYN&;H=5MoD1ge%a#4dO%8rW5>k79UJ~8^ zvNZ}0-ow1aJgg2o;$nBsMtKh1=i0 zw4=%Yi^dU?7P!Nx274^o&zp;Xb5TY9>JCe(+tsHeGe|}p+-)p6OPr+B?7@YSws5_0 z+}oMn%a2j>-7zNz(YzQdx$Ts$TajUR$v#NZn!j3i+=L%IbJ}SV}neab6p#RUBS^2SDvnTIi>BSPThvjP#*_I>Z2roNtL`KdkY}ULoo{ru? z`qhpBYL?Oh_z=~Tr~?7iU?>9((*S1c*Y9toI2uLF-P_N8l0Sq>d|J7E#GmSr>2dtG z!%U23=?FxT0L;k$s4Ll_VyH(M4OeM7d0-{hf;5UGi-htHAkWfu^6+*mnKMUpz?oJ& zlL0~zPe~^bXidQ;63>2k+R1{qhk1I}2*@Hx zCm;exIsB(-FZz5V-Wj8~-(Zk3lEtY#WzNlSU>$^k&#pe21v%URpi%j?dIS`>$x{#v zGT(}F0l$4wWmM((m1iysiz}OcAMv%HwWYsT573F&etooy!VW*$j8cTfWx6yKE7aKiXzKWS zoDO?`i`XZhMJmT>c~3A30JWdvAK|9%3HkPVD}jy)YiR-)+rS9%p6qm4))n zzr(|$KHn`ZdR+X(m7iW)*2E%byndP_&+|fXf}#qNwI;q)w?`bH7Un{GZ-*o$%8`&t^_PEA{I32?&OrWtbAw-|g=Sta z{mWXjFk);sN2vFtq02Fw>Dm??&aYODRkdF_q;F`kya=Nf3%5J*ZVq$k&L4L=+(1xKWL5>lacjgz<3nnYN86GisiInq zBkL;xqY)*Qcw~D!UgD+KE7{)DMmf_5P2@armCVsLSw=c`H;queIwmbtM%;!W<==kj z7edGM2rW7qg7w2VXsvh6Wu|P$r~rV&@q)NpA+#A;lp;7sfRFv69Tc^%r=9HrWlI(4U-Wv+qnhR5S`P3=!hubGN~;t_tJabxt3OuHv9UNdVfpB{awQI z#ZvwbS#Xdn@Ws?sgyRb8onrp|j_sUp#IMDk%weG>&B*7V($My?uFKGn6Bg}p*}L(t z(nm;zX6se42I6ngnMT-xgHOM_wT)4F*l9f0`VhJ^LE8Jv7Z-=;)!3!FJo!2qh_`q% zEyE88g8(iX8SH`O!<@tFuV`P_wLa`lZe(ZZP4u<#&j<3h`?h!P3&q=2%h5#~{64%4 zWp(rZzH{6G@A*#0@Nw*aS4+~#3?N5LLu1O! zgP~zha`8wGMYHVv1r7BsDj<1E;3$g`(|vjxILBi;SYl+W|0r?KU={vccohJ@2866` zNsOj!G!13!OzP)xeISPtzJn^gA$B`U6K6)H9>ImMl< zAkWXk_|*BoY&O;t9TqsyGURsc7n;Ex_S}yssVD?0hhkXtmE@Qx0ToXlMXBzjTR~rG z`a*Gqc8&`L$iqNEZ=J%XQC4U8(zf0uBcR9Vz>=p?*p8l|L$c*Dv;vf)BbctB$uICr zZTSY9ma(q~NtfH^r)AMiGIuNA8!rBq7{Pl#ObLU!t&7+JdZ3)qrO(Hyovx!msp-Y3 zBa@j3Bo-zAtw3_+wt`X$RD<7jCS;eWu%7+}^_`5pO>?y8cmteCyhQ=F`fUFF1IIL`lYCamZ#;4yl4`%w+I5CVa~p{iw!#QITkuE1Q|RC)^IJ1R1t zrEj*}&4)eGBuw4o$fzzmv>%pVQ|aFeRP}ALyDk>o>+zXKDu^q&i=16q30HTLBr_q7 z3l2UaM^lc*kG65dz5ZdT?zK7%UbBMU2ilZ69-GtKB+ur~Um!{uBjA@2-I}8TwC9a8 zHB&1%dHwB-JR+~}GJZEuZ150sBj-*KXEwJE?7%~@C!G}>_EXAZLC&8oQe zHD4xyYb>%EUweMKxh}`+3Lw;;gf*9QD^~!SCQD8RSLDqGWfpvz6m%FQ&PL_$g)Nat-W?q*Xzz0lG|_EFdr) z$n#ssc&JwZoqV(KnoZ5jEJ(nF;yS95IHcL}bJ6DEdt<@)B##*w(etYwnJcCYPT@!2 zAYX)oRnTorcHCUWmGy~8RzYWwuNUHbse27>74%J1T*7MGirAVhR*_owDEzw|tE`IPgKafRgGF(F!a?H$!pr}=B6TS4M%i=5h$uYf;tIhBEOGE)9G|Ipe5wbe;%o-7_5zHR-I%iiHO zJSYC;gU3yWrbB>Cf9QX55+SOr4Me&T<_Lj3`ne5Pe6`8A4V+kk~Dq{5TJe_Q$x{GtQv0Ikf&nE|K9$#0II0CM(MS# zu%=L`R|qY?$Wth_OXnm09Lm{cPHN$q=#-}$hFa}C;i4JV{YiC|7oN>YvH!NShwe&F?QSEfGQXh9O zYjVjyJaT?Ntcr6bSl!X|eCfEW=0f_q5~Rma zogG9kywfD6VDRNle}i~6$I(8uTBW$Zo8)cfZw#NzKnnndKN~Bcyd(coQ-OM@Lh`>_ zcjv7c30jGwtLgbhs}oP}FAG#mGi0OX`>G@2?S0A&hqEoDpCcJHRq^xk56tU)cehc~ zxMANlb?*(7xxG{HBTzPhf~Edtc#&Dk$|UKfj$MYlNP_8Dc97)xN$<+%2I+AWAHbedC-r$qW}igYFyK+k_W3Rr8;Q~TBm2CjtG&I`g}S-esB7dM z>PSq$Io*MEt1yZKQ7rH!hKwlq_wTblt6q=iC@PYx%Cpo+0>SC3O$io|7jR*Lfayn! zcaJFJt!|mL2JEXJTxpz!7^5G!s?a!7>Pi@L&pv-cZ^Djq&*T;iY()K(9iUZsqPsfa zBygR#)=DV^+^|z<)#phGElqGUx;lB@QKTrVl9rLY$ z!10Qyrdk32Y6WVMV5nc~Lx1TYG?ys9(aRE6BU~}57A_vBmi;IZVSs>FZb3v746#|> zOf8QW3Z6XqX3N91_W0z_d*1@jj`r@TgG0Sk9&p^>HNF~2CNaisbbDO*$@ZYr<)c8~ zzpga<6A!_J1G4zny;Dk7nF^C`_BBqx`Qk2GjbsoA?vh_J&Or?R&g{@h7&hjo_vLA} z#^wIic3j2-AO(9$$(1DOldwvN!y1*n=lxZqxF-+Vub2|MX@AC@fG@9R?*f5Y(-x|z3y31;V)w$&mdU#P#TgQh zgkF2Ac-*LGU~|Wa&y;zJZW-9A%lNJ8DAwer;7p%3&j4Wn0xYZcMZkw32YftTyqN!; zPRd0Hfe3Q@?9ED5F6U<0@HC{^d~1*((Es}~ATBEC-7j+nT1ince|}VJNmc=Fyw$eM zyq$?(GyT(hozy~q+(|F|4&nCu93x+)yDqU8hZRsXCTqnT)6~P3>us&WFW+3ApZK8& zNwb7J^6%y~l+ZPXtf)Cvw(<<#G%(r|%9=E0gMnV<>nJ^{aS(j_USyWSnNHioSsU>1 z-Ls@AN4>c})YqpOzY*-rX1<2Vuz+(L!@MOhA&Mru*Lc3lx?^vSXuYDT*$i*}cYNu9 z91RE1^G(<3iz>r&%kDAjVeQDHp+kFeK4zEdx!XsEYzu5kk!5*REPWCqLw8NSY? zff$T$j6Gia_)ApTh<_E&aT5P{+M|WL&5}ro2&;GE3pE>I zb6w_#qNiXcN+(kAYE(#x2y~c)7_$1xV+3U)lmuBE&v%6%;bxp-Pk)io^Eohk>Xexj zCfV|rO84)~zM?ePVE8y>e(U9IeRLPC^uMv%n|)@S;&g{c9(cx@FS$8ZNGwvSJN#fd zpA&N%B7gh3993eG*^1?CS^+T4@|8~S->t%`fDYqHHrJh$sfx#SX4&n7RfNIy-cA3Z zNi}cZWS5Yumq?Y(r?MX*pc% z-aPkLD|G{A@w&QszwvY{2(nv~pbdj)&cI+@U%z5Y1S{E8{BgGvaJ6}^@SswhJgtmo z&`|5DY85B`9zP<-XwMbcL#nmKJT?*gDvLO<$5^4^EcoRq6uhqWL%un=UbpIEkdGXt zSR-Hw7e;BDWJ)li5r#HTUkU#n6npu%;oh!R4OKpjEAW_jObib{M7dktiMaZ^^m`8t z=(-v%8xAizj=JYmYdH?9lne0h-3)Kr2~8QSvf79XzaVyBZk9RDqq(U2N9r1Xm<7;? zHVKK|`UC_IY1K#0FL2zrF3PKZa1I;o)SKt}4AwTr?>ca>fS-Y!x+5<9;}$A5h`4;{ zL%FZc7KCc4tLuw<5!~5hX2LVF>!hV{%Z)Nu|<%UhucM@I#C<3k6d8>YbIlAts0`iXu0;pTy6wZ9H=j|}h zVG(IfYIYG_>L=8IvY@UJOxwlrWwjIIUYlbG^>|42zILW7ZP4@)N~!kb0j^^)k zEYW4s+NYMe6gn34^8P0S2orhN1lY2$N_@TvGnZ0ahXom}z5q4(H4Iq$xsC10*N;tR(1qcKY(HxbIc95rjK+$X>X+_M}&OVOV_=L z!cT;h&RkL?5TsYNd73E-gW3M(c4<)No<=G60f(&zzm;<9%mf0!H6Aij75g2eU|9Y@Xf z^GzQWo>0-s%GWbAG;7sd;2-3yqobi~F|} zcj|W)8FyD%>-x30#9Wp~g4e$!I_vn)u-EqYKTTakhpZY+G44cK{4wi2ra4Bmzz7Vz zhKRJkdzY8U`tFWyTR8_EK#NTt*Q%YDSjBHy87l2L@1`&*r-yG~KBd>%t_$*$f4*NO zc~9dBzD{$#O}F4y`oVNYGDZ0W(n@>TRZ~=CcW2aw6@K8kdf9Q+#pTL*SQTl>UkzKq zhB5bghfocV`p@+)oXQgF~&qeWo3a zo5(pH@Uus&FiZ~3{lTYAEuThE)1KrI|H-mTTgjpPtAG;IC-l{A)2=>_V?AamJn;l? zvYxHL-Hudkc{yf#qqYUo(9UYH@?}#YpGzoNvx3bZ)C1d+C-S>Us`PDSsc^Vn%e`$g z+A5(*GzYyEr=tF_n?}u~n>D{zNdA9vx!DDuMFru5*_fpq;FVkPm0t@&4I$zYEu%2JfymvOBy4f1yNq($}r&P}F&7cgPxEu#$Rh-nL|3+N>30HZop&sjLsC)QbZ2{+qy-;@ir~AS z9E%I-^xSOQ3s%LFY%rswOap_wAobVxA(cc5Np=JjogkQK@RBDd3GoIXGo+f_E>Jza zJPOcRp+a)KjrqPvu=_)=131 zS${P>Fdr#!`44$FoiiS>Vc;3X;aWU6>3JpIBl;r9Q&}2P2MtRR6ckw+RxY<=3mqI- z`Xa(=MMG)rI=v#$+DaR=M{_(cB zz#ROG5MI5v<*Al|P}kKRbRT5$eFY>WARTgwQf(3RuRp?kjbgM|DqHj1&s<*>eKo;p z1D)B+`!oZwPs>b#c+XxmUL=La2P$Kg-R%s#Td}YROdE!y1 z_hE$1cYw*eN^8bZZB@H)#=m6@tk>6$EYg=|V?rq#V_R&QZ;5EVL7wBSnWdVyOF>2w z=){MC(q#+jps9UUadJC^ck@G^~6k=#f6DGbpNdu(C<2ns6X z@rF}}qhfjsftX_xL~tgGgDm-jZJ1wA*Fv#sw7wTV*H~|TeVc&vOgk4qh*puFrP&U_ zZ2B4$_R6ITTO)y#dsV~2)DL>%E>15#I#M}f z^l1OZ`4kvnv*1m3S{W=p^tu5Eyu;^=qNfBGLZe zTYI55v?VGeUJ+mLh=&Rw7OC0Z@6+91Ol}DySMVCl-EP6YAO9ykE~zeYgz9$?qrML> z_*&`fz2ZX04_-Ysc#_rNuA{7*o&Z!C@{h7>Y#|1yQng7JS>53h+Q~}sEF~wsN~$W8 zjKxIL?n>Wx)v97=k6+{bFsb%3qfKxr5<|~Iv?tVB`b4x(XG>J{u_0ZCc-&GoGX_RS zZvOmA&uEX!10!1Ks~8^EydJBA-zwTRID`mpEwPRn`{|2c+zWm#L6+imi1jnuzBoc> z^bk`vpToGsPtr=Lp2UD)xizc6BJ z%d}|jlIg>Z`%QaI8Vh|%aLatKHy_R3egopP4k!W{U+1SMZA*1u9+Su5%VqaPGq{$f zkS0tw8SDCFxBV{}8hdB)`{L|&E96CiQ-F`&aA?#^SJzy&6{M2i-o9^-E8Wv2;0ur& z*?Mz-sE^GJ$epiJq!i@m>>*S#Sd9W3A>TBl7LStT?{&sS#ztQK{dcg!^!3@z+^OI; z?uG6+rxsNObVE-sF|qDl&lAEf9+($^dsNEk67`%MS*qC`>yQ?7>dyV zgoi%4V)}X6+(7L7<8Vl#rf(%`_a-&4B0ukif&2{dn{U;}C)Ws7acV0& zNfvnWwhbAh6r|DSW{jrH3>!hCsk#R4qZNSR+1{qD>kGEr zRPAgbl)f>s*v7Hza)*p0jm0-VvBabUTAQK-BnPB}Z600?U;2HEyu5U#Y`jxdpMb}^ z-3cLl{6$SK{^yJ|)^+ypbNg<3k-7};$m>G$85MK9v0*BAAK?pjDr>C0*vN>gUsPw| zth2gr}p;L@6_KUG(UYrRy{C9WGY$T}m0qFKT4rhoJabIzjZ*!em;|G&R`>!daL z=j&=tqw6I5J9Kp(jOwwwv@YlPYus~xgD!YN0oo@hm;$TCs2=XT5OtA2;0H73Kn;JM;Gk(l3PHaU~K>oWU+fAznRT<4MTXcXf#UdVcFri@I zWB;o|JYO8ycyk zPf}KtO@+tErxO0WBOC#BWWDgdjM%9ZiKQkYnC(MCkr$Nsao@ui>-ae|k6*zp zw;YpTKQ~1H4b?We)uJ5VJ^Oiyr2p}Dngt=Xf}kE87F$2~-)Y_P#No-FP8mW*zi3db zU~5mQ2Df`Ga>SE>J}-3LYIAAhp8FoIZo1Ow;Kv#9UFDhlT5Y8Sk17Mfqf>U0a4GF& zH}>vsQ&qovvFW5YDnU5iJ6WAScvw8!v_OtO6`1(;)^$caQu>d{{nKit$5ur&Ic(g8 z4=CTvloUOC&~f^VyYrNlp8f3U_ONo}BPM>uW%JLdsS=YA)iS;JQ^n>BqQbjww$Q5K zwT~Jj6-*B1N%mgGiSaDj=M75s1qmDy6;>YdO84phpel;AD2>uzPs7M)t>N*S8!fi8 zmNg1)`TEGB*6SpDczOdWO`N$ozvn;Vv+3);ixv_d>VJ6TeuW62h*)ry(I`7uGnGPg zKuAbuA)7R(SiyLwd_HjkX6W+{{WinO>xK0DI;@MOz%x!gkf)?ABCR#AM`#c9tTXb~ zw^kJjN{Ac+=7Z>|ggn3-$$`5>PZen@K@nHAW z>6lXUiIH^?kX~qLYi}=FO*a?xg5piWZAa5V{yvuYBH<5ki3;NEClQvvyo6GkvdSHFAFkhR)cxmks0bfz!Fgd{uL;x2xz`?aw_ zq0se!vnd*3*Jbxi_QYDNZE`&EGs5_$lS7-Oa33xA* zPTEfDi6%V6L5c*kmJzJ1z|<8}B^Age^bh8yj<8uMYU+A-s!-z)O|PYuDZ8Rbm35fu zYX*zoM>Jce9Su)Y8XoD6-=mwC6Ap2Mm_DuNJ&_)7g&B%D(#zD)hgv~=(Mja)w|}H6 z((HhMu!jeAGeN$RI@_r<>wGhOj#Zccm6Id1Qg5IzW0l(Vwb%vvSY*WnRf0+yt zTsZaRWIn2~LttC~4u*9ecgeFWFrhIe7N$EMCjAM^I{3EucgI;n@pDnxdjYJ!vURA_ zW<=s!%0#MmdAGB)j=*5>b7ysp6@neX7JMCRD-RF_X}}HcNTT4`9B9gx8keX zsiZ~3D_L9nbzh!^lT#IfeHJB(3b}p}kR$8+G-n#>(Re9#osz#V+V6JCz6_g;RoX3@ zBx}3m_P6@%_Qs45Ztk+!<}oiI)EdplY>Q>|e0$;zAXZO%5W#an$6`I2|o+eH{FM=k%uUV~CUd}<<@jYfU{};>gfbN_H$Ab(m}5eN&nwSsk!fn6CD+ zs0p?5wt^mmuLoqp`Xv#J+a@T+1bngKw_$Q08O2W}o6quOp{;D6?6SC|nw4n;D%&M} z7IDyGt;lOR`o1B9zwbZEo4B;0Pq*RiBU^umr8v^7^N6vGZx=6{`PIgtM0+(ob&pHb z2Q2&_Zro71P%mFiaRO{_V0^ncZJJzRFc2^#=JRmlBZNi3`#zy0!czfUAwS}URcW?L zP>eM-Ogg3|lDT_@Nd`|2ONJwCQ-fSz%!4~tGlxu$K8dG(Db#)6*D9503jDFf_G5Z` z^E9BVq^y)T>8U{HZv7tW&kFWPbu6M}BBynADg5)aX-T9Vp}qBJn&KBU_H4Ubr>2#` z_#4*5rOSTWowt6k``;l+)v57Bb}zqRQgy6A%51yMce|&Ktj7WZ9A=ywiL#f+N(;s_ zO5KrRN?AI)$(bdh?BDR$C#Z6Fklheo>C4;a*BskZiv8|D7x%&y@?^7mcL}=(aE%Vi z&kC~<@eu7s+YXA4&GXaw=Gya@P9e5Y`<3u5E~%KXAR9Xe$1eHJ{qD#yelJ3%eR{$peqBt=TWoNz5@)3G}b_?s_OmxtU3|_#5c=7O1^1)G(w;ia)fkOtF zDlXq?<44#zx#@0@Wfx_xRhO#r+2We&t<6A11#LJ4JpWy+orU$eFx4Xt6S0*$za$n} zzRD_w2H#6`>7OVxbL#e2H*2lVqX4aiz zCsfOLdAEjN9(SqTGj1nDs(&`JWE}At!Nw|FeF8|)Z4=m1`%qYU`9F0y1X7&Yq*12O`c}u}f z{1<_RW@z-w>r^5}aT_xn%XM|un-YZC4995f4G3fl^M$sXPblYqS?(wqbM6sab1B}2 z5U+KW9q)z4svyrh>HzAiy1FdB8VUt_OntvB`{lvzhAR(;4hUEu^mfE2&F&}ZK>OT?FW+gHZcEKTk{{d2|32bl@+Mt?3L zUR){>w{I4@zZ-w(r?AG*H*ynDNOnQ6*;Yt~IB3LNGBBli`o1n#<}7}$DXm)ER^#g) z+=qtHer1=}l>Z@lZ{0GG>CKB&6jxh@Vcs)y*Wd+4J%^;op*&-8)odNt%5Y-5pzgua zQ5MTL{`!eD@DDwoI!qCzE5p>3_b$tzndy7D>-SVKcYqg~tB~9~CP_$32;dU&7!?DX zdnE$In%W>LjJZ(&65_!~lwaj7Ge}E)!wN(w=)Yxm&tX~Q_sfrAiDqT$66+RRbV1ma z@Dm~r|2>W908GGak*dvqro(Hlv%KmTLffs)M@uVJYyZdt;ZD_h+0|L+NwN=m_!O^k zF_=f%3vcP_ey0{qBJd^r-Cl@k*5C~#hl5Y|F0lufCZocSWWkiR!>GL!!X7zkY@2_K zJUKDgpoJJRsJMV2V(bL=*?{Ny&kFs(%rnk3Uh$x?lx@$sD*L^Kj+me*Ii_D%6hT7s z#nyYmYive3q_x1W*6#!5hxZn`yCY5%*Bh4>Kn~A~)(#KX4h}vw4HmG1S|gmSwGH~J zBo{8KuFi;i*rQ#$cjHu((UG>qpzY2h*bE9q>ahB;aT^jOq+o24D06MJZ={xj6silr z{A6rFu~7N;^XwDJM32F6(-J5ncZa=WdG*J8CqH0R3~Q53|+mM`Am&utX|uV;$&Co@WOjZ){uTQv4k zC2|#n`gqyb6tcj>-#j$F;T#rIHIy7)B(>*JCnlp@*Ljl@!s7Npph($xxS`fk&Vn3x zu54{fU&{jpaMHSFp1W~SXdk@M^~*6gReS!fs0OYqotLn$Bkz4$evd&3y)Bi{d| zT|Vq%;aHoqwg?>JTIrEy>p;IUaEph={5iwtwp-?-5x={NLjfhn*713PTJIxD`_E2K zt6w`sZlh$&oJ`Hes7R*=2Y-GaoJEF3ME=|j^*C(TxS-RGJi+e|^qS1_?026kX1A;H z%*8m~s+xOlQB;Y)m0E1cMTQ)^Xgqq*-b(?-yNk6(ld$#3W%<>U<|tiC{@pjOgPxo$ zFKdc3kZ`p+$6vn%g8vu@cnLL;t$|R(qT_Qse!F)G^!XJ1FZSO#vj#aFj;pIQ z)wP-#>V{MhM~^m3PUo0q1gcxZvMmwovk({M}Zu3i6&hZrOmsNls( z`+=HNKs`IYO~ej5@|?m13r;aNEbnPB7iUVHDqI8rIOrefYFV1*jW`Jk0yaRB_x!1J zI3A3+jb=&b89z<6@yh6Gs3|q&xW6@gSkQf0IPCv(m)n6bJ^M9J`ISqDs`0jLI~mw^ zGEbIP^2#eo9e+eT!XqQTwWh+CJ4QLp{ySY_^=E?vbBrIgqly%vJFgPBPJT;8Qm66uFOkip}T!T{=HJ|9wD(N(|WndrH(J?WegcmnXyh7OAfYCiDxaA*p!{>GLdqnjMwQ!as_2p4wN!V}1 z@k`H59=%(vPxMW{IS7XTh`j$huN7G{`|!}-3v@sgW*y}~ydJ+-8KCAUSYVz!TihPV zHBYW8!PHl+udnRx9<`q=Uj93H!g~6*AP%eIw6JjO-W7F@n_0MUDPcOtNBwu0N9G!E z4P^7hT^??(kU4YDuX{h}Jdx`DryLg{KX&`OG3JO&Ik~sqphOXG`F$yTXvIG+>gsa4 zJ}x%At-F1eaU_#=nlOf8d?qFtv&Fv01_cEvjB_f+xAea>t|BxdLv^D&$gq=E-W2c3 z^1XPpFOeBg=u~PdB}I()^{MU*8#VR|zmxlYnhEeoh{k|dG2c#AG!Ev+iN%mt+3 z!NL2+G=;onrKq#GQ>m+ST+W`aA5cwr+VTGawG>M0=hL&)CFLx2eR+AVy@_dY>kYAL7F=~Qbaw`IOOy}S_fdAXRIsBBknQ6d@=Z{@3SFXqAB zU)E+8J!swywctqmsmpsT&h+xutt#NRsl)@v6Zh*)`0YJCzU5DM30jjpv zd(LUCtC@khs7jY17g3$2i4&^2h#BJ6EF?6C#EiD}5T#oevkiyqV%A$LCC6s2)k?&G zB&x$O6lMVIBC#+v_gY)@&)i*1-Hf)H$ZP4reG6uRwceTIIS~&oasaUo4g^8&hLqSv zh>{_Q8vqPtY$6VpQu5%9si1?YZ}ym=21d4!yo#oj#*#w6ijGx8L=aHbMPrA+rt0RU z6j4~$CaRb7Ipw^p>+{oNTWgoToL{(vSX*LTCP8>Z?0 z{_WM_u$?tx0%oGB@$F?!(~$Nhj-FC-vp`TF zK)@S`;*;bOy+u`G5VJ@y3`3gdvxxpf`Vx&na5ohs5|h(%A?7k9QH@@hyS25(B?u9^ zSXTff0-j1~y{TuOr%*%K@*1EZiSzT6FUu$)<^Z62I!IaZj3dU|1kTC{=Am-pOmEl~MxwV{ADJhlmtM~7{ zEgv2qsg%3>dq5~dTDx=uQBT0@>8!1rc5_hQEDEM#;1(Q$E(l1aq>?!iB}PW@_=I?r z%Q@%GIfuxU0MWbZ#sxJ(X9QPeqD1Ul98gV!h!B%#uXP=VoqNBWPy1nV07i`KszX%j zIyjEw2!zb!fL%lYBL1FJGPf9?HI=2-T3gO(D5b3xfLd>o5np6x{GM6P>ER8 z9GuZI5jyY&IJL1b61W>Oxx1*>R$G_DZemVq-pws2yPIv*4SXoY+?c4g=I)BPuF}QV zWnPwxNQZdgnq5v$af+EQr)e06oY17~cKe$fo(^kk%W^>;m(%J0`@jFMdO5oRAx2G= znT7D1KmVuTkR@US8n3SuX}Z1#+md`Y@7~jH52>hIUzc#fM7ZkxS6_YePrv!}<%P&^ zj)zc2F`@%ftm-VZ!twc0i3_5dX++%2IF18IHv{OQAwKQ!RO;qdhE z%hl~If!X}>^6}@k*3;$GML&P~aC&}fUDT|Lw7z*aw$7qst-a(-7=BnVvz!uhia4y9 zmYl|tRMiZIoN}VeQVl2(C1Oq-)m&8>$`D#!c5+ow9fpwrI3;r_i8&{5sB4F?)w%)t z+S)K=>FNL~N&qD%cduP6=Aa1XzSMfP-yKT6J|2&UabwMWWP(h-yaQ`&@Q8ztn1gDVJdwZf>vt z^t<2w>-XOS!PS1+4LNg0;L}n+J$@!8b6Mw$JGpvq9nHWt?O+I+&>cjmrZFPC#EdAGZ&tuE_YI0aA< z0jDy?#cioI=e#ji5CK4C`(y)>sBil+@GgpoDWz$ckg$rFIRd+CPPq`8St9OYV;K^W zTBj6!35*6`>(K`1?V}u%s_5dvD|8hhz_xkY*;#P0BkaxrZ3n>kil~RPUfkwo3DY71 z=9H>P6;V;?EpCH}qQ!{;fbLyHwdCTa^;Ln9h#V>9%wYuqVGbjd0(h+r09D)CTIT%n z`1$$av57u?c{)G8xY@MZwbr_{`)_``yM43Y?}x+fdYQGgl!vau#9w{$>zs$)8aT!l zV%Qzj@$_=h)`??ooO+YGEFb^=Lmo;V3M3Ns$IlPz`2{(poG}lM6r|O08oz%3e%u}A zrN6sB4u!YU69C2hN7Z%vYQ{s#19on<=_to(KMVsC9j>phuMWddHkj$F!jC8s;d;5$ z)*@qtfR~r2r%#_E#IT$%+Qb0UQ0!IA8t1e9Za@!chKYzH-rT=Ey*yLS$8oqkKY{}j zcIngpP!fIl{PFbg@yo-<`E**>zVz0=`f8W=aPo^@86xyU;gs9|E^N!^X2(6C!%Fp zYwPD_PMHN%0bk}tJT$+!8%J|v1V*&2-RADDDnKv}`B(Qhz4b+G7zW}5od5XUAKK;l zm&d1Zx4S#;k$5QSyi`}XoKFtqNTGfpbayn10sumIc}f9hi1(g!?HwG=fskEeDQX+1 zLNroEDHZ07l)@UVW<PiU2sf$FXuapc1%eoSho0VKt`!J4|c`3sneUtHkW26LN+faGD z_BRmkKd@LE=fv63+!#5EB(BEcFF<_lnOZ#iN-BudRS6+wPMpNd+`6<7)0bhawE;Mg z1A-GFP@?3HqE>PyWOD<6-r6({=C1DDj1$*dMYN<$;9ZR4`S`k@t-YNt^W(#3>22EY zRokbZeh}%wdYbmv;-K0eA0AH6FA6cr=tMN_57TZmCLWIpK!`|A%#>1H=S1}7=N|wd zr5su1aT;?ON-i!+RKT&Mbal8=RbtL%P?7m`N=PY}p$y-C`@6e0_s^$`sNP=fnb85= zjr8@2r6#f2jd{DOZ?h{E2fDhxO)2Lg-`(CG4|{T-FLP_%RO{N186%#?BqA7uU;qLe zN_b~dDcQg;FX!IsmNjsfu4&!_#>-S%ffphV~A zPnY%5*7&DXj^OJ*TNbR zm6S&2{Wx47j{}ow8;3kj!_D>0p-j7R_vZTQ<1ZgN(3JW7^t9B)O-#Ex0GD}PncP&n zR0jh_16!px5eo^j=~hk#CkG}ThvEIr@!|C1WxP7<323*w`t!HH`)~jDea_>Xn;S;Y ziN#FN&+DoH_Byv#cR<_7$3$jkV3ZRN2{XesoK!b471fUHNC0LCKyjjoE{@~2{&&Z% zHc!Zmk;fehaYCdt?FqfD=jHq~4h4vjuuLO3sPq7MnW<^a~=jx)R~> zcoYxLXDTV3A3l-wwk`-roSC`x71Dr+|NUS7pXZN1tC>5w1E-uQIl%t<&ed|sW=h0J zv@VyQ{_=gvgn-Nmh^p#*zHrKh5Uzj#KB?JmH~sz(f0z#Yr{_~ddhfM2z!V(?P8}CoMQTvLMS_pDpl>!ckoCui+let9{-jPfV+{M7n(cM(KS;^TQ zQwnm7p*f?MloPS5x88TdU~Z~bIOm*l&aNH65Ui_l4(1d(Vv4Srd+%c4Dd%a-B5G#A zw`9aIr#H7(<2dZbA+`!R=V=%c(qX?Fkgu+;Z?5+L^8H`9jQeq@YYS5fA-|kX+ggDr zQ9?i_bhC9`Uv+qn1UV%j3P&N46XN@un|(_E{^0{pyW9Ia5&grr-#vf&`O^BE+nbVD z%-!MXG>1-dnNPzwrks$VYZFr>!W1QNL^Ci%U?jqn^0-UmIE-U1Bj-XXV;LwHN{Mnt zq7(wFoMM#Oq!F_tn!XOM)y$L=BNF97dq*>5$~mxsckRos$>=zY7WJiyy zp!!xy+kkUG=pNmyxEY(dt3^6tLrItsfpkH{oU=wI6|jrOxy;<7EYMnH6=f_#$prvS zbvF)7i2$Sv6NaQt)rL|sa|}rloXE#u-7IcpxV~?>u-LW z#+?DF8lsnB%Dd~Uckkc*(|2Ij*Ch`_YeL8--2uM&SF>CbF*c^TVg7 zhflSMnys}qjX+6k!N?uEx*H%;2p14hy19E{%$(NRtF*N?H51ivyKXTvKyg3Ka}*B6 zjH9>|KXpI{M<#J|_i-HK%3@|iPNk&CF(A^iR#iv9rmCit5+DTFgxq(-l!?Sd}6?6Co3pl&5i5a>>m5VYs`wPL%HN?+fd{{OxZ7psh89!|`xl=Sy7(v9{*m zyWOZ}L(Z~w`%pyPL4$3sL2e>8M}RVn|LgC6zkGT8;g^SD-0u&&obzvg_5Lq^``a{) zV@X3!i16jh696vD#T^aEfWurI3*QhF0}(KXW}H)sKtBec(_LzHbJ}u$8^1fCwT|0q zBS4bTxW_y?aa{4;fjITn0h}o@rA^V?TOtZmNlD4gmSt_N_PUUp_MVuLpthD$a`)Cc zMWCrmq9mqGEy5P_+Sa8GITN~d8*-+Umb$nDGNqDC#oWk!2!B#2E@F2~=w^;+03sFv z7$iq@ca3BWA_vWh2pwG941qSuR~TTURY)W@tW?5|W1CbWe$pur^iG%*@0kv#A4OZITkl@qgIwfs&{^KRuf&NC(5? z_3f9Z$6o8pTxwv_~T-J4IOMUy>Z=aq{VB0)k zKA+op{`}!56RCA|PrXU6)eW>&R|L)$i0a@Mx$URn+u!{z4^y{Jj0(D&3TcEY!=;!tu*sn&I#7ZE+5PKN2vzx(cp_S3(ApNHMm-EB%4Ol3KJdHAKSm&eba z9-p4(wFLwgX^W~?Q7{DX%oGJncgu;HDaKqxC}NSIiI+G>21t8&i(v0htS79?8Nw$>(UIS(mk zHBZce4Avh9`!?@cY-oAVPfBtX(^}qe=|NHs*v^ySC$$$LzpZ@Lt|JUX? zm27}RDP23H#5priU8=Sgf%Yh(CJ2wHXl-E5Oz42b07#ev2j&nAu~<5HHw)LLqq@0? zsu&aZCdiC2Y3fZ{i=&qI)p`vsl!2*QN`;6^qB7)6*m@+Bd1R=yoJ|s?vVgegV#K18jU6#e&x+ntG)~aY1 zTYK*!Q8bX~v@>O*#6?V|k_b`ETkD8s;EZhQZqT|S;%*pIVs~F^2RF{C_4RZ*o#%5G zakumNBE55BME>&dnb4#y(j*OoIdCF3t4nW}%iCZ7x~rW&eeA8f`~G-*dVE0ipa1$- zGXMMc-!CsO-s;289}gzNN gbCp8G%mna105|t2#^(&bZ2$lO07*qoM6N<$g6sq=ga7~l From a610194aa1c52a889ec5e9c6084bb2ffe4968965 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Fri, 28 Jan 2022 15:31:05 -0800 Subject: [PATCH 013/366] Replace r.Client.Update with patch helper Signed-off-by: F. Gold --- .../pod_volume_backup_controller.go | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index d050bf7d15..609a7d7123 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" corev1listers "k8s.io/client-go/listers/core/v1" + "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -90,6 +91,21 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ log.Info("PodVolumeBackup starting") + // Initialize the patch helper. + patchHelper, err := patch.NewHelper(&pvb, r.Client) + if err != nil { + log.WithError(err).Error("getting patch helper to update this resource") + return ctrl.Result{}, errors.WithStack(err) + } + + defer func() { + // Always attempt to patch the PVB object and status after each reconciliation. + if err := patchHelper.Patch(ctx, &pvb); err != nil { + log.WithError(err).Error("updating PodVolumeBackup resource") + return + } + }() + // Only process items for this node. if pvb.Spec.Node != r.NodeName { return ctrl.Result{}, nil @@ -161,11 +177,8 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ r.Metrics.ObserveResticOpLatency(r.NodeName, req.Name, resticCmd.Command, backupName, latencySeconds) r.Metrics.RegisterResticOpLatencyGauge(r.NodeName, req.Name, resticCmd.Command, backupName, latencySeconds) r.Metrics.RegisterPodVolumeBackupDequeue(r.NodeName) - log.Info("PodVolumeBackup completed") - if err := r.Client.Update(ctx, &pvb); err != nil { - log.WithError(err).Error("updating PodVolumeBackup resource") - } + log.Info("PodVolumeBackup completed") return ctrl.Result{}, nil } @@ -256,10 +269,6 @@ func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pv pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed pvb.Status.Message = msg pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - - if err := r.Client.Update(ctx, pvb); err != nil { - return ctrl.Result{}, errors.Wrap(err, "updating PodVolumeBackup resource with failed status") - } return ctrl.Result{}, errors.Wrap(err, msg) } From ec23f3b767dbd997b89d463b840426a25156ac9a Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Fri, 28 Jan 2022 18:08:49 -0800 Subject: [PATCH 014/366] Run go mod tidy Signed-off-by: F. Gold --- go.mod | 2 -- go.sum | 1 - 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index 60ee7f193d..972a2a9ab4 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,6 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gobuffalo/flect v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -108,7 +107,6 @@ require ( golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - golang.org/x/tools v0.1.5 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect diff --git a/go.sum b/go.sum index a7ab92374c..525808f44f 100644 --- a/go.sum +++ b/go.sum @@ -256,7 +256,6 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= From 5fa7d08fa16425d8c2838a6e843cb87b7f9b7ef2 Mon Sep 17 00:00:00 2001 From: Dominic Brekau <35068626+dbrekau@users.noreply.github.com> Date: Sat, 29 Jan 2022 08:47:55 +0100 Subject: [PATCH 015/366] Use OrderedResources in schedules (#4550) * Use OrderedResources in schedules Make ParseOrderedResources public for use in schedules Add changelog Signed-off-by: Dominic * Rename function in comment section Signed-off-by: Dominic --- changelogs/unreleased/4550-dbrekau | 1 + pkg/cmd/cli/backup/create.go | 6 +++--- pkg/cmd/cli/backup/create_test.go | 8 ++++---- pkg/cmd/cli/schedule/create.go | 10 ++++++++++ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/4550-dbrekau diff --git a/changelogs/unreleased/4550-dbrekau b/changelogs/unreleased/4550-dbrekau new file mode 100644 index 0000000000..dea16b5671 --- /dev/null +++ b/changelogs/unreleased/4550-dbrekau @@ -0,0 +1 @@ +Fix: OrderedResources in Schedules diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index e0eb06b5c2..f2d165df5e 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -291,11 +291,11 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return nil } -// parseOrderedResources converts to map of Kinds to an ordered list of specific resources of that Kind. +// ParseOrderedResources converts to map of Kinds to an ordered list of specific resources of that Kind. // Resource names in the list are in format 'namespace/resourcename' and separated by commas. // Key-value pairs in the mapping are separated by semi-colon. // Ex: 'pods=ns1/pod1,ns1/pod2;persistentvolumeclaims=ns1/pvc4,ns1/pvc8'. -func parseOrderedResources(orderMapStr string) (map[string]string, error) { +func ParseOrderedResources(orderMapStr string) (map[string]string, error) { entries := strings.Split(orderMapStr, ";") if len(entries) == 0 { return nil, fmt.Errorf("Invalid OrderedResources '%s'.", orderMapStr) @@ -337,7 +337,7 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro StorageLocation(o.StorageLocation). VolumeSnapshotLocations(o.SnapshotLocations...) if len(o.OrderedResources) > 0 { - orders, err := parseOrderedResources(o.OrderedResources) + orders, err := ParseOrderedResources(o.OrderedResources) if err != nil { return nil, err } diff --git a/pkg/cmd/cli/backup/create_test.go b/pkg/cmd/cli/backup/create_test.go index ec62369945..401e93e473 100644 --- a/pkg/cmd/cli/backup/create_test.go +++ b/pkg/cmd/cli/backup/create_test.go @@ -34,7 +34,7 @@ func TestCreateOptions_BuildBackup(t *testing.T) { o := NewCreateOptions() o.Labels.Set("velero.io/test=true") o.OrderedResources = "pods=p1,p2;persistentvolumeclaims=pvc1,pvc2" - orders, err := parseOrderedResources(o.OrderedResources) + orders, err := ParseOrderedResources(o.OrderedResources) assert.NoError(t, err) backup, err := o.BuildBackup(testNamespace) @@ -100,10 +100,10 @@ func TestCreateOptions_BuildBackupFromSchedule(t *testing.T) { } func TestCreateOptions_OrderedResources(t *testing.T) { - orderedResources, err := parseOrderedResources("pods= ns1/p1; ns1/p2; persistentvolumeclaims=ns2/pvc1, ns2/pvc2") + orderedResources, err := ParseOrderedResources("pods= ns1/p1; ns1/p2; persistentvolumeclaims=ns2/pvc1, ns2/pvc2") assert.NotNil(t, err) - orderedResources, err = parseOrderedResources("pods= ns1/p1,ns1/p2 ; persistentvolumeclaims=ns2/pvc1,ns2/pvc2") + orderedResources, err = ParseOrderedResources("pods= ns1/p1,ns1/p2 ; persistentvolumeclaims=ns2/pvc1,ns2/pvc2") assert.NoError(t, err) expectedResources := map[string]string{ @@ -112,7 +112,7 @@ func TestCreateOptions_OrderedResources(t *testing.T) { } assert.Equal(t, orderedResources, expectedResources) - orderedResources, err = parseOrderedResources("pods= ns1/p1,ns1/p2 ; persistentvolumes=pv1,pv2") + orderedResources, err = ParseOrderedResources("pods= ns1/p1,ns1/p2 ; persistentvolumes=pv1,pv2") assert.NoError(t, err) expectedMixedResources := map[string]string{ diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index 7a4971aa7a..5e15ac6bc5 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -111,11 +111,20 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error { } func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { + var orders map[string]string + veleroClient, err := f.Client() if err != nil { return err } + if len(o.BackupOptions.OrderedResources) > 0 { + orders, err = backup.ParseOrderedResources(o.BackupOptions.OrderedResources) + if err != nil { + return err + } + } + schedule := &api.Schedule{ ObjectMeta: metav1.ObjectMeta{ Namespace: f.Namespace(), @@ -135,6 +144,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { StorageLocation: o.BackupOptions.StorageLocation, VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, DefaultVolumesToRestic: o.BackupOptions.DefaultVolumesToRestic.Value, + OrderedResources: orders, }, Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, From 33219e6c4c8f2997ed04fe9dd987fbb19f5851d0 Mon Sep 17 00:00:00 2001 From: danfengl Date: Sat, 29 Jan 2022 09:14:36 +0000 Subject: [PATCH 016/366] Add 1.8 plugins map in e2e test Signed-off-by: danfengl --- test/e2e/util/velero/velero_utils.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index c1e6046cd1..3f8cdc162d 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -70,10 +70,16 @@ var pluginsMatrix = map[string]map[string][]string{ "vsphere": {"velero/velero-plugin-for-aws:v1.3.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.0"}, "gcp": {"velero/velero-plugin-for-gcp:v1.3.0"}, }, + "v1.8": { + "aws": {"velero/velero-plugin-for-aws:v1.4.0-rc1"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0-rc.1"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.4.0-rc1", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.4.0-rc1"}, + }, "main": { "aws": {"velero/velero-plugin-for-aws:main"}, "azure": {"velero/velero-plugin-for-microsoft-azure:main"}, - "vsphere": {"velero/velero-plugin-for-aws:main", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.0"}, + "vsphere": {"velero/velero-plugin-for-aws:main", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, "gcp": {"velero/velero-plugin-for-gcp:main"}, }, } From f757540c6db79c50f617bbca05b9be35412f562c Mon Sep 17 00:00:00 2001 From: danfengl Date: Sat, 29 Jan 2022 09:32:54 +0000 Subject: [PATCH 017/366] Fix E2E gcp plugin tag name Signed-off-by: danfengl --- test/e2e/util/velero/velero_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 3f8cdc162d..624a90caaa 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -74,7 +74,7 @@ var pluginsMatrix = map[string]map[string][]string{ "aws": {"velero/velero-plugin-for-aws:v1.4.0-rc1"}, "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0-rc.1"}, "vsphere": {"velero/velero-plugin-for-aws:v1.4.0-rc1", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.4.0-rc1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.4.0-rc.1"}, }, "main": { "aws": {"velero/velero-plugin-for-aws:main"}, From 7c4bd099d998ee55b4a26a15949ff8073ad52134 Mon Sep 17 00:00:00 2001 From: danfengl Date: Sat, 29 Jan 2022 10:11:17 +0000 Subject: [PATCH 018/366] Change 1.8 plugins version to release version Signed-off-by: danfengl --- test/e2e/util/velero/velero_utils.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 624a90caaa..2dff360f54 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -71,10 +71,10 @@ var pluginsMatrix = map[string]map[string][]string{ "gcp": {"velero/velero-plugin-for-gcp:v1.3.0"}, }, "v1.8": { - "aws": {"velero/velero-plugin-for-aws:v1.4.0-rc1"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0-rc.1"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.4.0-rc1", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.4.0-rc.1"}, + "aws": {"velero/velero-plugin-for-aws:v1.4.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.4.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.4.0"}, }, "main": { "aws": {"velero/velero-plugin-for-aws:main"}, From f0a29276cc5b3d919f3e7427ed400316468c1d3f Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Sun, 30 Jan 2022 14:26:55 +0800 Subject: [PATCH 019/366] Undeprecate the volumesnapshot plugin in the doc Since Itemsnapshotter plugin is still WIP, this commit removes the reference and the deprecation of volumeSnapshotter plugin from the doc to avoid confusion. We'll update the doc when it's ready and we have a reference implementation. Signed-off-by: Daniel Jiang --- site/content/docs/main/custom-plugins.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/site/content/docs/main/custom-plugins.md b/site/content/docs/main/custom-plugins.md index 34497c3b8c..c0937c20c0 100644 --- a/site/content/docs/main/custom-plugins.md +++ b/site/content/docs/main/custom-plugins.md @@ -54,14 +54,10 @@ You will need to give your plugin(s) the full name when registering them by call Velero supports the following kinds of plugins: - **Object Store** - persists and retrieves backups, backup logs and restore logs -- **Item Snapshotter** - creates snapshots for Kubernetes objects during backup and restores the object from snapshots during restore. ItemSnapshotters - are typically used with the [Astrolabe](https://github.com/vmware-tanzu/astrolabe) framework. +- **Volume Snapshotter** - creates volume snapshots (during backup) and restores volumes from snapshots (during restore) - **Backup Item Action** - executes arbitrary logic for individual items prior to storing them in a backup file - **Restore Item Action** - executes arbitrary logic for individual items prior to restoring them into a cluster - **Delete Item Action** - executes arbitrary logic based on individual items within a backup prior to deleting the backup -## Deprecated plugin kinds -- **Volume Snapshotter** - creates volume snapshots (during backup) and restores volumes from snapshots (during restore) VolumeSnapshotters -are deprecated and will be replaced with ItemSnapshotter/Astrolabe plugins. ## Plugin Logging From 131c6de407ffd97c802f89df0dd182b44c62375e Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Wed, 2 Feb 2022 10:39:13 -0800 Subject: [PATCH 020/366] Check for nil before logging DefaultVolumestToRestic value Signed-off-by: F. Gold --- pkg/backup/backup.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index ef03e78f72..15fba183de 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -200,7 +200,11 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, backupRequest.ResourceIncludesExcludes = collections.GetResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources) log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) - log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic) + if backupRequest.Backup.Spec.DefaultVolumesToRestic != nil { + log.Infof("Backing up all pod volumes using Restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic) + } else { + log.Infof("DefaultVolumesToRestic for backing up all pod volumes using Restic is %v", backupRequest.Backup.Spec.DefaultVolumesToRestic) + } var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) From 95c43d5b657db49d39d401e393f1be8b2957f9db Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Wed, 2 Feb 2022 17:02:22 -0800 Subject: [PATCH 021/366] Minor change to trigger GitHub actions Signed-off-by: F. Gold --- pkg/backup/backup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 15fba183de..c201fc5e92 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright the Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 9b9aa3f30832c82c1ebca1092d0c1001de822ba3 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Mon, 7 Feb 2022 11:41:15 -0800 Subject: [PATCH 022/366] Add changelog for PR 4436 Signed-off-by: F. Gold --- changelogs/unreleased/4436-fgold | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4436-fgold diff --git a/changelogs/unreleased/4436-fgold b/changelogs/unreleased/4436-fgold new file mode 100644 index 0000000000..ba9ac4c071 --- /dev/null +++ b/changelogs/unreleased/4436-fgold @@ -0,0 +1 @@ +Convert PodVolumebackup controller to the Kubebuilder framework \ No newline at end of file From a251fffc699165cfad130d8c342ecc3f50ac10f7 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 8 Feb 2022 10:55:19 +0800 Subject: [PATCH 023/366] Make sure the tag-release script fail on any error Signed-off-by: Daniel Jiang --- hack/release-tools/tag-release.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hack/release-tools/tag-release.sh b/hack/release-tools/tag-release.sh index 964f558e8c..a8591a899f 100755 --- a/hack/release-tools/tag-release.sh +++ b/hack/release-tools/tag-release.sh @@ -38,6 +38,9 @@ # This script is meant to be a combination of documentation and executable. # If you have questions at any point, please stop and ask! +# Fail on any error. +set -eo pipefail + # Directory in which the script itself resides, so we can use it for calling programs that are in the same directory. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" From 5844fca5afe61ebad962ab8dd8ec911a1727a46a Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 8 Feb 2022 20:32:52 +0800 Subject: [PATCH 024/366] Add pushing image to GCR in github workflow Push to GCR in github workflow to faciliate some environments that have rate limitation to docker hub, e.g. vSphere. Signed-off-by: Xun Jiang --- .github/workflows/push.yml | 19 +++++++++++++++++++ changelogs/unreleased/4623-jxun | 1 + 2 files changed, 20 insertions(+) create mode 100644 changelogs/unreleased/4623-jxun diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4d232ef9d2..d2dc0a761c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -46,3 +46,22 @@ jobs: run: | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} ./hack/docker-push.sh + + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v0.6.0' + with: + workload_identity_provider: 'projects/298797809281/locations/global/workloadIdentityPools/velero-pool/providers/velero-provider' + service_account: 'gcraccount@velero-gcp.iam.gserviceaccount.com' + + - name: Set up gCloud SDK + uses: 'google-github-actions/setup-gcloud@v0.5.0' + + - name: Configure docker for GCR + run: | + gcloud auth configure-docker --quiet + + # Push image to GCR to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. + - name: Publish container image to GCR + run: | + REGISTRY=gcr.io/velero-gcp ./hack/docker-push.sh diff --git a/changelogs/unreleased/4623-jxun b/changelogs/unreleased/4623-jxun new file mode 100644 index 0000000000..56e0fe4e3d --- /dev/null +++ b/changelogs/unreleased/4623-jxun @@ -0,0 +1 @@ +Add pushing image to GCR in github workflow to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. From 491942cf2de685a6ba85a61b38b3c9338b7b1113 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Wed, 9 Feb 2022 16:58:07 -0800 Subject: [PATCH 025/366] Simplify by assuming nil is false and use boolptr util function Signed-off-by: F. Gold --- pkg/backup/backup.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index c201fc5e92..697be85009 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -47,6 +47,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -200,11 +201,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, backupRequest.ResourceIncludesExcludes = collections.GetResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources) log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) - if backupRequest.Backup.Spec.DefaultVolumesToRestic != nil { - log.Infof("Backing up all pod volumes using Restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic) - } else { - log.Infof("DefaultVolumesToRestic for backing up all pod volumes using Restic is %v", backupRequest.Backup.Spec.DefaultVolumesToRestic) - } + log.Infof("Backing up all pod volumes using Restic: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToRestic)) var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) From 01842a1552e2398ecfca906b9151d85118306602 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 10 Feb 2022 15:40:31 +0800 Subject: [PATCH 026/366] Add GCR login actions use google-github-actions/auth to login GCP. Login gcr.io with generated access token. Signed-off-by: Xun Jiang --- .github/workflows/push.yml | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d2dc0a761c..5202a3b4e0 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,6 +6,22 @@ on: tags: - '*' +# id-token is added to enable OIDC token generation. All the others are copied from "Set up job" GITHUB_TOKEN Permissions +permissions: + actions: write + checks: write + contents: write + deployments: write + discussions: write + issues: write + packages: write + pages: write + pull-requests: write + repository-projects: write + security-events: write + statuses: write + id-token: write + jobs: build: @@ -47,21 +63,27 @@ jobs: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} ./hack/docker-push.sh + # actions/checkout MUST come before auth + - uses: 'actions/checkout@v2' + + # auth to GCP with OIDC token from GCP Workload Identity Provider. Output auth result to access token. - id: 'auth' name: 'Authenticate to Google Cloud' uses: 'google-github-actions/auth@v0.6.0' with: workload_identity_provider: 'projects/298797809281/locations/global/workloadIdentityPools/velero-pool/providers/velero-provider' service_account: 'gcraccount@velero-gcp.iam.gserviceaccount.com' + token_format: 'access_token' - - name: Set up gCloud SDK - uses: 'google-github-actions/setup-gcloud@v0.5.0' - - - name: Configure docker for GCR - run: | - gcloud auth configure-docker --quiet + # Use access_token generated above to login gcr.io + - uses: 'docker/login-action@v1' + with: + registry: 'gcr.io' # or REGION.docker.pkg.dev + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.access_token }}' # Push image to GCR to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. - name: Publish container image to GCR + if: github.repository == 'vmware-tanzu/velero' run: | REGISTRY=gcr.io/velero-gcp ./hack/docker-push.sh From 72d53fb11e716afaa2adc5592d4ea4434f00935b Mon Sep 17 00:00:00 2001 From: Ming Date: Fri, 11 Feb 2022 17:36:00 +0800 Subject: [PATCH 027/366] Add Ming Qiu's profile information into Velero Website's contributor list Signed-off-by: Ming --- site/content/contributors/02-ming-qiu.md | 7 +++++++ site/static/img/contributors/ming-qiu.png | Bin 0 -> 284504 bytes 2 files changed, 7 insertions(+) create mode 100644 site/content/contributors/02-ming-qiu.md create mode 100644 site/static/img/contributors/ming-qiu.png diff --git a/site/content/contributors/02-ming-qiu.md b/site/content/contributors/02-ming-qiu.md new file mode 100644 index 0000000000..cd6e43be62 --- /dev/null +++ b/site/content/contributors/02-ming-qiu.md @@ -0,0 +1,7 @@ +--- +first_name: Ming +last_name: Qiu +image: /img/contributors/ming-qiu.png +github_handle: qiuming-best +--- +Engineer \ No newline at end of file diff --git a/site/static/img/contributors/ming-qiu.png b/site/static/img/contributors/ming-qiu.png new file mode 100644 index 0000000000000000000000000000000000000000..6cae1f922acbbce5ab4f1cbb8289cf73a30cf03d GIT binary patch literal 284504 zcmYJZV{j!-)HHr$dt+>D+sSThZ*1GPooqI?ZQHhO-uTAG`+J`EkE!Z9U8j1!%!jF- zGaacYFM$M)2M+)MkfbCq>-{{<}cf1SK#br1jmzhfyPq9`RILagXyZ)Ry@3IIq( zCa1!vCXQnT0KXUU!O%&GDIO>#sREMbL_l(?ztJVBNhl$qIvd)+D}y3nQ!sTfl^k4Y z8nA|fFo(l@e_=r#w`Vv+lNkXKSBKWjVtDq?y%8$3v3A#?XP1 zWS`w>w{VfNTWkObbYg(D!O-zI`Q^=xLBQCTQ8z)cS>iTxJ{%SQbohUWNAq%EYf)*uZgIMqN#$Epf zk5w8HCf67tWGOWM+f#Uo{oZ6Ja&Zdy21&DG76z^0Trn13c#}97nPV0>#buRIyq*xH zQLb$N#O_?o=6HgKF=`=%N0!s+ z5K=ZS&733L(%RM

OCjn-|@lj0!|C#LZDSxf4xr{5+OTlRaOY1Tm)!8}Js4o4wBn zD1!p9yWbowY|#*U9iTu&1i+sPL!1I24B+V#wg>|0ZbVs;Aq|P~$OA$HU_yuyB>U0p z2&%wB^KfjSwSQr*AzAfZir%jp@sxgqarN? zxR8*Ifj5L|{N+1BfD%bi1W!UN{$(VI%n-ivYgiFi73wPRJTEu@a1QqreF2H1gBAKGN%{D4v< z{{c}P-XL19xKqZgN=l1KAyzGlnNO?8Q3kDwUxDF@upEsi?)%$#o}C@THquQDCeL_| z|H0@%+6Ary-WRYPBN*NjdMy&4*K0_P!w>^C0yWq_WW2yE!yK9hKP4!Oal(IsYzDX| ziXLXIqtu|X0dRt4#_5JJ^j{Bg8g15ZulZe4BZn{cY3^+~7;-Z*!l*}V#_@&l#db#G z_xTPb-gJ0qcl{JZV;ZR)R^LN8P}sLW$UPi|oq!|@LLiChqdG$4M0-NtfL(=y2w^FJ zpAR_|kD)BVSVg6aL>jsuk{D_wN0-Vyl8qvcl&q9WE+Q+6E)razYM@=ux|EYAogBv> ze;>a(dOvFY0Y98}ASG3Fq{yj!PHj%3K>ah>Li&$10Y%ROZLw9cS#hRxZ5d<*TC$w3 ze*S_)biHO6wpiUZYHHD9&Y z3aE`;>CBPB>cXqy>{6t;dKV1d@Em!jlAk5+^Aby{%a_YW%PAFVmD#En zrg=Cch{$A#^^uNAj;XhvDNpoI^!-&xjd*6;S&zSbTRKWRgu5lXl|JL%%#ljMYQ@9E z%f+KdjOCh2+RZ{v(5H1XfE?lGi{_m(C)11>zAjG?3>uR&hf%<7`dvrd<;aHjd0;&TX0?Re3>~};@I@; zw~Y8L3hV}0#<|M5JWZro9+`D*w2Zwr5v@m9Cz&@b-3AFb-I=j7)HC&40~iPxM6vi7 zI)DZz+_Px2<>`yYQY|LhCt54oPns-MLDef-%^KYq*4$i+Bo?*wsfkrR(HML z%pUwz+j#9-zD0rcPl;ze6akbPiP%vU1qg*6px`XfhRu!l9rZmqwK;XEa{MB=HToGI zeZA+@%`KE^&7gdybH%iinNi{=mWZTfWumKKl~un<5L#~D6aei#GE3snQl zk1dKE1z!R0g=a%Vk7S1sC;~3h62cG90goBp5)#NPzm`%rx=*nq2x52rG(`!El$JAM%hO9GMqcNt)Xq_rTv!fpqAW%G6G!- z!!fcx@*Q0Q_&&4C>rXsRQqFvK4W0-kgRP@g*3Rs_KQ6W;wk0BYnk)|uK=;H z`&)QSUCbx$VY*{_tmaGAQPrW5nq%0DPK*50!lhkgXp!PV=bGv5$}rgi@tGhlxi+!& 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%0AiadQKPEjLXX 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 A{{R30 literal 0 HcmV?d00001 From 6a61a418caba9a4eaf2aeddceec5bd5df4bc0d9c Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 14 Feb 2022 10:12:45 +0800 Subject: [PATCH 028/366] Fix E2E backup namespaces test Signed-off-by: Ming --- changelogs/unreleased/4634-qiuming-best | 1 + test/e2e/basic/resources-check/namespaces.go | 26 +++++++++++--------- test/e2e/test/test.go | 12 +++++++-- 3 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/4634-qiuming-best diff --git a/changelogs/unreleased/4634-qiuming-best b/changelogs/unreleased/4634-qiuming-best new file mode 100644 index 0000000000..b3569ea03b --- /dev/null +++ b/changelogs/unreleased/4634-qiuming-best @@ -0,0 +1 @@ +Fix E2E backup namespaces test diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index 73d1d4543b..366e3adfb2 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -48,17 +48,6 @@ func (m *MultiNSBackup) Init() error { m.Client = TestClientInstance m.NSExcluded = &[]string{} - // Currently it's hard to build a large list of namespaces to include and wildcards do not work so instead - // we will exclude all of the namespaces that existed prior to the test from the backup - namespaces, err := m.Client.ClientGo.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{}) - if err != nil { - return errors.Wrap(err, "Could not retrieve namespaces") - } - - for _, excludeNamespace := range namespaces.Items { - *m.NSExcluded = append(*m.NSExcluded, excludeNamespace.Name) - } - if m.IsScalTest { m.NamespacesTotal = 2500 m.TimeoutDuration = time.Hour * 2 @@ -74,6 +63,20 @@ func (m *MultiNSBackup) Init() error { FailedMSG: "Failed to successfully backup and restore multiple namespaces", } } + return nil +} + +func (m *MultiNSBackup) StartRun() error { + // Currently it's hard to build a large list of namespaces to include and wildcards do not work so instead + // we will exclude all of the namespaces that existed prior to the test from the backup + namespaces, err := m.Client.ClientGo.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "Could not retrieve namespaces") + } + + for _, excludeNamespace := range namespaces.Items { + *m.NSExcluded = append(*m.NSExcluded, excludeNamespace.Name) + } m.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", m.BackupName, @@ -85,7 +88,6 @@ func (m *MultiNSBackup) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", m.RestoreName, "--from-backup", m.BackupName, "--wait", } - return nil } diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 91a957ad4c..5648c06def 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -39,6 +39,7 @@ depends on your test patterns. */ type VeleroBackupRestoreTest interface { Init() error + StartRun() error CreateResources() error Backup() error Destroy() error @@ -137,6 +138,10 @@ func (t *TestCase) CreateResources() error { return nil } +func (t *TestCase) StartRun() error { + return nil +} + func (t *TestCase) Backup() error { if err := VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.BackupArgs); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "") @@ -180,8 +185,11 @@ func RunTestCase(test VeleroBackupRestoreTest) error { } defer test.Clean() - - err := test.CreateResources() + err := test.StartRun() + if err != nil { + return err + } + err = test.CreateResources() if err != nil { return err } From 9419aa91a7f2c70c7fb2a1fc60b41c146d1b57b7 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 14 Feb 2022 19:58:45 +0800 Subject: [PATCH 029/366] Update e2e test image to gcr.io By now, only busybox:latest is used by e2e. It is already upload to gcr.io/velero-gcp/busybox:latest Change the image to gcr.io to avoid pulling rate limitation from docker hub. Signed-off-by: Xun Jiang --- changelogs/unreleased/4639-jxun | 1 + test/e2e/util/k8s/deployment.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4639-jxun diff --git a/changelogs/unreleased/4639-jxun b/changelogs/unreleased/4639-jxun new file mode 100644 index 0000000000..a027b1b00b --- /dev/null +++ b/changelogs/unreleased/4639-jxun @@ -0,0 +1 @@ +Update image used by E2E test to gcr.io diff --git a/test/e2e/util/k8s/deployment.go b/test/e2e/util/k8s/deployment.go index 6763c3e9be..bfa1252a28 100644 --- a/test/e2e/util/k8s/deployment.go +++ b/test/e2e/util/k8s/deployment.go @@ -62,7 +62,7 @@ func NewDeployment(name, ns string, replicas int32, labels map[string]string) *a Containers: []v1.Container{ { Name: name, - Image: "busybox:latest", + Image: "gcr.io/velero-gcp/busybox:latest", Command: []string{"sleep", "1000000"}, }, }, From 70d12ea16e5856acc40b5bb7defd7605146693cb Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 15 Feb 2022 19:13:26 +0800 Subject: [PATCH 030/366] Refine the release note of v1.8 Signed-off-by: Daniel Jiang --- changelogs/CHANGELOG-1.8.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/changelogs/CHANGELOG-1.8.md b/changelogs/CHANGELOG-1.8.md index 94e94a6878..ed41f1d218 100644 --- a/changelogs/CHANGELOG-1.8.md +++ b/changelogs/CHANGELOG-1.8.md @@ -15,7 +15,7 @@ https://velero.io/docs/v1.8/upgrade-to-1.8/ ### Highlights -#### The plugins to support handling volumes created by the AWS CSI driver +#### The plugins to support handling volumes created by the CSI drivers of cloud providers The new versions of plugins for AWS, Azure and GCP will be released with the support of snapshotting and restoring the persistent volumes provisioned by CSI driver via the APIs of the cloud providers. With this enhancement, users can backup and restore the persistent volumes on these could providers without using the CSI plugin, which will remain beta and the feature flag `EnableCSI` will be disabled by default. For the version of the plugins and the CSI drivers they support respectively please see the table: @@ -26,6 +26,13 @@ For the version of the plugins and the CSI drivers they support respectively ple | velero-plugin-for-microsoft-azure | v1.4.0 | disk.csi.azure.com | | velero-plugin-for-gcp | v1.4.0 | pd.csi.storage.gke.io | +#### IPv6 dual stack support +By successfully running the E2E test on IPv6 dual stack environment, we've verfied the functionality of velero on IPv6 dual stack. +#### Refactor the controllers using Kubebuilder v3 +Continuing the code modernization work. Some controllers have been reworked using Kubebuilder v3. We'll continue the work to using Kubebuilder to rewrite the controllers in future releases. +#### Enhancements to E2E test cases +More test cases have been added to the E2E test suite to improve the release health. + #### Break change Starting v1.8 velero will only support v1 CRD, therefore, it will only run on Kubernetes v1.16+ From e3685786faf7e42627de0b747b7fe21a1a3eabb1 Mon Sep 17 00:00:00 2001 From: "F. Gold" Date: Tue, 15 Feb 2022 10:53:46 -0800 Subject: [PATCH 031/366] Add delete BSL cleanup highlight and fixed grammar in heading Signed-off-by: F. Gold --- changelogs/CHANGELOG-1.8.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG-1.8.md b/changelogs/CHANGELOG-1.8.md index 94e94a6878..9050fe6bf6 100644 --- a/changelogs/CHANGELOG-1.8.md +++ b/changelogs/CHANGELOG-1.8.md @@ -26,7 +26,11 @@ For the version of the plugins and the CSI drivers they support respectively ple | velero-plugin-for-microsoft-azure | v1.4.0 | disk.csi.azure.com | | velero-plugin-for-gcp | v1.4.0 | pd.csi.storage.gke.io | -#### Break change +#### Deleting BSLs also cleans up related resources + +When a Backup Storage Location (BSL) is deleted, backup and Restic repository resources will also be deleted. + +#### Breaking changes Starting v1.8 velero will only support v1 CRD, therefore, it will only run on Kubernetes v1.16+ ### All changes @@ -89,4 +93,3 @@ Also added DownloadTargetKindBackupItemSnapshots for retrieving the signed URL t Part of the Upload Progress enhancement (#3533) (#4077, @dsmithuchida) * Add upgrade test in E2E test (#4058, @danfengliu) * Handle namespace mapping for PVs without snapshots on restore (#3708, @sseago) - From 768a30618f37a91c8c98283e4c30e051565540c8 Mon Sep 17 00:00:00 2001 From: bynare <1852531+bynare@users.noreply.github.com> Date: Wed, 16 Feb 2022 10:34:58 +1300 Subject: [PATCH 032/366] restic backupper: ignore volumes of non-running pods (#4584) --- changelogs/unreleased/4584-bynare | 1 + pkg/restic/backupper.go | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/4584-bynare diff --git a/changelogs/unreleased/4584-bynare b/changelogs/unreleased/4584-bynare new file mode 100644 index 0000000000..008c138c44 --- /dev/null +++ b/changelogs/unreleased/4584-bynare @@ -0,0 +1 @@ +Skip volumes of non-running pods when backing up diff --git a/pkg/restic/backupper.go b/pkg/restic/backupper.go index 4e411c62dc..fd366a36bc 100644 --- a/pkg/restic/backupper.go +++ b/pkg/restic/backupper.go @@ -153,6 +153,11 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. } } + // ignore non-running pods + if pod.Status.Phase != corev1api.PodRunning { + log.Warnf("Skipping volume %s in pod %s/%s - pod not running", volumeName, pod.Namespace, pod.Name) + continue + } // hostPath volumes are not supported because they're not mounted into /var/lib/kubelet/pods, so our // daemonset pod has no way to access their data. isHostPath, err := isHostPathVolume(&volume, pvc, b.pvClient.PersistentVolumes()) @@ -165,11 +170,6 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. continue } - // emptyDir volumes on finished pods are not supported because the volume is already gone and would result in an error - if (pod.Status.Phase == corev1api.PodSucceeded || pod.Status.Phase == corev1api.PodFailed) && volume.EmptyDir != nil { - continue - } - // volumes that are not mounted by any container should not be backed up, because // its directory is not created if !mountedPodVolumes.Has(volumeName) { From b7b339b9c93416cc094141bf408b0f6cf647e71d Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Wed, 16 Feb 2022 05:37:21 +0800 Subject: [PATCH 033/366] Add more exempt labels for stalebot (#4641) --- .github/stale.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/stale.yml b/.github/stale.yml index 4c2adae9b0..ce96631aaa 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -14,9 +14,15 @@ exemptLabels: - Area/Design - Area/Documentation - Area/Plugins + - Bug - Enhancement/User + - kind/requirement + - kind/refactor - kind/tech-debt + - limitation - Needs investigation + - Needs triage + - Needs Product - P0 - Hair on fire - P1 - Important - P2 - Long-term important From 9fd241a1a0fbe19cbd48fe058901d9ba15726057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Wed, 16 Feb 2022 08:27:44 +0800 Subject: [PATCH 034/366] Update release note to reflect the fix for #1980 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update release note to reflect the fix for #1980 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/CHANGELOG-1.8.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelogs/CHANGELOG-1.8.md b/changelogs/CHANGELOG-1.8.md index ed41f1d218..178d5c911d 100644 --- a/changelogs/CHANGELOG-1.8.md +++ b/changelogs/CHANGELOG-1.8.md @@ -32,6 +32,8 @@ By successfully running the E2E test on IPv6 dual stack environment, we've verfi Continuing the code modernization work. Some controllers have been reworked using Kubebuilder v3. We'll continue the work to using Kubebuilder to rewrite the controllers in future releases. #### Enhancements to E2E test cases More test cases have been added to the E2E test suite to improve the release health. +#### Respect the cron setting of schedule +The creation time is taken into account to calculate the next run for scheduled backup. #### Break change Starting v1.8 velero will only support v1 CRD, therefore, it will only run on Kubernetes v1.16+ From bdcb6d3e981709ca2d4b4834e09dd576f8715a4e Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Wed, 16 Feb 2022 11:53:09 -0500 Subject: [PATCH 035/366] Copy edits for 1.8 release notes Signed-off-by: Abigail McCarthy --- changelogs/CHANGELOG-1.8.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/changelogs/CHANGELOG-1.8.md b/changelogs/CHANGELOG-1.8.md index 2a877c6896..aad674ea61 100644 --- a/changelogs/CHANGELOG-1.8.md +++ b/changelogs/CHANGELOG-1.8.md @@ -15,8 +15,8 @@ https://velero.io/docs/v1.8/upgrade-to-1.8/ ### Highlights -#### The plugins to support handling volumes created by the CSI drivers of cloud providers -The new versions of plugins for AWS, Azure and GCP will be released with the support of snapshotting and restoring the persistent volumes provisioned by CSI driver via the APIs of the cloud providers. With this enhancement, users can backup and restore the persistent volumes on these could providers without using the CSI plugin, which will remain beta and the feature flag `EnableCSI` will be disabled by default. +#### Velero plugins now support handling volumes created by the CSI drivers of cloud providers +Versions 1.4 of the Velero plugins for AWS, Azure and GCP now support snapshotting and restoring the persistent volumes provisioned by CSI driver via the APIs of the cloud providers. With this enhancement, users can backup and restore the persistent volumes on these cloud providers without using the Velero CSI plugin. The CSI plugin will remain beta and the feature flag `EnableCSI` will be disabled by default. For the version of the plugins and the CSI drivers they support respectively please see the table: @@ -27,13 +27,13 @@ For the version of the plugins and the CSI drivers they support respectively ple | velero-plugin-for-gcp | v1.4.0 | pd.csi.storage.gke.io | #### IPv6 dual stack support -By successfully running the E2E test on IPv6 dual stack environment, we've verfied the functionality of velero on IPv6 dual stack. -#### Refactor the controllers using Kubebuilder v3 -Continuing the code modernization work. Some controllers have been reworked using Kubebuilder v3. We'll continue the work to using Kubebuilder to rewrite the controllers in future releases. +We've verified the functionality of Velero on IPv6 dual stack by successfully running the E2E test on IPv6 dual stack environment. +#### Refactor the controllers using Kubebuilder v3 +In this release we continued our code modernization work, rewriting some controllers using Kubebuilder v3. This work is ongoing and we will continue to make progress in future releases. #### Enhancements to E2E test cases More test cases have been added to the E2E test suite to improve the release health. -#### Respect the cron setting of schedule -The creation time is taken into account to calculate the next run for scheduled backup. +#### Respect the cron setting of scheduled backup +The creation time is now taken into account to calculate the next run for scheduled backup. #### Deleting BSLs also cleans up related resources @@ -41,7 +41,7 @@ When a Backup Storage Location (BSL) is deleted, backup and Restic repository re #### Breaking changes -Starting v1.8 velero will only support v1 CRD, therefore, it will only run on Kubernetes v1.16+ +Starting in v1.8, Velero will only support Kubernetes v1 CRD meaning that Velero v1.8+ will only run on Kubernetes v1.16+. Before upgrading, make sure you are running a supported Kubernetes version. For more information, see our [compatibility matrix](https://github.com/vmware-tanzu/velero#velero-compatibility-matrix). ### All changes From 926f40170da3e4c64476b8602331774acd4dbfa1 Mon Sep 17 00:00:00 2001 From: "David L. Smith-Uchida" Date: Wed, 16 Feb 2022 10:30:11 -0800 Subject: [PATCH 036/366] Updates to 1.8 changelog Signed-off-by: David L. Smith-Uchida --- changelogs/CHANGELOG-1.8.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelogs/CHANGELOG-1.8.md b/changelogs/CHANGELOG-1.8.md index 94e94a6878..f4635ef1c0 100644 --- a/changelogs/CHANGELOG-1.8.md +++ b/changelogs/CHANGELOG-1.8.md @@ -29,6 +29,11 @@ For the version of the plugins and the CSI drivers they support respectively ple #### Break change Starting v1.8 velero will only support v1 CRD, therefore, it will only run on Kubernetes v1.16+ +#### Upload Progress Monitoring and Item Snapshotter +Item Snapshotter plugin API was merged. This will support both Upload Progress +monitoring and the planned Data Mover. Upload Progress monitoring PRs are +in progress for 1.9. + ### All changes * E2E test on ssr object with controller namespace mix-ups (#4521, @mqiu) From e0791a7fd0f826127dd776bd55c3e1e1baaa55cb Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 17 Feb 2022 08:52:28 +0800 Subject: [PATCH 037/366] Add Xun Jiang's profile information into Velero Website. (#4620) Signed-off-by: Xun Jiang --- site/content/contributors/02-xun-jiang.md | 7 +++++++ site/static/img/contributors/xun-jiang.png | Bin 0 -> 262094 bytes 2 files changed, 7 insertions(+) create mode 100644 site/content/contributors/02-xun-jiang.md create mode 100644 site/static/img/contributors/xun-jiang.png diff --git a/site/content/contributors/02-xun-jiang.md b/site/content/contributors/02-xun-jiang.md new file mode 100644 index 0000000000..0afc2dec73 --- /dev/null +++ b/site/content/contributors/02-xun-jiang.md @@ -0,0 +1,7 @@ +--- +first_name: Xun +last_name: Jiang +image: /img/contributors/xun-jiang.png +github_handle: blackpiglet +--- +Engineer \ No newline at end of file diff --git a/site/static/img/contributors/xun-jiang.png b/site/static/img/contributors/xun-jiang.png new file mode 100644 index 0000000000000000000000000000000000000000..24e6f05c93b6f4ad73a39bb472e197b0965c2d14 GIT binary patch literal 262094 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%0AC4Lq@RfJpQ(_tDUB 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

Z3iji}5o_p69`(Tk7MHJ9rq4 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 Date: Thu, 17 Feb 2022 06:27:02 +0530 Subject: [PATCH 038/366] Add CloudCasa to "Adopters of Velero" section on velero.io (#4588) - Added CloudCasa logo - Added description of CloudCasa and its integration with Velero Signed-off-by: Kiran Kothule --- ADOPTERS.md | 12 +++++-- site/static/img/adopters/cloudcasa.svg | 50 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 site/static/img/adopters/cloudcasa.svg diff --git a/ADOPTERS.md b/ADOPTERS.md index 636c96e416..44f25513c1 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -14,7 +14,7 @@ If you're using Velero and want to add your organization to this list, sighup.io      mayadata.io      replicated.com - +cloudcasa.io ## Success Stories Below is a list of adopters of Velero in **production environments** that have @@ -56,8 +56,11 @@ MayaData is a large user of Velero as well as a contributor. MayaData offers a D Okteto integrates Velero in [Okteto Cloud][94] and [Okteto Enterprise][95] to periodically backup and restore our clusters for disaster recovery. Velero is also a core software building block to provide namespace cloning capabilities, a feature that allows our users cloning staging environments into their personal development namespace for providing production-like development environments. **[Replicated][100]**
-Replicated uses the Velero open source project to enable snapshots in [KOTS][101] to backup Kubernetes manifests & persistent volumes. In addition to the default functionality that Velero provides, [KOTS][101] provides a detailed interface in the [Admin Console][102] that can be used to manage the storage destination and schedule, and to perform and monitor the backup and restore process. -​ +Replicated uses the Velero open source project to enable snapshots in [KOTS][101] to backup Kubernetes manifests & persistent volumes. In addition to the default functionality that Velero provides, [KOTS][101] provides a detailed interface in the [Admin Console][102] that can be used to manage the storage destination and schedule, and to perform and monitor the backup and restore process.
+ +**[CloudCasa][103]**
+[Catalogic Software][104] integrates Velero with [CloudCasa][103] - A Smart Home in the Cloud for Backups. CloudCasa is a simple, scalable, cloud-native solution providing data protection and disaster recovery as a service. This solution is built using Kubernetes for protecting Kubernetes clusters.
+ ## Adding your organization to the list of Velero Adopters If you are using Velero and would like to be included in the list of `Velero Adopters`, add an SVG version of your logo to the `site/static/img/adopters` directory in this repo and submit a [pull request][3] with your change. Name the image file something that reflects your company (e.g., if your company is called Acme, name the image acme.png). See this for an example [PR][4]. @@ -110,3 +113,6 @@ If you would like to add your logo to a future `Adopters of Velero` section on [ [100]: https://www.replicated.com [101]: https://kots.io [102]: https://kots.io/kotsadm/snapshots/overview/ + +[103]: https://cloudcasa.io/ +[104]: https://www.catalogicsoftware.com/ diff --git a/site/static/img/adopters/cloudcasa.svg b/site/static/img/adopters/cloudcasa.svg new file mode 100644 index 0000000000..e93dc2416b --- /dev/null +++ b/site/static/img/adopters/cloudcasa.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 6d9004dc62de890c0dcbdfaefe990804870120b5 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 17 Feb 2022 15:24:56 +0800 Subject: [PATCH 039/366] [fix] Avoid overwritten hook's exec.container parameter when running pod command executor Fix #4499 When hook influnce multiple pods, current logic's first pod's container will overwrite the hook's exec.container parameter. That will cause the other pod fail on the hook executing. Signed-off-by: Xun Jiang --- changelogs/unreleased/4661-jxun | 1 + pkg/podexec/pod_command_executor.go | 36 +++++++++++++----------- pkg/podexec/pod_command_executor_test.go | 23 +++++++++++++++ 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 changelogs/unreleased/4661-jxun diff --git a/changelogs/unreleased/4661-jxun b/changelogs/unreleased/4661-jxun new file mode 100644 index 0000000000..3c08a0e3c5 --- /dev/null +++ b/changelogs/unreleased/4661-jxun @@ -0,0 +1 @@ +Avoid overwritten hook's exec.container parameter when running pod command executor. \ No newline at end of file diff --git a/pkg/podexec/pod_command_executor.go b/pkg/podexec/pod_command_executor.go index a445548207..569fca4e58 100644 --- a/pkg/podexec/pod_command_executor.go +++ b/pkg/podexec/pod_command_executor.go @@ -83,42 +83,44 @@ func (e *defaultPodCommandExecutor) ExecutePodCommand(log logrus.FieldLogger, it return errors.New("hook is required") } + localHook := *hook + pod := new(corev1api.Pod) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item, pod); err != nil { return errors.WithStack(err) } - if hook.Container == "" { - if err := setDefaultHookContainer(pod, hook); err != nil { + if localHook.Container == "" { + if err := setDefaultHookContainer(pod, &localHook); err != nil { return err } - } else if err := ensureContainerExists(pod, hook.Container); err != nil { + } else if err := ensureContainerExists(pod, localHook.Container); err != nil { return err } - if len(hook.Command) == 0 { + if len(localHook.Command) == 0 { return errors.New("command is required") } - switch hook.OnError { + switch localHook.OnError { case api.HookErrorModeFail, api.HookErrorModeContinue: // use the specified value default: // default to fail - hook.OnError = api.HookErrorModeFail + localHook.OnError = api.HookErrorModeFail } - if hook.Timeout.Duration == 0 { - hook.Timeout.Duration = defaultTimeout + if localHook.Timeout.Duration == 0 { + localHook.Timeout.Duration = defaultTimeout } hookLog := log.WithFields( logrus.Fields{ "hookName": hookName, - "hookContainer": hook.Container, - "hookCommand": hook.Command, - "hookOnError": hook.OnError, - "hookTimeout": hook.Timeout, + "hookContainer": localHook.Container, + "hookCommand": localHook.Command, + "hookOnError": localHook.OnError, + "hookTimeout": localHook.Timeout, }, ) hookLog.Info("running exec hook") @@ -130,8 +132,8 @@ func (e *defaultPodCommandExecutor) ExecutePodCommand(log logrus.FieldLogger, it SubResource("exec") req.VersionedParams(&corev1api.PodExecOptions{ - Container: hook.Container, - Command: hook.Command, + Container: localHook.Container, + Command: localHook.Command, Stdout: true, Stderr: true, }, kscheme.ParameterCodec) @@ -156,8 +158,8 @@ func (e *defaultPodCommandExecutor) ExecutePodCommand(log logrus.FieldLogger, it }() var timeoutCh <-chan time.Time - if hook.Timeout.Duration > 0 { - timer := time.NewTimer(hook.Timeout.Duration) + if localHook.Timeout.Duration > 0 { + timer := time.NewTimer(localHook.Timeout.Duration) defer timer.Stop() timeoutCh = timer.C } @@ -165,7 +167,7 @@ func (e *defaultPodCommandExecutor) ExecutePodCommand(log logrus.FieldLogger, it select { case err = <-errCh: case <-timeoutCh: - return errors.Errorf("timed out after %v", hook.Timeout.Duration) + return errors.Errorf("timed out after %v", localHook.Timeout.Duration) } hookLog.Infof("stdout: %s", stdout.String()) diff --git a/pkg/podexec/pod_command_executor_test.go b/pkg/podexec/pod_command_executor_test.go index 39b46d11dd..d4637bf4a7 100644 --- a/pkg/podexec/pod_command_executor_test.go +++ b/pkg/podexec/pod_command_executor_test.go @@ -30,6 +30,7 @@ import ( "github.com/stretchr/testify/require" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" @@ -101,12 +102,34 @@ func TestExecutePodCommandMissingInputs(t *testing.T) { Container: "foo", }, }, + { + name: "hook's container is not overwritten by pod", + item: velerotest.UnstructuredOrDie(`{"kind":"Pod","spec":{"containers":[{"name":"foo"}]}}`).Object, + podNamespace: "ns", + podName: "pod", + hookName: "hook", + hook: &v1.ExecHook{ + Container: "bar", + }, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + pod := new(corev1api.Pod) + hookPodContainerNotSame := false + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(test.item, pod); err != nil { + assert.Error(t, err) + } + if (len(pod.Spec.Containers) > 0) && (pod.Spec.Containers[0].Name != test.hook.Container) { + hookPodContainerNotSame = true + } + e := &defaultPodCommandExecutor{} err := e.ExecutePodCommand(velerotest.NewLogger(), test.item, test.podNamespace, test.podName, test.hookName, test.hook) + if hookPodContainerNotSame && test.hook.Container == pod.Spec.Containers[0].Name { + assert.Error(t, fmt.Errorf("hook exec container is overwritten")) + } assert.Error(t, err) }) } From 5220562d37a6a796ec818e21886d549f365f33c9 Mon Sep 17 00:00:00 2001 From: danfengl Date: Fri, 26 Nov 2021 10:29:53 +0000 Subject: [PATCH 040/366] Add backup deletion e2e test Test case description is "Deleted backups are deleted from object storage and backups deleted from object storage can be deleted locally", in this test, only resource backup objects are target for verifition, restic repo verification is not included in this PR, and snapshot verification will be in later PR Signed-off-by: danfengl --- changelogs/unreleased/4401-danfengliu | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4401-danfengliu diff --git a/changelogs/unreleased/4401-danfengliu b/changelogs/unreleased/4401-danfengliu new file mode 100644 index 0000000000..e0982ae12b --- /dev/null +++ b/changelogs/unreleased/4401-danfengliu @@ -0,0 +1 @@ +Add backup deletion e2e test \ No newline at end of file From aedcdcccad4b2899686659e708bf1e8d5cc9a2a4 Mon Sep 17 00:00:00 2001 From: Jonas Rosland Date: Thu, 17 Feb 2022 14:28:19 -0500 Subject: [PATCH 041/366] Remove the office hours from the community page Signed-off-by: Jonas Rosland --- site/content/community/_index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index b6740d946c..bc57977b5a 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -20,4 +20,3 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) -* Have a question about "how do I configure Velero to do X?", or "how can I backup X with Velero?", or "how do I start to contribute to Velero?" and would like to chat with us face to face, come join the [Velero Office Hours](https://hackmd.io/I3u1x0u9T46KhuYZN4LX-A?view) that's held twice per month. From f1dea158d09f2738f99c99f19c83635534317099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 15 Feb 2022 14:09:34 +0800 Subject: [PATCH 042/366] Don't exit when getting no match for grep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't exit when getting no match for grep Signed-off-by: Wenkai Yin(尹文开) --- hack/release-tools/tag-release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hack/release-tools/tag-release.sh b/hack/release-tools/tag-release.sh index a8591a899f..11a56887f8 100755 --- a/hack/release-tools/tag-release.sh +++ b/hack/release-tools/tag-release.sh @@ -138,8 +138,8 @@ if [[ -n $release_branch_name ]]; then remote_release_branch_name="$remote/$release_branch_name" # Determine whether the local and remote release branches already exist - local_branch=$(git branch | grep "$release_branch_name") - remote_branch=$(git branch -r | grep "$remote_release_branch_name") + local_branch=$(git branch | { grep "$release_branch_name" || true; }) + remote_branch=$(git branch -r | { grep "$remote_release_branch_name" || true;}) if [[ -z $remote_branch ]]; then echo "The branch $remote_release_branch_name must be created before you tag the release." exit 1 From 59ff57b3c62574e2a7a1254aa53b93eeb2ebc363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 21 Feb 2022 17:45:58 +0800 Subject: [PATCH 043/366] Enable building and pushing image for release branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable building and pushing image for release branches Signed-off-by: Wenkai Yin(尹文开) --- .github/workflows/push.yml | 4 +++- hack/docker-push.sh | 23 +++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4d232ef9d2..9bcf750f4e 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -2,7 +2,9 @@ name: Main CI on: push: - branches: [ main ] + branches: + - 'main' + - 'release-**' tags: - '*' diff --git a/hack/docker-push.sh b/hack/docker-push.sh index cf3a0c5f80..8ad3df1132 100755 --- a/hack/docker-push.sh +++ b/hack/docker-push.sh @@ -56,26 +56,21 @@ elif [[ "$triggeredBy" == "tags" ]]; then TAG=$(echo $GITHUB_REF | cut -d / -f 3) fi -if [[ "$BRANCH" == "main" ]]; then - VERSION="$BRANCH" -elif [[ ! -z "$TAG" ]]; then +TAG_LATEST=false +if [[ ! -z "$TAG" ]]; then + echo "We're building tag $TAG" + VERSION="$TAG" # Explicitly checkout tags when building from a git tag. # This is not needed when building from main git fetch --tags # Calculate the latest release if there's a tag. highest_release - VERSION="$TAG" + if [[ "$TAG" == "$HIGHEST" ]]; then + TAG_LATEST=true + fi else - echo "We're not on main and we're not building a tag, exit early." - exit 0 -fi - -# Assume we're not tagging `latest` by default, and never on main. -TAG_LATEST=false -if [[ "$BRANCH" == "main" ]]; then - echo "Building main, not tagging latest." -elif [[ "$TAG" == "$HIGHEST" ]]; then - TAG_LATEST=true + echo "We're on branch $BRANCH" + VERSION="$BRANCH" fi if [[ -z "$BUILDX_PLATFORMS" ]]; then From fc2760d206fababa6cdac70e4b9006e9f1bb067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 21 Feb 2022 19:10:30 +0800 Subject: [PATCH 044/366] Append "-dev" suffix for the image tag of release branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append "-dev" suffix for the image tag of release branches: release-1.0-dev Signed-off-by: Wenkai Yin(尹文开) --- hack/docker-push.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hack/docker-push.sh b/hack/docker-push.sh index 8ad3df1132..d9f1835b8b 100755 --- a/hack/docker-push.sh +++ b/hack/docker-push.sh @@ -71,6 +71,9 @@ if [[ ! -z "$TAG" ]]; then else echo "We're on branch $BRANCH" VERSION="$BRANCH" + if [[ "$VERSION" == release-* ]]; then + VERSION=${VERSION}-dev + fi fi if [[ -z "$BUILDX_PLATFORMS" ]]; then @@ -82,6 +85,7 @@ echo "Highest tag found: $HIGHEST" echo "BRANCH: $BRANCH" echo "TAG: $TAG" echo "TAG_LATEST: $TAG_LATEST" +echo "VERSION: $VERSION" echo "BUILDX_PLATFORMS: $BUILDX_PLATFORMS" echo "Building and pushing container images." From 7be12a92206beb1ee11e5e9dcf4a09dd778605f8 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 21 Feb 2022 22:04:21 +0800 Subject: [PATCH 045/366] [fix] Add regional PV support for GKE fix #4663. For GKE pv, when create backup, return all zones retrived from node affinity. Signed-off-by: Xun Jiang --- changelogs/unreleased/4680-jxun | 1 + pkg/backup/item_backupper.go | 16 +++++++++++++++- pkg/backup/item_backupper_test.go | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4680-jxun diff --git a/changelogs/unreleased/4680-jxun b/changelogs/unreleased/4680-jxun new file mode 100644 index 0000000000..9d6b56ba2d --- /dev/null +++ b/changelogs/unreleased/4680-jxun @@ -0,0 +1 @@ +Support regional pv for GKE \ No newline at end of file diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index a2830484ac..648447625c 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "path/filepath" + "strings" "time" "github.com/pkg/errors" @@ -384,6 +385,7 @@ const ( awsEbsCsiZoneKey = "topology.ebs.csi.aws.com/zone" azureCsiZoneKey = "topology.disk.csi.azure.com/zone" gkeCsiZoneKey = "topology.gke.io/zone" + zoneSeparator = "__" ) // takePVSnapshot triggers a snapshot for the volume/disk underlying a PersistentVolume if the provided @@ -539,15 +541,27 @@ func zoneFromPVNodeAffinity(res *corev1api.PersistentVolume, topologyKeys ...str return "", "" } keySet := sets.NewString(topologyKeys...) + providerGke := false + zones := make([]string, 0) for _, term := range nodeAffinity.Required.NodeSelectorTerms { if term.MatchExpressions == nil { continue } for _, exp := range term.MatchExpressions { if keySet.Has(exp.Key) && exp.Operator == "In" && len(exp.Values) > 0 { - return exp.Key, exp.Values[0] + if exp.Key == gkeCsiZoneKey { + providerGke = true + zones = append(zones, exp.Values[0]) + } else { + return exp.Key, exp.Values[0] + } } } } + + if providerGke { + return gkeCsiZoneKey, strings.Join(zones, zoneSeparator) + } + return "", "" } diff --git a/pkg/backup/item_backupper_test.go b/pkg/backup/item_backupper_test.go index 819ca54d85..03ed7c017a 100644 --- a/pkg/backup/item_backupper_test.go +++ b/pkg/backup/item_backupper_test.go @@ -131,6 +131,21 @@ func Test_zoneFromPVNodeAffinity(t *testing.T) { wantKey: "topology.disk.csi.azure.com/zone", wantValue: "us-central", }, + { + name: "Volume with multiple valid keys, and provider is gke, returns the all match", // it should never happen + pv: builder.ForPersistentVolume("multi-matching-pv").NodeAffinityRequired( + builder.ForNodeSelector( + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "us-central1-c").Result(), + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "us-east-2c", "us-east-2b").Result(), + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "europe-north1-a").Result(), + ).Result(), + ).Result(), + wantKey: "topology.gke.io/zone", + wantValue: "us-central1-c__us-east-2c__europe-north1-a", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 8c2a07b9068b394664e5afae1e3a42ab3b631882 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 17 Feb 2022 10:13:28 +0800 Subject: [PATCH 046/366] Update --use-owner-references-in-backup description in velero command line Specify the risk of this parameter set to true. Add the issue first reported about this topic which includeds the google document illustrates about it. Signed-off-by: Xun Jiang --- changelogs/unreleased/4660-jxun | 1 + pkg/cmd/cli/schedule/create.go | 2 +- site/content/docs/main/backup-reference.md | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4660-jxun diff --git a/changelogs/unreleased/4660-jxun b/changelogs/unreleased/4660-jxun new file mode 100644 index 0000000000..cf09ca08e2 --- /dev/null +++ b/changelogs/unreleased/4660-jxun @@ -0,0 +1 @@ +Update --use-owner-references-in-backup description in velero command line. \ No newline at end of file diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index 5e15ac6bc5..c5fe9398cc 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -95,7 +95,7 @@ func NewCreateOptions() *CreateOptions { func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { o.BackupOptions.BindFlags(flags) flags.StringVar(&o.Schedule, "schedule", o.Schedule, "A cron expression specifying a recurring schedule for this backup to run") - flags.BoolVar(&o.UseOwnerReferencesInBackup, "use-owner-references-in-backup", o.UseOwnerReferencesInBackup, "Specifies whether to use OwnerReferences on backups created by this Schedule") + flags.BoolVar(&o.UseOwnerReferencesInBackup, "use-owner-references-in-backup", o.UseOwnerReferencesInBackup, "Specifies whether to use OwnerReferences on backups created by this Schedule. Notice: if set to true, when schedule is deleted, backups will be deleted too.") } func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { diff --git a/site/content/docs/main/backup-reference.md b/site/content/docs/main/backup-reference.md index 60e0191880..06fdc07b85 100644 --- a/site/content/docs/main/backup-reference.md +++ b/site/content/docs/main/backup-reference.md @@ -61,6 +61,20 @@ velero backup create --from-schedule example-schedule This command will immediately trigger a new backup based on your template for `example-schedule`. This will not affect the backup schedule, and another backup will trigger at the scheduled time. + +### Limitation +Backups created from schedule can have owner reference to the schedule. This can be achieved by command: + +``` +velero schedule create --use-owner-references-in-backup +``` +By this way, schedule is the owner of it created backups. This is useful for some GitOps scenarios, or the resource tree of k8s synchronized from other places. + +Please do notice there is also side effect that may not be expected. Because schedule is the owner, when the schedule is deleted, the related backups CR (Just backup CR is deleted. Backup data still exists in object store and snapshots) will be deleted by k8s GC controller, too, but Velero controller will sync these backups from object store's metadata into k8s. Then k8s GC controller and Velero controller will fight over whether these backups should exist all through. + +If there is possibility the schedule will be disable to not create backup anymore, and the created backups are still useful. Please do not enable this option. For detail, please reference to [Backups created by a schedule with useOwnerReferenceInBackup set do not get synced properly](https://github.com/vmware-tanzu/velero/issues/4093). + + ## Kubernetes API Pagination By default, Velero will paginate the LIST API call for each resource type in the Kubernetes API when collecting items into a backup. The `--client-page-size` flag for the Velero server configures the size of each page. From 4c8d1c2693ba12a1b86feb0c1dea316643c6b3c0 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 22 Feb 2022 16:06:39 +0800 Subject: [PATCH 047/366] Modify according to comments 1. rename zoneSeparator to gkeZoneSeparator 2. add example of regional PV's node affinity. modify test case description. Signed-off-by: Xun Jiang --- pkg/backup/item_backupper.go | 4 ++-- pkg/backup/item_backupper_test.go | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 648447625c..32a9f55044 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -385,7 +385,7 @@ const ( awsEbsCsiZoneKey = "topology.ebs.csi.aws.com/zone" azureCsiZoneKey = "topology.disk.csi.azure.com/zone" gkeCsiZoneKey = "topology.gke.io/zone" - zoneSeparator = "__" + gkeZoneSeparator = "__" ) // takePVSnapshot triggers a snapshot for the volume/disk underlying a PersistentVolume if the provided @@ -560,7 +560,7 @@ func zoneFromPVNodeAffinity(res *corev1api.PersistentVolume, topologyKeys ...str } if providerGke { - return gkeCsiZoneKey, strings.Join(zones, zoneSeparator) + return gkeCsiZoneKey, strings.Join(zones, gkeZoneSeparator) } return "", "" diff --git a/pkg/backup/item_backupper_test.go b/pkg/backup/item_backupper_test.go index 03ed7c017a..2152a53010 100644 --- a/pkg/backup/item_backupper_test.go +++ b/pkg/backup/item_backupper_test.go @@ -132,7 +132,22 @@ func Test_zoneFromPVNodeAffinity(t *testing.T) { wantValue: "us-central", }, { - name: "Volume with multiple valid keys, and provider is gke, returns the all match", // it should never happen + /* an valid example of node affinity in a GKE's regional PV + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: topology.gke.io/zone + operator: In + values: + - us-central1-a + - matchExpressions: + - key: topology.gke.io/zone + operator: In + values: + - us-central1-c + */ + name: "Volume with multiple valid keys, and provider is gke, returns all valid entries's first zone value", pv: builder.ForPersistentVolume("multi-matching-pv").NodeAffinityRequired( builder.ForNodeSelector( *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", From 0070138c62403fc1afcd448ec8b873bbd91c2a89 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 22 Feb 2022 18:15:28 +0800 Subject: [PATCH 048/366] Bypass the remap CRD version plugin when v1beta1 CRD is not supported When velero is running on clusters that don't support v1beta1 CRD, the plugin will not try to backup v1beta1 CRD. The plugin should be kept for backward compatibility. It will be removed when velero drop the support for k8s v1.21 Signed-off-by: Daniel Jiang --- changelogs/unreleased/4686-reasonerjt | 1 + pkg/backup/remap_crd_version_action.go | 32 +++++++++++-- pkg/backup/remap_crd_version_action_test.go | 53 +++++++++++++++++++-- pkg/cmd/server/plugin/plugin.go | 11 ++++- 4 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/4686-reasonerjt diff --git a/changelogs/unreleased/4686-reasonerjt b/changelogs/unreleased/4686-reasonerjt new file mode 100644 index 0000000000..ed64de8f64 --- /dev/null +++ b/changelogs/unreleased/4686-reasonerjt @@ -0,0 +1 @@ +Bypass the remap CRD version plugin when v1beta1 CRD is not supported diff --git a/pkg/backup/remap_crd_version_action.go b/pkg/backup/remap_crd_version_action.go index b282e876e3..d54f018ca5 100644 --- a/pkg/backup/remap_crd_version_action.go +++ b/pkg/backup/remap_crd_version_action.go @@ -30,6 +30,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -37,13 +39,14 @@ import ( // RemapCRDVersionAction inspects CustomResourceDefinition and decides if it is a v1 // CRD that needs to be backed up as v1beta1. type RemapCRDVersionAction struct { - logger logrus.FieldLogger - betaCRDClient apiextv1beta1client.CustomResourceDefinitionInterface + logger logrus.FieldLogger + betaCRDClient apiextv1beta1client.CustomResourceDefinitionInterface + discoveryHelper velerodiscovery.Helper } // NewRemapCRDVersionAction instantiates a new RemapCRDVersionAction plugin. -func NewRemapCRDVersionAction(logger logrus.FieldLogger, betaCRDClient apiextv1beta1client.CustomResourceDefinitionInterface) *RemapCRDVersionAction { - return &RemapCRDVersionAction{logger: logger, betaCRDClient: betaCRDClient} +func NewRemapCRDVersionAction(logger logrus.FieldLogger, betaCRDClient apiextv1beta1client.CustomResourceDefinitionInterface, discoveryHelper velerodiscovery.Helper) *RemapCRDVersionAction { + return &RemapCRDVersionAction{logger: logger, betaCRDClient: betaCRDClient, discoveryHelper: discoveryHelper} } // AppliesTo selects the resources the plugin should run against. In this case, CustomResourceDefinitions. @@ -68,7 +71,26 @@ func (a *RemapCRDVersionAction) Execute(item runtime.Unstructured, backup *v1.Ba return item, nil, nil } - // We've got a v1 CRD, so proceed. + // This plugin will exit if the CRD was installed via v1beta1 but the cluster does not support v1beta1 CRD + supportv1b1 := false +CheckVersion: + for _, g := range a.discoveryHelper.APIGroups() { + if g.Name == apiextv1.GroupName { + for _, v := range g.Versions { + if v.Version == apiextv1beta1.SchemeGroupVersion.Version { + supportv1b1 = true + break CheckVersion + } + } + + } + } + if !supportv1b1 { + a.logger.Info("Exiting RemapCRDVersionAction, the cluster does not support v1beta1 CRD") + return item, nil, nil + } + + // We've got a v1 CRD and the cluster supports v1beta1 CRD, so proceed. var crd apiextv1.CustomResourceDefinition // Do not use runtime.DefaultUnstructuredConverter.FromUnstructured here because it has a bug when converting integers/whole diff --git a/pkg/backup/remap_crd_version_action_test.go b/pkg/backup/remap_crd_version_action_test.go index 9308ea049c..ea679ef0eb 100644 --- a/pkg/backup/remap_crd_version_action_test.go +++ b/pkg/backup/remap_crd_version_action_test.go @@ -32,6 +32,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" velerotest "github.com/vmware-tanzu/velero/pkg/test" @@ -48,8 +50,7 @@ func TestRemapCRDVersionAction(t *testing.T) { c := b.Result() _, err := betaClient.Create(context.TODO(), c, metav1.CreateOptions{}) require.NoError(t, err) - - a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient) + a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient, fakeDiscoveryHelper()) t.Run("Test a v1 CRD without any Schema information", func(t *testing.T) { b := builder.ForV1CustomResourceDefinition("test.velero.io") @@ -109,6 +110,33 @@ func TestRemapCRDVersionAction(t *testing.T) { require.NoError(t, err) assert.Equal(t, "apiextensions.k8s.io/v1beta1", item.UnstructuredContent()["apiVersion"]) }) + + t.Run("When the cluster only supports v1 CRD, v1 CRD will be returned even the input has Spec.PreserveUnknownFields set to true (issue 4080)", func(t *testing.T) { + a.discoveryHelper = &velerotest.FakeDiscoveryHelper{ + APIGroupsList: []metav1.APIGroup{ + { + Name: apiextv1.GroupName, + Versions: []metav1.GroupVersionForDiscovery{ + { + Version: apiextv1.SchemeGroupVersion.Version, + }, + }, + }, + }, + } + b := builder.ForV1CustomResourceDefinition("test.velero.io") + b.PreserveUnknownFields(true) + c := b.Result() + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&c) + require.NoError(t, err) + + item, _, err := a.Execute(&unstructured.Unstructured{Object: obj}, backup) + require.NoError(t, err) + assert.Equal(t, "apiextensions.k8s.io/v1", item.UnstructuredContent()["apiVersion"]) + // set it back to the default one + a.discoveryHelper = fakeDiscoveryHelper() + }) + } // TestRemapCRDVersionActionData tests the RemapCRDVersionAction plugin against actual CRD to confirm that the v1beta1 version is returned when the v1 version is passed in to the plugin. @@ -116,8 +144,7 @@ func TestRemapCRDVersionActionData(t *testing.T) { backup := &v1.Backup{} clientset := apiextfakes.NewSimpleClientset() betaClient := clientset.ApiextensionsV1beta1().CustomResourceDefinitions() - - a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient) + a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient, fakeDiscoveryHelper()) tests := []struct { crd string @@ -192,3 +219,21 @@ func TestRemapCRDVersionActionData(t *testing.T) { } } + +func fakeDiscoveryHelper() velerodiscovery.Helper { + return &velerotest.FakeDiscoveryHelper{ + APIGroupsList: []metav1.APIGroup{ + { + Name: apiextv1.GroupName, + Versions: []metav1.GroupVersionForDiscovery{ + { + Version: apiextv1beta1.SchemeGroupVersion.Version, + }, + { + Version: apiextv1.SchemeGroupVersion.Version, + }, + }, + }, + }, + } +} diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 5072002660..5c833d76a1 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -110,7 +110,16 @@ func newRemapCRDVersionAction(f client.Factory) veleroplugin.HandlerInitializer return nil, err } - return backup.NewRemapCRDVersionAction(logger, client.ApiextensionsV1beta1().CustomResourceDefinitions()), nil + clientset, err := f.KubeClient() + if err != nil { + return nil, err + } + discoveryHelper, err := velerodiscovery.NewHelper(clientset.Discovery(), logger) + if err != nil { + return nil, err + } + + return backup.NewRemapCRDVersionAction(logger, client.ApiextensionsV1beta1().CustomResourceDefinitions(), discoveryHelper), nil } } From 9cb81d825eb0dba6a0a6f59b08df9aa1f031e3d0 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 23 Feb 2022 13:12:36 +0800 Subject: [PATCH 049/366] Add skip case support in e2e test Signed-off-by: Xun Jiang --- changelogs/unreleased/4692-jxun | 1 + test/e2e/Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4692-jxun diff --git a/changelogs/unreleased/4692-jxun b/changelogs/unreleased/4692-jxun new file mode 100644 index 0000000000..b18f53f860 --- /dev/null +++ b/changelogs/unreleased/4692-jxun @@ -0,0 +1 @@ +Add GINKGO_SKIP to support skip specific case in e2e test. \ No newline at end of file diff --git a/test/e2e/Makefile b/test/e2e/Makefile index cf8eacd248..b49829ab68 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -46,6 +46,7 @@ GINKGO := $(GOPATH)/bin/ginkgo KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin GINKGO_FOCUS ?= +GINKGO_SKIP ?= VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) @@ -85,7 +86,7 @@ run: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plug-in provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" . -- -velerocli=$(VELERO_CLI) \ + @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" -skip="$(GINKGO_SKIP)" . -- -velerocli=$(VELERO_CLI) \ -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ From 77be862b9c8d4d0b9187a8239059e6ed9b54cd1b Mon Sep 17 00:00:00 2001 From: James Landrein Date: Thu, 24 Feb 2022 10:00:14 +0100 Subject: [PATCH 050/366] Add support for --pod-labels (#4694) * Add support for --pod-labels * Add changelog Signed-off-by: James Landrein --- changelogs/unreleased/4694-j4m3s-s | 1 + pkg/cmd/cli/install/install.go | 4 ++++ pkg/install/daemonset.go | 7 +++---- pkg/install/deployment.go | 12 ++++++++---- pkg/install/resources.go | 18 +++++++++++++++++- 5 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/4694-j4m3s-s diff --git a/changelogs/unreleased/4694-j4m3s-s b/changelogs/unreleased/4694-j4m3s-s new file mode 100644 index 0000000000..636d635dbb --- /dev/null +++ b/changelogs/unreleased/4694-j4m3s-s @@ -0,0 +1 @@ +Add --pod-labels flag to velero install diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index 5b4941dffc..dd7b9a914c 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -47,6 +47,7 @@ type InstallOptions struct { Prefix string ProviderName string PodAnnotations flag.Map + PodLabels flag.Map ServiceAccountAnnotations flag.Map VeleroPodCPURequest string VeleroPodMemRequest string @@ -84,6 +85,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.Image, "image", o.Image, "Image to use for the Velero and restic server pods. Optional.") flags.StringVar(&o.Prefix, "prefix", o.Prefix, "Prefix under which all Velero data should be stored within the bucket. Optional.") flags.Var(&o.PodAnnotations, "pod-annotations", "Annotations to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2") + flags.Var(&o.PodLabels, "pod-labels", "Labels to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2") flags.Var(&o.ServiceAccountAnnotations, "sa-annotations", "Annotations to add to the Velero ServiceAccount. Add iam.gke.io/gcp-service-account=[GSA_NAME]@[PROJECT_NAME].iam.gserviceaccount.com for workload identity. Optional. Format is key1=value1,key2=value2") flags.StringVar(&o.VeleroPodCPURequest, "velero-pod-cpu-request", o.VeleroPodCPURequest, `CPU request for Velero pod. A value of "0" is treated as unbounded. Optional.`) flags.StringVar(&o.VeleroPodMemRequest, "velero-pod-mem-request", o.VeleroPodMemRequest, `Memory request for Velero pod. A value of "0" is treated as unbounded. Optional.`) @@ -116,6 +118,7 @@ func NewInstallOptions() *InstallOptions { BackupStorageConfig: flag.NewMap(), VolumeSnapshotConfig: flag.NewMap(), PodAnnotations: flag.NewMap(), + PodLabels: flag.NewMap(), ServiceAccountAnnotations: flag.NewMap(), VeleroPodCPURequest: install.DefaultVeleroPodCPURequest, VeleroPodMemRequest: install.DefaultVeleroPodMemRequest, @@ -173,6 +176,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { Bucket: o.BucketName, Prefix: o.Prefix, PodAnnotations: o.PodAnnotations.Data(), + PodLabels: o.PodLabels.Data(), ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), VeleroPodResources: veleroPodResources, ResticPodResources: resticPodResources, diff --git a/pkg/install/daemonset.go b/pkg/install/daemonset.go index 74e841f3d5..fb88647632 100644 --- a/pkg/install/daemonset.go +++ b/pkg/install/daemonset.go @@ -68,10 +68,9 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "name": "restic", - "component": "velero", - }, + Labels: podLabels(c.labels, map[string]string{ + "name": "restic", + }), Annotations: c.annotations, }, Spec: corev1.PodSpec{ diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index d07ded2139..191835c22c 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -36,6 +36,7 @@ type podTemplateConfig struct { envVars []corev1.EnvVar restoreOnly bool annotations map[string]string + labels map[string]string resources corev1.ResourceRequirements withSecret bool defaultResticMaintenanceFrequency time.Duration @@ -56,6 +57,12 @@ func WithAnnotations(annotations map[string]string) podTemplateOption { } } +func WithLabels(labels map[string]string) podTemplateOption { + return func(c *podTemplateConfig) { + c.labels = labels + } +} + func WithEnvFromSecretKey(varName, secret, key string) podTemplateOption { return func(c *podTemplateConfig) { c.envVars = append(c.envVars, corev1.EnvVar{ @@ -141,9 +148,6 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment args = append(args, "--default-volumes-to-restic=true") } - containerLabels := Labels() - containerLabels["deploy"] = "velero" - deployment := &appsv1.Deployment{ ObjectMeta: objectMeta(namespace, "velero"), TypeMeta: metav1.TypeMeta{ @@ -154,7 +158,7 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"deploy": "velero"}}, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: containerLabels, + Labels: podLabels(c.labels, map[string]string{"deploy": "velero"}), Annotations: podAnnotations(c.annotations), }, Spec: corev1.PodSpec{ diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 0e86698cfc..7a2c4e5deb 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -48,6 +48,20 @@ func Labels() map[string]string { } } +func podLabels(userLabels ...map[string]string) map[string]string { + // Use the default labels as a starting point + base := Labels() + + // Merge base labels with user labels to enforce CLI precedence + for _, labels := range userLabels { + for k, v := range labels { + base[k] = v + } + } + + return base +} + func podAnnotations(userAnnotations map[string]string) map[string]string { // Use the default annotations as a starting point base := map[string]string{ @@ -77,7 +91,6 @@ func objectMeta(namespace, name string) metav1.ObjectMeta { return metav1.ObjectMeta{ Name: name, Namespace: namespace, - Labels: Labels(), } } @@ -201,6 +214,7 @@ type VeleroOptions struct { Bucket string Prefix string PodAnnotations map[string]string + PodLabels map[string]string ServiceAccountAnnotations map[string]string VeleroPodResources corev1.ResourceRequirements ResticPodResources corev1.ResourceRequirements @@ -265,6 +279,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { deployOpts := []podTemplateOption{ WithAnnotations(o.PodAnnotations), + WithLabels(o.PodLabels), WithImage(o.Image), WithResources(o.VeleroPodResources), WithSecret(secretPresent), @@ -294,6 +309,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { if o.UseRestic { dsOpts := []podTemplateOption{ WithAnnotations(o.PodAnnotations), + WithLabels(o.PodLabels), WithImage(o.Image), WithResources(o.ResticPodResources), WithSecret(secretPresent), From cb11bf1924273428890b4f75e012c6e18cf8fed7 Mon Sep 17 00:00:00 2001 From: James Landrein Date: Thu, 24 Feb 2022 10:20:10 +0100 Subject: [PATCH 051/366] Fix labels removed in ObjectMeta Signed-off-by: James Landrein --- pkg/install/resources.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 7a2c4e5deb..f4c4d268a3 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -91,6 +91,7 @@ func objectMeta(namespace, name string) metav1.ObjectMeta { return metav1.ObjectMeta{ Name: name, Namespace: namespace, + Labels: Labels(), } } From a57298254f2d90dc09532ad7228464e1b3bb45ad Mon Sep 17 00:00:00 2001 From: MatthieuFin Date: Thu, 24 Feb 2022 12:09:04 +0100 Subject: [PATCH 052/366] Fix typo on tests fields name and add another test with gesture of errors wanted on equals Signed-off-by: MatthieuFin --- pkg/util/kube/security_context_test.go | 50 ++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/pkg/util/kube/security_context_test.go b/pkg/util/kube/security_context_test.go index f4b9ccb7ce..64a9a22cb8 100644 --- a/pkg/util/kube/security_context_test.go +++ b/pkg/util/kube/security_context_test.go @@ -67,7 +67,7 @@ capabilities: add: - cap1 - cap2 -sELinuxOptions: +seLinuxOptions: user: userLabel role: roleLabel type: typeLabel @@ -98,6 +98,46 @@ allowPrivilegeEscalation: false`}, AllowPrivilegeEscalation: boolptr.False(), }, }, + { + "valid securityContext with comments only secCtx key check seLinuxOptions is correctly parsed", + args{"", "", "", ` +capabilities: + drop: + - ALL + add: + - cap1 + - cap2 +seLinuxOptions: + user: userLabelFail + role: roleLabel + type: typeLabel + level: levelLabel +# user www-data +runAsUser: 3333 +# group www-data +runAsGroup: 3333 +runAsNonRoot: true +readOnlyRootFilesystem: true +allowPrivilegeEscalation: false`}, + true, + &corev1.SecurityContext{ + RunAsUser: pointInt64(3333), + RunAsGroup: pointInt64(3333), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{"cap1", "cap2"}, + }, + SELinuxOptions: &corev1.SELinuxOptions{ + User: "userLabel", + Role: "roleLabel", + Type: "typeLabel", + Level: "levelLabel", + }, + RunAsNonRoot: boolptr.True(), + ReadOnlyRootFilesystem: boolptr.True(), + AllowPrivilegeEscalation: boolptr.False(), + }, + }, { "valid securityContext with secCtx key override runAsUser runAsGroup and allowPrivilegeEscalation", args{"1001", "999", "true", ` @@ -107,7 +147,7 @@ capabilities: add: - cap1 - cap2 -sELinuxOptions: +seLinuxOptions: user: userLabel role: roleLabel type: typeLabel @@ -216,7 +256,7 @@ allowPrivilegeEscalation: false`}, for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseSecurityContext(tt.args.runAsUser, tt.args.runAsGroup, tt.args.allowPrivilegeEscalation, tt.args.secCtx) - if tt.wantErr { + if err != nil && tt.wantErr { assert.Error(t, err) return } @@ -226,6 +266,10 @@ allowPrivilegeEscalation: false`}, tt.expected = &corev1.SecurityContext{} } + if tt.wantErr { + assert.NotEqual(t, *tt.expected, got) + return + } assert.Equal(t, *tt.expected, got) }) } From 9f46b10b701e66f542348b63a78fde3070e874db Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 28 Feb 2022 00:54:41 +0800 Subject: [PATCH 053/366] Enable coverage in test.sh and upload to codecov Signed-off-by: Daniel Jiang --- .github/workflows/pr-ci-check.yml | 6 ++++++ .github/workflows/push.yml | 7 +++++++ changelogs/unreleased/4704-reasonerjt | 1 + hack/test.sh | 2 +- 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4704-reasonerjt diff --git a/.github/workflows/pr-ci-check.yml b/.github/workflows/pr-ci-check.yml index 12bb066224..834c3c589b 100644 --- a/.github/workflows/pr-ci-check.yml +++ b/.github/workflows/pr-ci-check.yml @@ -21,3 +21,9 @@ jobs: ${{ runner.os }}-go- - name: Make ci run: make ci + - name: Upload test coverage + uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.out + verbose: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4d232ef9d2..1c41655e5e 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -40,6 +40,13 @@ jobs: - name: Test run: make test + - name: Upload test coverage + uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.out + verbose: true + # Only try to publish the container image from the root repo; forks don't have permission to do so and will always get failures. - name: Publish container image if: github.repository == 'vmware-tanzu/velero' diff --git a/changelogs/unreleased/4704-reasonerjt b/changelogs/unreleased/4704-reasonerjt new file mode 100644 index 0000000000..79218ea620 --- /dev/null +++ b/changelogs/unreleased/4704-reasonerjt @@ -0,0 +1 @@ +Enable coverage in test.sh and upload to codecov \ No newline at end of file diff --git a/hack/test.sh b/hack/test.sh index bab6383190..40846ff53d 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -43,5 +43,5 @@ fi # but the user and group don't exist inside the container, when the code(https://github.com/kubernetes-sigs/controller-runtime/blob/v0.10.2/pkg/internal/testing/addr/manager.go#L44) # tries to get the cache directory, it gets the directory "/" and then get the permission error when trying to create directory under "/". # Specifying the cache directory by environment variable "XDG_CACHE_HOME" to workaround it -XDG_CACHE_HOME=/tmp/ go test -installsuffix "static" -short -timeout 60s "${TARGETS[@]}" +XDG_CACHE_HOME=/tmp/ go test -installsuffix "static" -short -timeout 60s -coverprofile=coverage.out "${TARGETS[@]}" echo "Success!" From 0503b2675f89da4d0bc91957348bb6798c7efa26 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 28 Feb 2022 19:28:17 +0800 Subject: [PATCH 054/366] Modify according to comments. Change hook.Container's value in test case to "" Signed-off-by: Xun Jiang --- pkg/podexec/pod_command_executor_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/podexec/pod_command_executor_test.go b/pkg/podexec/pod_command_executor_test.go index d4637bf4a7..addd627242 100644 --- a/pkg/podexec/pod_command_executor_test.go +++ b/pkg/podexec/pod_command_executor_test.go @@ -109,7 +109,7 @@ func TestExecutePodCommandMissingInputs(t *testing.T) { podName: "pod", hookName: "hook", hook: &v1.ExecHook{ - Container: "bar", + Container: "", }, }, } @@ -127,6 +127,7 @@ func TestExecutePodCommandMissingInputs(t *testing.T) { e := &defaultPodCommandExecutor{} err := e.ExecutePodCommand(velerotest.NewLogger(), test.item, test.podNamespace, test.podName, test.hookName, test.hook) + if hookPodContainerNotSame && test.hook.Container == pod.Spec.Containers[0].Name { assert.Error(t, fmt.Errorf("hook exec container is overwritten")) } From 4ebf764ddc50a6e0abd744a5a4df7c67b6621dcf Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 25 Jan 2022 01:33:33 +0000 Subject: [PATCH 055/366] Add e2e for verify snapshto in VSL Signed-off-by: danfengl --- changelogs/unreleased/4401-danfengliu | 1 - go.mod | 4 +- go.sum | 4 +- test/e2e/backups/deletion.go | 33 +++-- test/e2e/e2e_suite_test.go | 3 +- test/e2e/types.go | 6 + test/e2e/util/providers/aws_utils.go | 46 ++++++- test/e2e/util/providers/azure_utils.go | 156 ++++++++++++++++++++++-- test/e2e/util/providers/common.go | 71 ++++++++++- test/e2e/util/providers/gcloud_utils.go | 49 +++++++- test/e2e/util/velero/velero_utils.go | 36 +++++- 11 files changed, 379 insertions(+), 30 deletions(-) delete mode 100644 changelogs/unreleased/4401-danfengliu diff --git a/changelogs/unreleased/4401-danfengliu b/changelogs/unreleased/4401-danfengliu deleted file mode 100644 index e0982ae12b..0000000000 --- a/changelogs/unreleased/4401-danfengliu +++ /dev/null @@ -1 +0,0 @@ -Add backup deletion e2e test \ No newline at end of file diff --git a/go.mod b/go.mod index 972a2a9ab4..328767bb7d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( cloud.google.com/go/storage v1.10.0 github.com/Azure/azure-pipeline-go v0.2.3 - github.com/Azure/azure-sdk-for-go v42.0.0+incompatible + github.com/Azure/azure-sdk-for-go v61.4.0+incompatible github.com/Azure/azure-storage-blob-go v0.14.0 github.com/Azure/go-autorest/autorest v0.11.21 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 @@ -35,6 +35,7 @@ require ( github.com/vmware-tanzu/crash-diagnostics v0.3.7 golang.org/x/mod v0.4.2 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f google.golang.org/api v0.56.0 google.golang.org/grpc v1.40.0 k8s.io/api v0.22.2 @@ -102,7 +103,6 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.0 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index 525808f44f..fcfe0b81a7 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v42.0.0+incompatible h1:yz6sFf5bHZ+gEOQVuK5JhPqTTAmv+OvSLSaqgzqaCwY= -github.com/Azure/azure-sdk-for-go v42.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v61.4.0+incompatible h1:BF2Pm3aQWIa6q9KmxyF1JYKYXtVw67vtvu2Wd54NGuY= +github.com/Azure/azure-sdk-for-go v61.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 477bf227da..100dede1cc 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -33,17 +33,15 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) -const ( - deletionTest = "deletion-workload" -) +const deletionTest = "deletion-workload" // Test backup and restore of Kibishi using restic -func Backup_deletion_with_snapshots() { +func BackupDeletionWithSnapshots() { backup_deletion_test(true) } -func Backup_deletion_with_restic() { +func BackupDeletionWithRestic() { backup_deletion_test(false) } func backup_deletion_test(useVolumeSnapshots bool) { @@ -86,7 +84,6 @@ func backup_deletion_test(useVolumeSnapshots bool) { // runUpgradeTests runs upgrade test on the provider by kibishii. func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNamespace, backupName, backupLocation string, useVolumeSnapshots bool, registryCredentialFile, bslPrefix, bslConfig string) error { - oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) if err := CreateNamespace(oneHourTimeout, client, deletionTest); err != nil { @@ -124,15 +121,31 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa if err != nil { return err } + if useVolumeSnapshots { + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.ExpectCount = 2 + snapshotCheckPoint.NamespaceBackedUp = deletionTest + err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, snapshotCheckPoint) + if err != nil { + return err + } + } err = DeleteBackupResource(context.Background(), veleroCLI, backupName) if err != nil { return err } + err = ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, 5) if err != nil { - fmt.Println(errors.Wrapf(err, "Failed to get object from bucket %q", backupName)) return err } + if useVolumeSnapshots { + err = SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix) + if err != nil { + return err + } + } + backupName = "backup-1-" + UUIDgen.String() if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command @@ -142,7 +155,6 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa } err = DeleteObjectsInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix) if err != nil { - fmt.Println(errors.Wrapf(err, "Failed to delete object in bucket %q", backupName)) return err } err = ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, 1) @@ -151,8 +163,9 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa } err = DeleteBackupResource(context.Background(), veleroCLI, backupName) if err != nil { - fmt.Println(errors.Wrapf(err, "|| UNEXPECTED || - Failed to delete backup %q", backupName)) - return err + return errors.Wrapf(err, "|| UNEXPECTED || - Failed to delete backup %q", backupName) + } else { + fmt.Printf("|| EXPECTED || - Success to delete backup %s locally\n", backupName) } fmt.Printf("|| EXPECTED || - Backup deletion test completed successfully\n") return nil diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 0c582f6005..cabb27f452 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -89,7 +89,8 @@ var _ = Describe("[ResourceFiltering][IncludeNamespaces][Restore] Velero test on var _ = Describe("[ResourceFiltering][IncludeResources][Backup] Velero test on include resources from the cluster backup", BackupWithIncludeResources) var _ = Describe("[ResourceFiltering][IncludeResources][Restore] Velero test on include resources from the cluster restore", RestoreWithIncludeResources) var _ = Describe("[ResourceFiltering][LabelSelector] Velero test on backup include resources matching the label selector", BackupWithLabelSelector) -var _ = Describe("[Backups][Deletion] Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", Backup_deletion_with_restic) +var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup deletion", BackupDeletionWithRestic) +var _ = Describe("[Backups][Deletion][Snapshot] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) diff --git a/test/e2e/types.go b/test/e2e/types.go index b8c214434d..919b25f71d 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -49,3 +49,9 @@ type VerleroConfig struct { AddBSLPlugins string InstallVelero bool } + +type SnapshotCheckPoint struct { + NamespaceBackedUp string + SnapshotIDList []string + ExpectCount int +} diff --git a/test/e2e/util/providers/aws_utils.go b/test/e2e/util/providers/aws_utils.go index 3e64b2b3a3..3cddbef734 100644 --- a/test/e2e/util/providers/aws_utils.go +++ b/test/e2e/util/providers/aws_utils.go @@ -23,11 +23,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" + . "github.com/vmware-tanzu/velero/test/e2e" ) type AWSStorage string @@ -92,8 +94,10 @@ func (s AWSStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix } var backupNameInStorage string for _, item := range bucketObjects.CommonPrefixes { + fmt.Println("item:") + fmt.Println(item) backupNameInStorage = strings.TrimPrefix(*item.Prefix, strings.Trim(bslPrefix, "/")+"/") - fmt.Println(backupNameInStorage) + fmt.Println("backupNameInStorage:" + backupNameInStorage) if strings.Contains(backupNameInStorage, backupObject) { fmt.Printf("Backup %s was found under prefix %s \n", backupObject, bslPrefix) return true, nil @@ -139,3 +143,43 @@ func (s AWSStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPr fmt.Printf("Deleted object(s) from bucket: %s %s \n", bslBucket, fullPrefix) return nil } + +func (s AWSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { + + config := flag.NewMap() + config.Set(bslConfig) + region := config.Data()["region"] + s3Config := &aws.Config{ + Region: aws.String(region), + Credentials: credentials.NewSharedCredentials(cloudCredentialsFile, ""), + } + if region == "minio" { + return errors.New("No snapshot for Minio provider") + } + sess, err := session.NewSession(s3Config) + if err != nil { + return errors.Wrapf(err, "Error waiting for uploads to complete") + } + svc := ec2.New(sess) + params := &ec2.DescribeSnapshotsInput{ + OwnerIds: []*string{aws.String("self")}, + Filters: []*ec2.Filter{ + { + Name: aws.String("tag:velero.io/backup"), + Values: []*string{ + aws.String(backupObject), + }, + }, + }, + } + result, err := svc.DescribeSnapshots(params) + if err != nil { + fmt.Println(err) + } + if len(result.Snapshots) != snapshotCheck.ExpectCount { + return errors.New(fmt.Sprintf("Snapshot count is not as expected %d", snapshotCheck.ExpectCount)) + } else { + fmt.Printf("Snapshot count %d is as expected %d\n", len(result.Snapshots), snapshotCheck.ExpectCount) + return nil + } +} diff --git a/test/e2e/util/providers/azure_utils.go b/test/e2e/util/providers/azure_utils.go index d9ee4c6ca5..b89f690997 100644 --- a/test/e2e/util/providers/azure_utils.go +++ b/test/e2e/util/providers/azure_utils.go @@ -22,29 +22,47 @@ import ( "net/url" "os" "strings" + "time" "github.com/Azure/azure-pipeline-go/pipeline" + + disk "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-08-01/compute" storagemgmt "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage" "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/joho/godotenv" "github.com/pkg/errors" "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/util/sets" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" + . "github.com/vmware-tanzu/velero/test/e2e" ) type AzureStorage string +const fqdn = "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + const ( - subscriptionIDEnvVar = "AZURE_SUBSCRIPTION_ID" - cloudNameEnvVar = "AZURE_CLOUD_NAME" - resourceGroupEnvVar = "AZURE_RESOURCE_GROUP" - storageAccountKey = "AZURE_STORAGE_ACCOUNT_ACCESS_KEY" - storageAccount = "storageAccount" - subscriptionID = "subscriptionId" - resourceGroup = "resourceGroup" + subscriptionIDConfigKey = "subscriptionId" + subscriptionIDEnvVar = "AZURE_SUBSCRIPTION_ID" + cloudNameEnvVar = "AZURE_CLOUD_NAME" + resourceGroupEnvVar = "AZURE_RESOURCE_GROUP" + storageAccountKey = "AZURE_STORAGE_ACCOUNT_ACCESS_KEY" + storageAccount = "storageAccount" + subscriptionID = "subscriptionId" + resourceGroup = "resourceGroup" + + apiTimeoutConfigKey = "apiTimeout" + snapsIncrementalConfigKey = "incremental" + + snapshotsResource = "snapshots" + disksResource = "disks" + + resourceGroupConfigKey = "resourceGroup" + credentialsFileConfigKey = "credentialsFile" ) func getStorageCredential(cloudCredentialsFile, bslConfig string) (string, string, error) { @@ -63,6 +81,7 @@ func getStorageCredential(cloudCredentialsFile, bslConfig string) (string, strin } return accountName, accountKey, nil } + func loadCredentialsIntoEnv(credentialsFile string) error { if credentialsFile == "" { return nil @@ -160,6 +179,42 @@ func handleErrors(err error) { } } +func getRequiredValues(getValue func(string) string, keys ...string) (map[string]string, error) { + missing := []string{} + results := map[string]string{} + + for _, key := range keys { + if val := getValue(key); val == "" { + missing = append(missing, key) + } else { + results[key] = val + } + } + + if len(missing) > 0 { + return nil, errors.Errorf("the following keys do not have values: %s", strings.Join(missing, ", ")) + } + + return results, nil +} + +func validateConfigKeys(config map[string]string, validKeys ...string) error { + validKeysSet := sets.NewString(validKeys...) + + var invalidKeys []string + for k := range config { + if !validKeysSet.Has(k) { + invalidKeys = append(invalidKeys, k) + } + } + + if len(invalidKeys) > 0 { + return errors.Errorf("config has invalid keys %v; valid keys are %v", invalidKeys, validKeys) + } + + return nil +} + func deleteBlob(p pipeline.Pipeline, accountName, containerName, blobName string) error { ctx := context.Background() @@ -239,7 +294,7 @@ func (s AzureStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bsl for marker := (azblob.Marker{}); marker.NotDone(); { listBlob, err := containerURL.ListBlobsFlatSegment(ctx, marker, azblob.ListBlobsSegmentOptions{}) if err != nil { - return errors.Wrapf(err, "Fail to create gcloud client") + return errors.Wrapf(err, "Fail to list blobs client") } marker = listBlob.NextMarker @@ -256,3 +311,88 @@ func (s AzureStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bsl } return nil } + +func mapLookup(data map[string]string) func(string) string { + return func(key string) string { + return data[key] + } +} +func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { + + ctx := context.Background() + config := flag.NewMap() + config.Set(bslConfig) + if err := validateConfigKeys(config.Data(), + resourceGroupConfigKey, + subscriptionIDConfigKey, + storageAccount, + ); err != nil { + return err + } + + if err := loadCredentialsIntoEnv(cloudCredentialsFile); err != nil { + return err + } + // we need AZURE_SUBSCRIPTION_ID, AZURE_RESOURCE_GROUP + envVars, err := getRequiredValues(os.Getenv, subscriptionIDEnvVar, resourceGroupEnvVar) + if err != nil { + return errors.Wrap(err, "unable to get all required environment variables") + } + + // Get Azure cloud from AZURE_CLOUD_NAME, if it exists. If the env var does not + // exist, parseAzureEnvironment will return azure.PublicCloud. + env, err := parseAzureEnvironment(os.Getenv(cloudNameEnvVar)) + if err != nil { + return errors.Wrap(err, "unable to parse azure cloud name environment variable") + } + + // set a different subscriptionId for snapshots if specified + snapshotsSubscriptionID := envVars[subscriptionIDEnvVar] + if val := config.Data()[subscriptionIDConfigKey]; val != "" { + // if subscription was set in config, it is required to also set the resource group + if _, err := getRequiredValues(mapLookup(config.Data()), resourceGroupConfigKey); err != nil { + return errors.Wrap(err, "resourceGroup not specified, but is a requirement when backing up to a different subscription") + } + snapshotsSubscriptionID = val + } + // set up clients + snapsClient := disk.NewSnapshotsClientWithBaseURI(env.ResourceManagerEndpoint, snapshotsSubscriptionID) + snapsClient.PollingDelay = 5 * time.Second + + authorizer, err := auth.NewAuthorizerFromEnvironment() + if err != nil { + return errors.Wrap(err, "error getting authorizer from environment") + } + // // if config["snapsIncrementalConfigKey"] is empty, default to nil; otherwise, parse i + snapsClient.Authorizer = authorizer + snaps := &snapsClient + //return ListByResourceGroup(ctx, snaps, envVars[resourceGroupEnvVar], backupObject, snapshotCount) + req, err := snaps.ListByResourceGroupPreparer(ctx, envVars[resourceGroupEnvVar]) + if err != nil { + return autorest.NewErrorWithError(err, "compute.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing request") + } + + resp, err := snaps.ListByResourceGroupSender(req) + if err != nil { + return autorest.NewErrorWithError(err, "compute.SnapshotsClient", "ListByResourceGroup", resp, "Failure sending request") + } + result, err := snaps.ListByResourceGroupResponder(resp) + snapshotCountFound := 0 + backupNameInSnapshot := "" + for _, v := range *result.Value { + backupNameInSnapshot = *v.Tags["velero.io-backup"] + fmt.Println(backupNameInSnapshot) + if backupObject == backupNameInSnapshot { + snapshotCountFound++ + } + } + if err != nil { + return autorest.NewErrorWithError(err, "compute.SnapshotsClient", "ListByResourceGroup", resp, "Failure responding to request") + } + if snapshotCountFound != snapshotCheck.ExpectCount { + return errors.New(fmt.Sprintf("Snapshot count %d is not as expected %d\n", snapshotCountFound, snapshotCheck.ExpectCount)) + } else { + fmt.Printf("Snapshot count %d is as expected %d\n", snapshotCountFound, snapshotCheck.ExpectCount) + return nil + } +} diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index aa00065c90..f6549eea61 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -17,16 +17,21 @@ limitations under the License. package providers import ( + "context" "fmt" "strings" "time" "github.com/pkg/errors" + + . "github.com/vmware-tanzu/velero/test/e2e" + velero "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) type ObjectsInStorage interface { IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) (bool, error) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) error + IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error } func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { @@ -92,7 +97,7 @@ func IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix func DeleteObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { bslPrefix = getFullPrefix(bslPrefix, subPrefix) - fmt.Printf("|| VERIFICATION || - Delete backup %s in storage %s", backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - Delete backup %s in storage %s\n", backupName, bslPrefix) s, err := getProvider(cloudProvider) if err != nil { return errors.Wrapf(err, fmt.Sprintf("Cloud provider %s is not valid", cloudProvider)) @@ -103,3 +108,67 @@ func DeleteObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPr } return nil } + +func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { + fmt.Printf("|| VERIFICATION || - Snapshots should not exist in cloud, backup %s\n", backupName) + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.ExpectCount = 0 + err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix, snapshotCheckPoint) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s is existed in cloud after backup as expected", backupName)) + } + fmt.Printf("|| EXPECTED || - Snapshots are not existed in cloud, backup %s\n", backupName) + return nil +} + +func SnapshotsShouldBeCreatedInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, snapshotCheckPoint SnapshotCheckPoint) error { + fmt.Printf("|| VERIFICATION || - Snapshots should exist in cloud, backup %s\n", backupName) + err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix, snapshotCheckPoint) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s are not existed in cloud after backup as expected", backupName)) + } + fmt.Printf("|| EXPECTED || - Snapshots are existed in cloud, backup %s\n", backupName) + return nil +} + +func IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, snapshotCheck SnapshotCheckPoint) error { + bslPrefix = getFullPrefix(bslPrefix, subPrefix) + s, err := getProvider(cloudProvider) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("Cloud provider %s is not valid", cloudProvider)) + } + if cloudProvider == "vsphere" { + var retSnapshotIDs []string + ctx, _ := context.WithTimeout(context.Background(), time.Minute*2) + retSnapshotIDs, err = velero.GetVsphereSnapshotIDs(ctx, time.Hour, snapshotCheck.NamespaceBackedUp) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("Fail to get snapshot CRs of backup%s", backupName)) + } + bslPrefix = "plugins" + subPrefix = "vsphere-astrolabe-repo/ivd/data" + if snapshotCheck.ExpectCount == 0 { + for _, snapshotID := range retSnapshotIDs { + err := ObjectsShouldNotBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, snapshotID, subPrefix, 5) + if err != nil { + return errors.Wrapf(err, "|| UNEXPECTED || - Snapshot %s of backup %s exist in object store", snapshotID, backupName) + } + } + } else { + if snapshotCheck.ExpectCount != len(retSnapshotIDs) { + return errors.New(fmt.Sprintf("Snapshot CRs count %d is not equal to expected %d", len(retSnapshotIDs), snapshotCheck.ExpectCount)) + } + for _, snapshotID := range retSnapshotIDs { + err := ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, snapshotID, subPrefix) + if err != nil { + return errors.Wrapf(err, "|| UNEXPECTED || - Snapshot %s of backup %s does not exist in object store", snapshotID, backupName) + } + } + } + } else { + err = s.IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, snapshotCheck) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("Fail to get snapshot of backup%s", backupName)) + } + } + return nil +} diff --git a/test/e2e/util/providers/gcloud_utils.go b/test/e2e/util/providers/gcloud_utils.go index fdc01b0811..8f2c54870b 100644 --- a/test/e2e/util/providers/gcloud_utils.go +++ b/test/e2e/util/providers/gcloud_utils.go @@ -17,14 +17,20 @@ limitations under the License. package providers import ( + "encoding/json" "fmt" + "io/ioutil" "strings" "cloud.google.com/go/storage" "github.com/pkg/errors" "golang.org/x/net/context" + "golang.org/x/oauth2/google" + "google.golang.org/api/compute/v1" "google.golang.org/api/iterator" "google.golang.org/api/option" + + . "github.com/vmware-tanzu/velero/test/e2e" ) type GCSStorage string @@ -43,7 +49,8 @@ func (s GCSStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix for { obj, err := iter.Next() if err == iterator.Done { - return false, errors.Wrapf(err, fmt.Sprintf("Backup %s was not found under prefix %s \n", backupObject, bslPrefix)) + //return false, errors.Wrapf(err, fmt.Sprintf("Backup %s was not found under prefix %s \n", backupObject, bslPrefix)) + return false, nil } if err != nil { return false, errors.WithStack(err) @@ -97,3 +104,43 @@ func (s GCSStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPr } } } + +func (s GCSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { + ctx := context.Background() + data, err := ioutil.ReadFile(cloudCredentialsFile) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("Failed reading gcloud credential file %s", cloudCredentialsFile)) + } + + creds, err := google.CredentialsFromJSON(ctx, data) + if err != nil { + return errors.Wrapf(err, fmt.Sprintf("Failed getting credentials from JSON data %s", string(data))) + } + + computeService, err := compute.NewService(context.Background(), option.WithCredentialsFile(cloudCredentialsFile)) + if err != nil { + return errors.Wrapf(err, "Fail to create gcloud compute service") + } + // Project ID for this request. + project := creds.ProjectID + req := computeService.Snapshots.List(project) + snapshotCountFound := 0 + if err := req.Pages(ctx, func(page *compute.SnapshotList) error { + for _, snapshot := range page.Items { + snapshotDesc := map[string]string{} + json.Unmarshal([]byte(snapshot.Description), &snapshotDesc) + if backupObject == snapshotDesc["velero.io/backup"] { + snapshotCountFound++ + } + } + return nil + }); err != nil { + return errors.Wrapf(err, "Failed listing snapshot pages") + } + if snapshotCountFound != len(snapshotCheck.SnapshotIDList) { + return errors.New(fmt.Sprintf("Snapshot count %d is not as expected %d\n", snapshotCountFound, len(snapshotCheck.SnapshotIDList))) + } else { + fmt.Printf("Snapshot count %d is as expected %d\n", snapshotCountFound, len(snapshotCheck.SnapshotIDList)) + return nil + } +} diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 2dff360f54..70ebb0c1e7 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -19,6 +19,7 @@ package velero import ( "bytes" "context" + b64 "encoding/base64" "encoding/json" "fmt" "io" @@ -41,9 +42,8 @@ import ( veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) -const ( - BackupObjectsPrefix = "backups" -) +const BackupObjectsPrefix = "backups" +const PluginsObjectsPrefix = "plugins" var pluginsMatrix = map[string]map[string][]string{ "v1.4": { @@ -478,6 +478,36 @@ func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, return err } +func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace string) ([]string, error) { + checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", + "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.spec.resourceHandle.name}{\"=\"}{.status.snapshotID}{\"\\n\"}{end}'") + fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) + stdout, stderr, err := veleroexec.RunCommand(checkSnapshotCmd) + if err != nil { + fmt.Print(stdout) + fmt.Print(stderr) + return nil, errors.Wrap(err, "failed to verify") + } + stdout = strings.Replace(stdout, "'", "", -1) + lines := strings.Split(stdout, "\n") + var result []string + + for _, curLine := range lines { + fmt.Println("curLine:" + curLine) + curLine = strings.Replace(curLine, "\n", "", -1) + if len(curLine) == 0 { + continue + } + snapshotID := curLine[strings.LastIndex(curLine, ":")+1:] + fmt.Println("snapshotID:" + snapshotID) + snapshotIDDec, _ := b64.StdEncoding.DecodeString(snapshotID) + fmt.Println("snapshotIDDec:" + string(snapshotIDDec)) + result = append(result, string(snapshotIDDec)) + } + fmt.Println(result) + return result, nil +} + func getVeleroVersion(ctx context.Context, veleroCLI string, clientOnly bool) (string, error) { args := []string{"version", "--timeout", "60s"} if clientOnly { From 37412fe21429139abbffaf56943e6b140787958e Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 1 Mar 2022 10:59:39 +0800 Subject: [PATCH 056/366] Change authentication method from workload identity provider to service account key. Signed-off-by: Xun Jiang --- .github/workflows/push.yml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 5202a3b4e0..aa8fa9b0bd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,22 +6,6 @@ on: tags: - '*' -# id-token is added to enable OIDC token generation. All the others are copied from "Set up job" GITHUB_TOKEN Permissions -permissions: - actions: write - checks: write - contents: write - deployments: write - discussions: write - issues: write - packages: write - pages: write - pull-requests: write - repository-projects: write - security-events: write - statuses: write - id-token: write - jobs: build: @@ -71,8 +55,7 @@ jobs: name: 'Authenticate to Google Cloud' uses: 'google-github-actions/auth@v0.6.0' with: - workload_identity_provider: 'projects/298797809281/locations/global/workloadIdentityPools/velero-pool/providers/velero-provider' - service_account: 'gcraccount@velero-gcp.iam.gserviceaccount.com' + credentials_json: '${{ secrets.GCR_SA_KEY }}' token_format: 'access_token' # Use access_token generated above to login gcr.io From 860e4e2c1a9ab605493d2a54b0e5304a7a211a97 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 1 Mar 2022 15:16:52 +0800 Subject: [PATCH 057/366] Remove gcp auth action. Use JSON key as docker login secret instead. Signed-off-by: Xun Jiang --- .github/workflows/push.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index aa8fa9b0bd..bac9c2782d 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -47,23 +47,12 @@ jobs: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} ./hack/docker-push.sh - # actions/checkout MUST come before auth - - uses: 'actions/checkout@v2' - - # auth to GCP with OIDC token from GCP Workload Identity Provider. Output auth result to access token. - - id: 'auth' - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v0.6.0' - with: - credentials_json: '${{ secrets.GCR_SA_KEY }}' - token_format: 'access_token' - - # Use access_token generated above to login gcr.io + # Use the JSON key in secret to login gcr.io - uses: 'docker/login-action@v1' with: registry: 'gcr.io' # or REGION.docker.pkg.dev - username: 'oauth2accesstoken' - password: '${{ steps.auth.outputs.access_token }}' + username: '_json_key' + password: '${{ secrets.GCR_SA_KEY }}' # Push image to GCR to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. - name: Publish container image to GCR From 27f749419084c033339c1b8d351238af45544833 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy <20771501+a-mccarthy@users.noreply.github.com> Date: Wed, 2 Mar 2022 20:57:31 -0500 Subject: [PATCH 058/366] fix broken links in ibm config page (#4715) Signed-off-by: Abigail McCarthy --- site/content/docs/main/contributions/ibm-config.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/docs/main/contributions/ibm-config.md b/site/content/docs/main/contributions/ibm-config.md index 4aaf823a2f..b551bc1a73 100644 --- a/site/content/docs/main/contributions/ibm-config.md +++ b/site/content/docs/main/contributions/ibm-config.md @@ -89,7 +89,7 @@ If you run the nginx example, in file `examples/nginx-app/with-pv.yaml`: Uncomment `storageClassName: ` and replace with your `StorageClass` name. -[0]: namespace.md +[0]: ../namespace.md [1]: https://cloud.ibm.com/docs/cloud-object-storage/getting-started.html [2]: https://cloud.ibm.com/docs/cloud-object-storage/getting-started.html#create-buckets [3]: https://cloud.ibm.com/docs/cloud-object-storage/iam?topic=cloud-object-storage-service-credentials @@ -97,5 +97,5 @@ Uncomment `storageClassName: ` and replace with your `S [4]: https://www.ibm.com/docs/en/cloud-private [5]: https://cloud.ibm.com/docs/containers/container_index.html#container_index [14]: http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html -[15]: customize-installation.md#customize-resource-requests-and-limits -[16]: restic.md +[15]: ../customize-installation.md#customize-resource-requests-and-limits +[16]: ../restic.md From bcc9aa8d69393d5a070fd278a77b99d402c7ee13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 25 Feb 2022 21:41:43 +0800 Subject: [PATCH 059/366] Update BSL CR as long as there is any error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Mark the BSL as "Unavailable" when gets any error 2. Add a new field "Message" to the BSL status to record the error message Fixes #4485 Fixes #4405 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4719-ywk253100 | 1 + .../velero.io_backupstoragelocations.yaml | 4 ++ config/crd/v1/crds/crds.go | 2 +- .../velero/v1/backupstoragelocation_types.go | 4 ++ .../backup_storage_location_controller.go | 68 +++++++++++-------- 5 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 changelogs/unreleased/4719-ywk253100 diff --git a/changelogs/unreleased/4719-ywk253100 b/changelogs/unreleased/4719-ywk253100 new file mode 100644 index 0000000000..96f584864a --- /dev/null +++ b/changelogs/unreleased/4719-ywk253100 @@ -0,0 +1 @@ +Mark the BSL as "Unavailable" when gets any error and add a new field "Message" to the status to record the error message \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml index 24ac9e9450..002126d046 100644 --- a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml @@ -158,6 +158,10 @@ spec: format: date-time nullable: true type: string + message: + description: Message is a message about the backup storage location's + status. + type: string phase: description: Phase is the current state of the BackupStorageLocation. enum: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index b23a26318c..cb45808fcb 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,7 +30,7 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd6\xe3E\x84\xdd\xcc\x19;\x8e\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe03MQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>n\x9e\xff\xf1K\xef3@\x8e63\xb2rL\x14\x8f\x18H\v\x02\x9eyY`\x02\xf9\xc1\x1d\x84\x03\x83\x95A\x8b\xcaYp\a\x84LT\xae6\bz\a?\xd7[4\n\x1d\xda\x064@V\xd4֡\x01\xeb\x84C\x10\x0e\x04TZ*\aR\x81\x93%\xc2\x1f>>n@o\xff\x82\x99\xb3 T\x0e\xc2Z\x9dI\xe10\x87\xa3.\xea\x12\xfd\xd8?\xae\x1b\xa8\x95\xd1\x15\x1a'#\x9d}\xebHU\xe7\xeb`y\x1f\x88\x02\xbe\x17\xe4$N\xe8\x97\x11\xa8\x88y \x1a\xad\xc7\x1d\xa4m\x97\xcb\x12\xd2\x03\f\xd4I\xa8\x80\xfc\x1a\xbe\xa0!0`\x0f\xba.r\x92\xc2#\x1a\"X\xa6\xf7J\xfew\x03ۂ\xd3\xa3\xc1`u\x0fg\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:\xf7\x80\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŷ\xc0;p\xa6\xc6\t\xca\bc\xc4i\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc9F\xf0{&\xcaA\xeb\x97%B\xfc\v\xf5i\xf5\x1ed\xec%\xc1\x16\x0f\xe2(\xb5\tK\x0ffh\x8b\x80_1\xab\x1d\x8eɿp\x90\xcb\xdd\x0e\r\xc1\xa9\x0e¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xd9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ʼ\x16\x05He\x9dP\x99_\x8fh\xf0:_\x0f\xcc1\xf9\fg\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cw\x1dZ\xb0\xb6M-{+H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x99\x04\xddp\xc4\xfb\x12\x85\xd8b\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xebAf\ao\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5ij\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\xff'a\xa3\xda\u007f\x83\xd0nΆ\xbe\xaf\xd0\x12I%\xb9\xf3\x9b\x1d`Y\xb9\xd3\rH\x17\xbf.A$g\xa5\x9d\xff\xef\x981\x97K\xfcf8\xf2]%~\x96+K\x10\x89+\xcd\xf4\u007f\x87Lac\xf1%؊d\x86\xfc\xd2\x1du\x03r\xd70$\xbf\x81\x9d,\x1c\x9a\x01g~\xd3~y\x0fb\xa4\xd8;j\xa5p\xd9\xe1\xe1+y^\xb6M8%\xd2e8\xd8\xfb\xafџ\xef\x1b\xe6\x05\xb8\xc0\x01\xaa4X\xfa\xc0\xf7\x89\xa9\xd9~a\x8f\xea\xe3\xe7O\x98ϑ\a\xd2$\xefl!\x1f\a\xc8v\xa7\x0eNy\xea2\x82\xeb\xd3\xc47>\xa5q\x03\x02^\xf0\xe4=\x16\xa1\x80\x98#h\xa2\x89H\xe7\x9c8\x9c[a!{\xc1\x13\x83\tɒ\xc5ѩ\xa2\xe0\xdb\v\x9eR\xba\r\bH8I\x1b\x92@DI\xfa\xc0\x84\xe0\xd8:\x9dx\xc0\x89\xaf\xa8\x8b\x96\x17\a\xe9\x8a$\xb6H\xfb7,\xb3a['iȌ\xfd`=\x8bh\x17\x1cd\x95\xb8P2s`\x91wKL}=\x8bB\xe6\xcdD^\xee7j\xda\x1b\xee\xb7\xcf\xdam\xd4\r<|\x956d\x1f?i\xb4\x9f\xb5\xe3/߄\x9c\x1e\xf17\x10\xd3\x0f\xe4\xed\xa5\xbc\xda&:tsh\t\xc2\xed\xdb\xc6Gx\r{\xa4\x85\x8d\xa2\xb8%Ѓ3\xa2~\xbay\xfb\xd0oem9I\xa6\xb4Z\xb1\xa9\\\x8f\xcd䉝\bR\x9b\x1eG\xceQk&\xf5\x13&\x82}\"K\xe2\xc7\xfb\x1co!2\xcc!\xaf\x99\x98\x9c\x99\x14\x0e\xf72\x83\x12\xcd~\xcept[E\xfa=\r\x85D\xad\xebۅ\x12\x96f\xdac\v\xaa;_FfE;7\xa1Wd\xf6b\u05c9\x84\xe4t\xd7\xe5\x15\xb1\x89e\xffc\x91\xba\"\xcf\xf9,I\x14\x8f\x17h\xfc\vxqn\xfb=b\xdeB\x96\x82\x93\x8c\xffCf\x8e\x05\xfa\u007f\xa1\x12\xd2$\xec\xe1\x8f|4T`ol\xc8bu\xa7\xa1\x19\xa4\x05\xe2\xefQ\x14\xe7\xa9\xee\x91\xc5i\xd2-XxC\xaewg\x1e\xcb\r\xbc\x1e\xb4\xf56u'q4\xa5\xdao\xd2\xc2\xf5\v\x9e\xaeo\xce\xf4\xc0\xf5F]{\x03\u007f\xb1\xbai\xbc\x05\xad\x8a\x13\\\xf3\xd8\xeb\xdf\xe2\x04%JbR7>\x82Ku\x95)\x96\x8c\x9e\x00\rlΝ\xc8͝\xc3:I\x0e+m]2*\x8f\xda:\x9fY칥\x97d\xb1\xc0\xcbP\xc8^\x81\xd8\xf9\x93?m\xe2\x99\x0e\xa9\xbdA\u0095\xb8f\xe75,\xb1\xb1Ɉy\xa0\x14X]\xb7;\xd8\xeb\xd3k\u007f\xd0Ó\x88\x8c\x9d\x8bE\xb8\x95\xd1\x19Z;/\"\t\xdaz!I\xd8$\b\x85\x0f`\xfc\x81\xc9|R2\xb6t\x87\x94\x88t\xa1+\xff\U000354fd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9cԃ1o\n\x9c\xfe\xecGv\x12Y\a\xfd\x1a\xcfW'\x0f3\xc7\x1a\x1f\n!\xc8\x1dH\a\xa82]+N\xbf\xd0V\xe7)<\v\xbc\x82N&Y\x9a\x82\xa0\x86\xaa.\xd3\b\xb0b\xa9\x93j6O\xd3\xed\xfe\x93\x90ŷ`\x9b\x93%\xeaz\xd6p\xb6\xadǶ'?\xb2wP^\x8a\xaf\xb2\xacK\x10%\x91>5\xec\xd9\xf9\xe2\x98\x1e\xc7\xe1UHǖ\x83\xe0\xb2\x19q\x9a6UU\xa0Kݑ[\xdci\xc3\xfb\xd9\xca\x1c\x1b\xc3\x1c\xa4@+\x10\xb0\x13\xb2\xa8M\xa2\x86\xbc\x88\xb6\x97\xc4\x1aAY\xbc_\x10\x916\xf9\x8aI\x91\x90\x88Mt\x16\xe7\xb5ue\xd2]\xc5G\x83i\xee\xd9RR:\xbag\x95\x91$K\xfa\xbd=\xb4 bB\x9d\xbe\xbbhg\xed\xbb\x8b\xb6о\xbbh\x93\xed\xbb\x8b\xb6ܾ\xbbh\xa1}w\xd1b\xfb\xee\xa2}w\xd1\xe6\xba\xcdi\xeb%\x8c|\xc5\xfdď\x8bX$\x1cOϡ8\x03?TS\xdc\xfb\xea\xfb\xd4\n\xcb\xcd\xf8\xa8\x91\xba\xdaPֿ\xe2\x1b\tc\x12\xd0\x16]\xb4\xa6\xa4)\xb9\xa4\r\x12\xc5\xdb\x17\x10/\x14a&\x95S\x8eWߦ\x14\xfc,\x95\xf9\xf4\xebL\x9b2\x9bXh\xaa\xe3$#t\x887\x1b\xc8\xed\xed\u0590\xf4\xebu\xd8ύ\x98\xfe\xcdkP\x13Jq\x16\np\xe6\vs\xe7\xe85\b=\xfa\x043\xbd\x82\xd1\xdf\r\xbd\x16\xaad\xa6kc\xc2I\x10:q\xfca\xdd\xff\xc5\xe9P)\x03\xaf\xd2\x1dF\x96\xf2z@\xc5gXj\xdf-{\x8d\xf2\x16\xae\x98\f\xe9\bڀ\x92\x05\x93sFZ{\xe4\x85?W>\x84\xbbx_·\x1fi\xb54o\xae\xa0\xe9W\xc8L\xa8\xe8K\x8f\x8c\xd2\v\x85\xd3kd\xe6\x8bZ.\xa9\x8c\x19ֽL\x02]\xae\x87I\x89\x1c\x17j_\xdeP\xf1\x92X\xed\xf8\x9b\x0f\xc6RjZ\xdeTɲX\x10\x98X\xbfүL\x99\ayA\xd5J\x12q\x96+T.\xaeK\tu \xb3\xebH\xaeF\x19\xa93\x99\x05\xf0\xb4\xaaY\xac\x06Y\xf4\x91\xe7\xf1[\xac\xf7\xb8\xa4\xcac\x91bo\xac\xe8h*6&潴\x8e\xa3_\xa71\x014\xa5zc\xa2:c\x02\xe2l\xcdFjM\xc6\x04\xec\x05\xb3;+%3?\x8e_\x84\x84E\xfbV\xfc\xb5$\xea\xad\v\xd3&G3롧\xa29\x8bb?\xe35\x98sPf\x1f/NR\xaf\xae\xd7?\xc6rݔ\x84g\xf0\xb3T\xb9\x97\x13\x12\xf4\x8e\x9f\xc0\x17\x85\xb9(\xa6qWZ\u007fo\x1c\xe8 ҰX\t\xc37ɷ'\x9f\xad\xb0kx\x10١\xdf\x11\x0e\xc2RLZ\x8e\xbaa\xd7M\x98v\x1bGї\xeb5\xc0O\xba\x89\x84\xbb\u05ec\xac,\xab\xe2\x04\xb5E\xb8\xee\x0fy[\xcc1*\x01V\x89\xca\x1et\xbc\x03\xbb\x10v|\xe9\xf7\x1e\x89\xe8\xe3\rج\xd0u\xde@\x9f`\x9eP'x|f߇\xef\x0ef\xed=\xca\xe0\xdf\xc4Hbx\xcd\xf2\xc7\xf7\x8f\xf0\xad\xd3F\xec\xf1\x17\xed/#/Q\xa2\u07fbw\x13=谘q\x8b\x05Yb\x84\b\xe1Z\xf4\x00X\x9bH\x0f\xbb\xa1M~\x10\x96c\xeamf\xff9W,,\xe6\xe9\xe9\x17\xbf\x00'K\\\u007f\xaa}6eU\tc\x91\xa8\x19\x17\xe6\am\xe9\xbf\a\xfd:\xa6\xf0tX\xf3\x8fC\xbc\rr\xb2\x9e\x936\x17a\xef\xafMG\xc1\x8b$Z\x12\xd4\xe7\xf1Q\x9d@\xaf\xc3$\xbf\xcb\xf5ع\xc4\x14\x9c\xce\xeb\x12\x14X\xfbb\xbb\xf7\xbd\xfb;\xe5\xb1Lݿw\xc2\xd5v\xf9\x06>w\x8b\xefm\x84#\x9f\xda\xf0\xc5]\x0f\xc2_t}\xd3%\xfc\x90\xa1\uef412ϧ\xfb\xf3\x11\xfc҅\xc9=j\x9c\x1bonӿ\n\xdbd\xc1GM|\vΏd\x0f\x9a\xa0a\x0exD\x05Zqқ\xaf\xc4\xfa\xd7X\x86cƒI\x1d(!\xab^W\x85\x16y\xdc\xe1\xd1f\x85\x17<\x9eX\u007f\x99#\x9a\x0fv\x06&\xbf\x18\xb0\xd3f\x8c\b\xe7\n\xd3\x1b\x96;ȅ\xc3\xd5(\xd0$\xdd7*l|>\xb4xo\x9e;\xf9\x1dćK\xf1q\x03\u007f\xbaT\xa2\xb5b\x1f/̿\x92\x02ۣB\xb6\x9b#\xeb\x0f\xf1L{\x1aѿ.\xeeS*\"s\xb5\b\x13\xc4lR\xa7ׇ1\xb3R\xe8=\xecd\xc1]\xc3\xd3\x1eA\xb3O\xa9\x1d\xa9\x1c\xeeq\xe8\xae\xe2\xd7J\x9a\x14K\xf0\xd0t$\xdap>\x8d\xb5A\xfb\x04\x0e\x16r/I\x8d\x12\xb3\xf7\xc2l\xc5\x1eW\x99.\n\xe4҅s\xbc\xbe%\xaf=\xec\xd1'nΖ\xf6S\xb7o\xf4\xa7\x82\xb0{8\xf1ś\x9b`\xa1\xc7\x1d\xd4R\xfcE\x9b\x1b(\xa5\xa2\u007f\xc8\r\xe3\xc84\x0e\xbe\xc8\x1e\xf0\xeb\x02\vx?R\x9f\xe6@\xbb\xa3\xdd0\x8aٔ\xff0~\x88\xb9\x82\xcfxn\xee\xfc\xb9$\xe6\x9c{\x19{ׇ\xbalԣ\xd1{\n\x9dF~\xbc\x8f\xbal\xe4\xb7Ga\x9c\x14Eq\xf2\x93L\xce>\xf2\xc3'$m2iR\xc6\xc9\x1a\xb0\\\xa2l\xe8\xd6\xc6iRyI\xe0\x13ŭ\xae]o\x83\xb6\x1b|D,\xe2\x9ck\xf8\xac\x1d\xc64\x9e\xec\xc3$\xbb\x8a֭p\xb7\xd3\xc6\xf9\xb0p\xb5\x02\xb9\v&j\x04.ixN]\xfbgv@\xba6}\xd2J/{\x9f\x06\x85e\xe9u\xfc\xd8\x0f\x9f.\x89,#\x0f\bo\xad\x13ň\xd6\xf8M\xd9j\xf6\x05H\xfa0\xff\xb7\x11\xe3xF\xf0M\xb7\u007fS\xe2_\x97[\x1f\x1d18O9>\xfd\xf7\x1a\xb3\x98ʣl\x11\x15\xbc\x1a\xe9\x1ci\xa9nn\x1f\x1c饢\x00\xaba'&\x1et\x98ӗ\xfc\xbbv\xa2\xd8L\xe7\x94\xfaNg\xd39.\x8b\x87\x9f/N\x13[\xb6L\x82\x89e\xf9\xea2i\xe3Xbev\x10jOBet\xbd?D\xb9\x9c\xb07S\x99\xf8\x9a\x90\x82\xaa\xa8\xf7$\xea!7\xeej\xa3:qqȖ\xe7\x1dtE\xf62\x89i\xc8\x0eƧ\xden\xc3\x13\r\xab\x9d\xd1\xe5*\xf0\x82\xf3\x067!^5R\x93SF\xd1\xd5\x04\xd0\xf6.4\x8bAU\xa1\x02a\x03>\t\xa5o\xf3l\x9d\v\x1e\x9d0.\xd5\xd5\xfb\xd2\xeb\xbc\xe0\xe51\xe4q|\xbf\x84hܗ\x00\xde\x0f\x1fݣ\xb8Y\xc5W\xe6|\xb4\xefE\xc1\x92\xf3g\x90\x03\xa8\xd1\xf3\x8a3\xb7\xad\xe7\xa4\xf5\xd1\xff\xeb\xfag\xc7\xc6\xc2<\xa4xjσ\xee\x83sT\xda\xe5-\xc4\xe0]\x8d\xd0\xe3\x0fr\xe7\x0fR2\xc2\xfa\x8f\u007f\xf3\xf3\xd1c\x92\xcf\xf2a\xd6]aO\xa4\xf1;\xe0\x13V\x063ڽc\xcbx,\x90\xfc\b\x8b\xd8\xf7\x84>\\\xe4H\xf6\x03X\xfb\xd19,\xab\xd1\x19g\"\xd8vؔ\xb2\x14\xb1\xc3\xc8B\xe2\x03\x88\x11X(-\x9a\tY/XP\xe3\xc4\\\xb6\xa0f\xd8Ԃl\x9d\x91\xd2\xda\xd5\xe3欉\x03\xdfyu\xaf\xc2(\xa9\xf6K{\xec\xdfC\xb7\x91x(@\x18\x89\x88F\x96\xd1\xc4H\x8b\x11Q' \x8a8N\xbcK6\b\x92\xde)$\x1a\xb5\x03g\x1fY\x81杽\x1df\n_\xda,\x85\xc82$q\xfd<|\xe8\xf4\xfa\x9a\xff\x88o\x99\xf2\x9f\x99V\xde\xdc\xda;\xf8\x8f\xff\xbc\x82\x90\x06{\x8e\x8f\x96\xd2\xc7\xff\v\x00\x00\xff\xffy\x8fd\x10\x14V\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YK\x8f\xe3\xb8\x11\xbe\xfbW\x14f\x0f}\x19˳\xc9!\x81/\x81Ǔ\x00\x83\xf4l7Ɲ\xce!\t\xb04Y\xb2\xb9M\x91\nI\xd9\xeb\x04\xf9\xefA\xf1!ɒ\xdcv\x0f\x92]^\xba\xcdG\xb1\xea\xab75\x9b\xcf\xe73V\xcbg\xb4N\x1a\xbd\x04VK\xfc٣\xa6_\xaex\xf9\xbd+\xa4Y\x1c\xbe\x9f\xbdH-\x96\xb0n\x9c7\xd5Wt\xa6\xb1\x1c?a)\xb5\xf4\xd2\xe8Y\x85\x9e\t\xe6\xd9r\x06\xc0\xb46\x9eѴ\xa3\x9f\x00\xdcho\x8dRh\xe7;\xd4\xc5K\xb3\xc5m#\x95@\x1b\x88\xe7\xab\x0f\x1f\x8a\xdf\x15\x1ff\x00\xdcb8\xfe$+t\x9eU\xf5\x12t\xa3\xd4\f@\xb3\n\x97\xb0e\xfc\xa5\xa9\x9d7\x96\xedP\x19\x1e\xef*\x0e\xa8КB\x9a\x99\xab\x91\xd3\xd5;k\x9az\t\xddB\xa4\x90؊\"}\f\xc46\x91\xd8}\"\x16֕t\xfeϗ\xf7\xdcK\xe7þZ5\x96\xa9Kl\x85-no\xac\xff\xa1\xbbz\x0e[\xa7\xe2\x8aԻF1{\xe1\xf8\f\xc0qS\xe3\x12\xc2\xe9\x9aq\x143\x80\x84Y\xa06\a&D\xd0\x02S\x8fVj\x8fvmTS\xe9\xf6.\x81\x8e[Y\xfb\x80r\x94\x05\x920\x90\xa5\x01\xe7\x99o\x1c\xb8\x86\xef\x819X\x1d\x98Tl\xabp\xf1\x17\xcd\xf2\xff\x81\x1e\xc0O\xce\xe8G\xe6\xf7K(⩢\xde3\x97W\xa3\x8e\x1e{3\xfeD\x028o\xa5\xdeM\xb1tϜ\u007ffJ\x8aV\xeb \x1d\xf8=\x82b\u0383\xa7\t\xfa\x15\x11\x02\x82\b!#\x04G\xe6\xd2=\x00\x87H%`4ͩ\x1a\xddu\xc66\xb1\x02\xcf\x03*\x91\u007f\x9aI\xdc\xf7\xc8f\xc3/FF{Fw\xb5\xc3K\xc4Π\xf8\x84%k\x94\xef\x8bJZR}\xbb<\x17\xabF^\x88x\xea\xec\xc6Ogs\xf1֭1\nY\xa4\x12w\x1d\xbe\x8fV\xc8\xf7X\xb1e\xdaljԫ\xc7\xcfϿݜMÔ!\r\x9c\x82\x14\xc7z\xba٣Ex\x0e\xfe\x17\xf5\xe6\x92h-M\x00\xb3\xfd\t\xb9\xef\x94X[S\xa3\xf52;K\x1c\xbd ՛\x1d\xf0tGl\xc7] (:a\xb4\xa3\xe4/(\x92\xa4`J\xf0{\xe9\xc0bmѡ\xf6}x[\xc6J`:\xb1W\xc0\x06-\x91!_n\x94\xa0\xa0v@\xeb\xc1\"7;-\xff\xd5\xd2v\xe0M2^\x8f\xce\x0fh\x06\xff\xd4L\x91\xa96\xf8\x1e\x98\x16P\xb1\x13X\xa4[\xa0\xd1=za\x8b+\xe0\vٻԥY\xc2\xde\xfb\xda-\x17\x8b\x9d\xf498sSU\x8d\x96\xfe\xb4\bqVn\x1bo\xac[\b<\xa0Z8\xb9\x9b3\xcb\xf7\xd2#\xf7\x8d\xc5\x05\xab\xe5<\xb0\xaecЬ\xc4w6\x85sww\xc6\xeb\xc8k\xe3\bQ\xf3\x15\rPČV\x10\x8fF):\xa0i\x8a\xd0\xf9\xfa\xc7\xcd\x13䫃2\x86\xe8\aܻ\x83\xaeS\x01\x01&u\x896*\xb1\xb4\xa6\n4Q\x8b\xdaH\xed\xc3\x0f\xae$\xea!\xfc\xae\xd9Vғ\xde\xff٠\xf3\xa4\xab\x02\xd6!c\xc1\x16\xa1\xa9\x83\xdf\x17\xf0YÚU\xa8\xd6\xcc\xe1\xff]\x01\x84\xb4\x9b\x13\xb0\xb7\xa9\xa0\x9fl\x87\x9b#j\xbd\x85\x9c\v/\xe8kҋ75\xf23\xff\x11\xe8\xa4%\v\xf7\xccc\xf0\x8b\x01\xae\xc9\xc5/'\xd3<\xa6\x9d\x9b\x06\xe3\x1c\x9d\xfbb\x04\x0eW\x06,\xafڍg<\xd6h+\xe9BZ\x84\xd2\xd8a\xc6`m\x04\xee\x8f\x1c\xa9\x8a\xd1\x1a\xea\xa6\x1a32\x87\xaf\xc8ăV\xa7\vK\u007f\xb5ҏ/\xba\xa0H\x1a\x91\xc5\xcdI\xf3G\xb4҈+\xc2\u007f\x1clo!؛#\x94\xc1\xac\xb5W'\x8aA\xee\xa4\xf98\xda\xe6\xb1z\xfc\x9c#ot\xa0\xe4o\t\xab\x02V\xc9sM\t\x1f@HG\x05\x80\vD\xc7`QyF\xebK\xf0\xb6y\x93\xf8\xdc\xe8R\xee\xc6B\xf7k\x9aK\x16s\x85\xf4\x00\xb9u\xb8\x89B\x13YGm\xcdA\n\xb4s\xf2\x0fYJ\x9e8il\xcc\\\xa5D%\xdcX\xd2\v^\x16D\xb1(ȫ\x99\xba\xa2\xc3u\xbb1\x94\xc6L\xeah\xc1\x1d\x81\x10ll\x95R\xaa\xf6\xa8E[\x8d\x9cqcB\xd4r(\xe0(\xfd>\x86C5\xe5w\xf0\xaa\xef\xd1x\xc1\xd3\xd4\xf4\x80\xf7\xa7=\xd2Θ@\x11\x1cr\x8b>X\x1b*2\x1f2\xa5\x02\xe0K\xe3B@\x1dƉ\x86\x8d\x19\xf0\x9a\xf9=H\xed\xa4@`\x13\xf0\xc7b킠m\xfe\u007fHQ\xe4\x1b\xd4\xf3\x9a\xb7Gv\xde\xe2\xf0\x19\xe3+\xfe\U000d8db5(\xe4\xdf)\xf2\x9fׂ\x97\xfcxR\xa2C\xfb`\xf0\xa7Xa\xf1\x89Ty\xc6\xcc\xf3\xf8\xc4+\x95Z~\xb6\x98rf\xaa\v\x8c\xb5\xe8j\xa3\x055O\xb7\xd5i\x1d\xcb\xff\xbbjmZ\xad\xf3\xf3(7X\xcbZ\xb8\xa9U\tO4onV\xe2\xc3U\xbf\x150[G\x9dbׯ\fd\xfcEڔw\xbd>\x85\xfaa\r\x8d\x0e\x95Z\xc8\xf8\x05\xfc]\xc3'\xeam);\x89%\xf1m\xa7\f@:\xd0\xe6H\xc7{\xf4\x02\t0:\xe6k\xea֘\x16\xa9\x19\x0eKG\xa9\x14el\x8b\x959Lfl*4-\xaa\x130G\xa6s\xf8M\xf1\xa1x\xf7\xabuA\x8a9OM\r\x8a\xafx\x90\xe3W\x9e1\xba\xf7\xa3\x13\xd9\xf1[w\xa0\x1f?\xe6fyaӶ\x1f'\xc0(\xa5\xa2Zp\"Nt\x15\xc3\xf8=\xf2\xe3\xe6\xfe΅\x12\x1e\xb5\x9f*\xfb\x8eh1tL(\xa8\x8a7\xe9]\xa2q\x1e\xed\x84\x01\xb4\xda\v:\ae\xf4n\xe08q\xa4W\n\xaaТA\x19\v\x02=\xa5&\xbd\x03\xbegz\x87\xdd+T\xe2\xffuN\xc9|\x066\xd3Y\x88ԗ\xcc\xe3&\x8d>ɩ2}\xf4\x02\xdcm\x9e~\xfd\xcd\xdcg\xcd^ls\xae\xe0>ڟ\xb34\x81:\xf7\u074bp7\xbe\xbd\xbd\x1d?7߀\xc4[\xdf\xc2_y׀#sݫ\xf8\xaf\x87C\xf8@p-\x81Ӟ,-o,\xb5i\u074bSp\xb8\xa9\xb8}\xfb\xd3\xcdj\xf0\x1d\xa3\xbf6\xfe\xcaq\x83\\\x93yl4\x19sQ\x0f\xb3\x14Z\xfa3Ͷ}\x85͜\xa7l\b\xff\xfeϬK\x8c\x94}j\x8f\xe2\x87\xe1g\xacw\xd19\xf3\xb7\xa8\xf0\x93S\xc5\x10\xbf\xc3\xc1\xdf\xfe1\x8b\x17\xa3x\xce\x1f\x8fh\xf2\xbf\x01\x00\x00\xff\xffHX\xbeK\x01\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_o\x1b\xb9\x11\u007fק\x18\xf8\x1e\xdc\x03\xbc\xd2ݵh\v\xbd]\xec^\xe1\xf6\xce1\"7/A\x1eF\xcb\xd9]\xd6\\\x92%\xb9RԢ߽\x18\x92\xab?\xab\x95d\x1bH\xba/\x89\xc9\xe1p\xfe\xcfo\xa8IQ\x14\x13\xb4\xf2#9/\x8d\x9e\x03ZI_\x02i\xfe\xcbO\x9f\xff\xec\xa7\xd2\xccV?N\x9e\xa5\x16s\xb8\xed|0\xed\a\xf2\xa6s%\xddQ%\xb5\f\xd2\xe8IK\x01\x05\x06\x9cO\x00Pk\x13\x90\x97=\xff\tP\x1a\x1d\x9cQ\x8a\\Q\x93\x9e>wKZvR\tr\x91y\u007f\xf5\xea\x87韦?L\x00JG\xf1\xf8\x93l\xc9\al\xed\x1ct\xa7\xd4\x04@cKs\xb0F\xac\x8c\xeaZZb\xf9\xdcY?]\x91\"g\xa6\xd2L\xbc\xa5\x92/\xad\x9d\xe9\xec\x1cv\x1b\xe9l\x16()\xf3h\xc4\xc7\xc8\xe6]d\x13w\x94\xf4\xe1\xefc\xbb\xbfJ\x1f\"\x85U\x9dCu,D\xdc\xf4RםBw\xb4=\x01\xf0\xa5\xb14\x87\a\x16\xc3bIb\x02\x90u\x8fb\x15Y\xbbՏ\x89U\xd9P\x8bI^\x00cI\xff\xfcx\xff\xf1\xf7\x8b\x83e\x00\xeb\x8c%\x17d\xafZ\xfa\xf6<\xba\xb7\n ȗN\xda\x10\xed}\xcd\f\x13\x15\bv%y\b\r\xf5B\x91\xc82\x80\xa9 4҃#\xebȓN\xce=`\fL\x84\x1a\xcc\xf2\x9fT\x86),\xc81\x1b\xf0\x8d\xe9\x94\xe0\bX\x91\v\xe0\xa84\xb5\x96\xff\xde\xf2\xf6\x10L\xbcTa\xa0l\xe1\xdd'u \xa7Q\xc1\nUG7\x80Z@\x8b\x1bpķ@\xa7\xf7\xf8E\x12?\x85ߌ#\x90\xba2shB\xb0~>\x9b\xd52\xf4\x91\\\x9a\xb6\xed\xb4\f\x9bY\fJ\xb9\xec\x82q~&hEj\xe6e]\xa0+\x1b\x19\xa8\f\x9d\xa3\x19ZYD\xd1u\x8c\xe6i+\xbes9\xf6\xfd\xf5\x81\xacaþ\xf5\xc1I]\xefm\xc4@;\xe3\x01\x0e5\x90\x1e0\x1fMZ\xec\f\xcdKl\x9d\x0f\u007fY[S\xe1\x87`F$[&\x17\x90\x80a@\xc0٠\x803U}T\xe2\x9f\x1f\xef\xfbJ\xde\x1b1\xcb\x1e\x8e\xef\xbd`\x1f\xfe*IJ\xbc\x9ft;!S\xe69Y\xd7\xe4\"p\x19\xfbbS\xe1R\xfd=\x18\xc7\x16\xd0f\x8fEd\xcc\xdeK\x85\x92đП~\xfa|R\xe2C{\x81Ԃ\xbe\xc0O u\xb2\x8d5\xe2\xfb)<\xc5\xe8\xd8\xe8\x80_\xf8\xa6\xb21\x9eNY\xd6h\xb5a\x9d\x1b\\\x11x\xd3\x12\xacI\xa9\"\xe1 \x01kܰ\x15z\xc7q\xbc!Xt\xe1l\xb4\xf6\xe8\xe7\xe9\xfd\xdd\xfby\x92\x8c\x03\xaa\x8e\x95\x98\xbbf%\x19\xcd0\x8cI\xbd8F\xe3Q3\xef?ߥ\xf0\t\x06\xca\x06uMI_\x82\xaa\xe3\xee8\xbd~K\x1e\x1fC\x92\xfe\x1b\x81&\xc3\xc2\xf1\u007fk\xee/T.\"\xe8\x17(\xf7\xb0\x17\xe5g\x95\xe3Y\xc5i\n\x14\xf5\x13\xa6\xf4\xacZI6\xf8\x99Y\x91[IZ\xcf\xd6\xc6=K]\x17\x1c\x9aE\x8a\x01?\x8b\xe3\xc6\xec\xbb\xf8ϛu\x89\x83\xc2K\x15\x8a\xc4\xdfB+\xbe\xc7\xcfޤT\x8fa_\xdeǮ\x17\x19Y\r\xcfrZ\xac\x1bY6\xfdp\x92k\xec\x89d\x92\x8c\x84E*ͨ7_=\x94٠\x9dc\x896E\x1e\x80\vԂ\xff\xef\xa5\x0f\xbc\xfe&\vv\xf2E\xe9\xfb\x8f\xfb\xbbo\x13\xe0\x9d|S\xae\x9e\x00\xe0)F\xac\xb9\x17l\xcaJ\x92\xbb\x00\xcc>\x1c\x10\xf7\xd0q\x04\xb1ni^\x85\f\x03\xd6#P\f\x85\x88\xcf\x1e\xa8\x1e\xcf\x02\xb6\xb3\x168P\xe3\tk\x0f\xe8\b\x10Z\xb4\xec\xb9g\xda\x14\xa9\xc5[\x94ܟ\xb9\x05g̳$@k\x95\x1cmŹ\x91g\x10\x9a\xf1>\x0f\xdaX\xfbS\xba\x8f\xfa!q\xb8`\xff4\xe0\x8cA\xf6,@\xc27[\xd8\x1e\f,\xc7R\xf4\f(>iE\x9eK\x19\xad\x1d\x8aX\x8c\x0fP\x03\x1a\x1e(\x06Kֈ\xc1\xcaa$\x0e6\x93~/\x9a*\x03\x86οb\xae\x8c\xf4\xbdMS\x15\t\x99K\x84\xd0o\x9d,K\xc3\xe8\xf4\xf0i\xed\xbc{o\x8fO\xc4G\x1c'\x92pA\xb6\x1c\xb39\xca\xd6\xe8\xfb;\xc6FC\xd8c\x97Nƺ\xcd\xdcHD\xe8\xc8ȶB\xa9H@\xff\xb67<3\xc2u\x9f˒*.r\x9dU\x06E?\x90e\xf1\xb6\xf0\x8c\xe7\xf5\xf8:r\xed\xcf\xf0\xec<\x898ɏ\x18\xe1\x18\xb2UƵ\x18\xe6 0P1\xcaTwJ\xe1R\xd1\x1c\x82뎷\xcf\x14\x8b\x96\xbc\xc7\xfaR*\xfe\x96\xa8Ҝ\x9a\x8f\x00.M\x17\xb6\x83\xeaAQ\xb8\xf69\xa6^7+\x8f\x8e\x80\x87\xe1\x8c\f\xd1}\x86\xaaJ\xc53\xfb\x85`\xf7 \x1c\xa5Z\xd2x\xab{KM\x00\xb0\r\xfaK\xa6zd\x9a\xb1\x04\xdbV\xaf\xb3\x19\xc6\x1f\xe9\xae=\xbe\xa5\x80\aZ\x8f\xac\xde\xebGgjG\xfe8p\x8a>\xbeF\xaay\x01\xbf\xc4lx\x95\xfe\xf9\xa2K&\xc8d\xd0\x18\xd5'\xb3\t\xa8@w\xed\x92\x1c\xdba\xb9\t\xe4\x0f\xcb\xf9ثD\x9cfvf\xdc;\xdf\xfb/q\xca\x03Z\x89:\xbe\x1erv\x05\x03Bz\xabp3¸W$\"\x16N..\x01\xbbx\xee\x93ڒ\x8b[\xaf}M\x892\xdd\x19}\x02_\xf7\xf9,u\xf8\xe3\x1f\xce\xe0\x1b\xa9\x03Ճ\xe6\x90\xf7ٜ\xef\xf8\x96\xafsÙ\xd6\xed5Zߘp\u007fw!\n\x16[\xc2>\x1bv@)־\xf8\xb6\x99\x89r(\x8c\xb9j[[^\x95\xaa>\xa0\v/mE\x8b\x03\xe2\v](r\x1e\xefA\v\xb2\xe88\xd3\xe3K\xf8\xed\xf0\xb7\xa6\x1b\xf02>\xef1\xdeJ\x00,\rߞ\x9b\x13\x03K\xe3h\xa4d\xc2q[9h\"\x87\xe2\u007f\xcb\xfe1\x1a'G\x8bQr\xb1\xc7;?\x11\xe7\x95\x1d\x86\xc1\x92\xa7\x03\x12\x0f\xc3\xdfӮ\xd2\xebM\xff\x03Y\xfc\xb34:Ae?\x87O\x9f'\x90\x9f\x8d?\xf6\xbf{\xf1\xe2\xff\x02\x00\x00\xff\xffTTw\xa4\x84\x1c\x00\x00"), diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index 5b57f1e8f7..6a0a917341 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -74,6 +74,10 @@ type BackupStorageLocationStatus struct { // +nullable LastValidationTime *metav1.Time `json:"lastValidationTime,omitempty"` + // Message is a message about the backup storage location's status. + // +optional + Message string `json:"message,omitempty"` + // LastSyncedRevision is the value of the `metadata/revision` file in the backup // storage location the last time the BSL's contents were synced into the cluster. // diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index dc9c430a78..e6abd9b356 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -23,8 +23,8 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" @@ -95,37 +95,45 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr continue } - backupStore, err := r.BackupStoreGetter.Get(location, pluginManager, log) - if err != nil { - log.WithError(err).Error("Error getting a backup store") - continue - } - - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(location, r.Client) - if err != nil { - log.WithError(err).Error("Error getting a patch helper to update this resource") - continue - } - - // updates the default backup location - location.Spec.Default = isDefault - - log.Info("Validating backup storage location") anyVerified = true - if err := backupStore.IsValid(); err != nil { - log.Info("Backup storage location is invalid, marking as unavailable") - unavailableErrors = append(unavailableErrors, errors.Wrapf(err, "Backup storage location %q is unavailable", location.Name).Error()) - location.Status.Phase = velerov1api.BackupStorageLocationPhaseUnavailable - } else { - log.Info("Backup storage location valid, marking as available") - location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable - } - location.Status.LastValidationTime = &metav1.Time{Time: time.Now().UTC()} - if err := patchHelper.Patch(r.Ctx, location); err != nil { - log.WithError(err).Error("Error updating backup storage location phase") - } + func() { + // Initialize the patch helper. + patchHelper, err := patch.NewHelper(location, r.Client) + if err != nil { + log.WithError(err).Error("Error getting a patch helper to update this resource") + return + } + defer func() { + location.Status.LastValidationTime = &metav1.Time{Time: time.Now().UTC()} + if err != nil { + log.Info("Backup storage location is invalid, marking as unavailable") + err = errors.Wrapf(err, "Backup storage location %q is unavailable", location.Name) + unavailableErrors = append(unavailableErrors, err.Error()) + location.Status.Phase = velerov1api.BackupStorageLocationPhaseUnavailable + location.Status.Message = err.Error() + } else { + log.Info("Backup storage location valid, marking as available") + location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable + location.Status.Message = "" + } + if err := patchHelper.Patch(r.Ctx, location); err != nil { + log.WithError(err).Error("Error updating backup storage location phase") + } + }() + + backupStore, err := r.BackupStoreGetter.Get(location, pluginManager, log) + if err != nil { + err = errors.Wrapf(err, "Error getting a backup store") + return + } + + // updates the default backup location + location.Spec.Default = isDefault + + log.Info("Validating backup storage location") + err = backupStore.IsValid() + }() } if !anyVerified { From 3c607931c0842135ce4a98f9f81f41db07c783ca Mon Sep 17 00:00:00 2001 From: Ganesh Hubale Date: Tue, 8 Mar 2022 14:26:52 +0530 Subject: [PATCH 060/366] Fixed start contributing link (#4723) Signed-off-by: Ganesh Hubale --- site/content/docs/v1.8/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/v1.8/_index.md b/site/content/docs/v1.8/_index.md index 74eece82f2..d4a88da6a3 100644 --- a/site/content/docs/v1.8/_index.md +++ b/site/content/docs/v1.8/_index.md @@ -33,7 +33,7 @@ If you encounter issues, review the [troubleshooting docs][30], [file an issue][ ## Contributing -If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8.0/start-contributing/) documentation for guidance on how to setup Velero for development. +If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8/start-contributing/) documentation for guidance on how to setup Velero for development. ## Changelog From d694d4091999004ee495f7522684ce786177c1ba Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 8 Mar 2022 17:04:32 +0800 Subject: [PATCH 061/366] Support multiple skip option for E2E test The GINKGO_SKIP option is updated to string that can be separated by "." for "make test-e2e". Signed-off-by: Xun Jiang --- changelogs/unreleased/4725-jxun | 1 + test/e2e/Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4725-jxun diff --git a/changelogs/unreleased/4725-jxun b/changelogs/unreleased/4725-jxun new file mode 100644 index 0000000000..f9e0e4d94a --- /dev/null +++ b/changelogs/unreleased/4725-jxun @@ -0,0 +1 @@ +Support multiple skip option for E2E test \ No newline at end of file diff --git a/test/e2e/Makefile b/test/e2e/Makefile index b49829ab68..01099282d2 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -47,6 +47,7 @@ KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin GINKGO_FOCUS ?= GINKGO_SKIP ?= +SKIP_STR := $(foreach var, $(subst ., ,$(GINKGO_SKIP)),-skip "$(var)") VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) @@ -86,7 +87,7 @@ run: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plug-in provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" -skip="$(GINKGO_SKIP)" . -- -velerocli=$(VELERO_CLI) \ + @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" $(SKIP_STR) . -- -velerocli=$(VELERO_CLI) \ -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ From d0422d8ee5a6022b6750c84aca3283a82ac437c2 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Tue, 8 Mar 2022 10:39:01 -0500 Subject: [PATCH 062/366] Clarify backups that get deleted via object storage sync Signed-off-by: Tiger Kaovilai --- site/content/docs/main/how-velero-works.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/how-velero-works.md b/site/content/docs/main/how-velero-works.md index 250ab2dab5..cd227b22a3 100644 --- a/site/content/docs/main/how-velero-works.md +++ b/site/content/docs/main/how-velero-works.md @@ -77,7 +77,8 @@ Velero treats object storage as the source of truth. It continuously checks to s This allows restore functionality to work in a cluster migration scenario, where the original backup objects do not exist in the new cluster. -Likewise, if a backup object exists in Kubernetes but not in object storage, it will be deleted from Kubernetes since the backup tarball no longer exists. +Likewise, if a `Completed` backup object exists in Kubernetes but not in object storage, it will be deleted from Kubernetes since the backup tarball no longer exists. +`Failed` or `PartiallyFailed` backup will not be removed by object storage sync. [10]: backup-hooks.md [11]: restore-hooks.md From 09cdf41d97a4a0eb208299cfa9b95cd689a699bd Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 1 Mar 2022 01:47:15 +0000 Subject: [PATCH 063/366] Add E2E test of bsl deletion Signed-off-by: danfengl --- go.mod | 3 +- test/e2e/backups/deletion.go | 8 +- test/e2e/backups/sync_backups.go | 17 +- test/e2e/basic/enable_api_group_versions.go | 2 +- test/e2e/bsl-mgmt/deletion.go | 330 ++++++++++++++++++++ test/e2e/e2e_suite_test.go | 3 + test/e2e/types.go | 1 + test/e2e/upgrade/upgrade.go | 2 +- test/e2e/util/common/common.go | 54 ++++ test/e2e/util/k8s/common.go | 59 ++++ test/e2e/util/kibishii/kibishii_utils.go | 2 +- test/e2e/util/providers/aws_utils.go | 23 +- test/e2e/util/providers/azure_utils.go | 19 +- test/e2e/util/providers/common.go | 23 +- test/e2e/util/providers/gcloud_utils.go | 5 +- test/e2e/util/velero/velero_utils.go | 134 +++++++- 16 files changed, 620 insertions(+), 65 deletions(-) create mode 100644 test/e2e/bsl-mgmt/deletion.go create mode 100644 test/e2e/util/common/common.go diff --git a/go.mod b/go.mod index 358bb12f04..fc39dd2b9e 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.5.2 + github.com/google/go-cmp v0.5.6 github.com/google/uuid v1.2.0 github.com/hashicorp/go-hclog v0.12.0 github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 @@ -71,7 +72,6 @@ require ( github.com/gobuffalo/flect v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-cmp v0.5.6 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/googleapis/gax-go/v2 v2.1.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect @@ -108,6 +108,7 @@ require ( golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 100dede1cc..13c254ff08 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -102,7 +102,7 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa if err != nil { return err } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots, ""); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) @@ -125,7 +125,7 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.ExpectCount = 2 snapshotCheckPoint.NamespaceBackedUp = deletionTest - err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, snapshotCheckPoint) + err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { return err } @@ -140,14 +140,14 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa return err } if useVolumeSnapshots { - err = SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix) + err = SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName) if err != nil { return err } } backupName = "backup-1-" + UUIDgen.String() - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots, ""); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index bd81014d0b..ae0756ff9b 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -29,7 +29,6 @@ import ( "github.com/google/uuid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/util/wait" . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" @@ -83,7 +82,7 @@ func BackupsSyncTest() { }() By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false); err != nil { + if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false, ""); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") } Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) @@ -116,7 +115,7 @@ func BackupsSyncTest() { }() By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false); err != nil { + if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false, ""); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") } Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) @@ -141,15 +140,5 @@ func BackupsSyncTest() { } func (b *SyncBackups) IsBackupsSynced() error { - return wait.PollImmediate(10*time.Second, 10*time.Minute, func() (bool, error) { - if exist, err := IsBackupExist(b.ctx, VeleroCfg.VeleroCLI, b.backupName); err != nil { - return false, err - } else { - if exist { - return true, nil - } else { - return false, nil - } - } - }) + return WaitForBackupCreated(b.ctx, VeleroCfg.VeleroCLI, b.backupName, 10*time.Minute) } diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 215fdf773d..a251b278d8 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -236,7 +236,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso backup := "backup-rockbands-" + UUIDgen.String() + "-" + strconv.Itoa(i) namespacesStr := strings.Join(tc.namespaces, ",") - err = VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, namespacesStr, "", false) + err = VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, namespacesStr, "", false, "") if err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, "") return errors.Wrapf(err, "back up %s namespaces on source cluster", namespacesStr) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go new file mode 100644 index 0000000000..78135e8616 --- /dev/null +++ b/test/e2e/bsl-mgmt/deletion.go @@ -0,0 +1,330 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package bslmgmt + +import ( + "context" + "flag" + "fmt" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" + + . "github.com/vmware-tanzu/velero/test/e2e/util/providers" + . "github.com/vmware-tanzu/velero/test/e2e/util/velero" +) + +const ( + // Please make sure length of this namespace should be shorter, + // otherwise ResticRepositories name verification will be wrong + // when making combination of ResticRepositories name(max length is 63) + bslDeletionTestNs = "bsl-deletion" +) + +// Test backup and restore of Kibishi using restic + +func BslDeletionWithSnapshots() { + BslDeletionTest(true) +} + +func BslDeletionWithRestic() { + BslDeletionTest(false) +} +func BslDeletionTest(useVolumeSnapshots bool) { + client, err := NewTestClient() + Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup deletion tests") + less := func(a, b string) bool { return a < b } + BeforeEach(func() { + if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { + Skip("Volume snapshots not supported on kind") + } + var err error + flag.Parse() + UUIDgen, err = uuid.NewRandom() + Expect(err).To(Succeed()) + if VeleroCfg.InstallVelero { + Expect(VeleroInstall(context.Background(), &VeleroCfg, "", useVolumeSnapshots)).To(Succeed()) + } + }) + + AfterEach(func() { + if VeleroCfg.InstallVelero { + Expect(DeleteNamespace(context.Background(), client, bslDeletionTestNs, + true)).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", + bslDeletionTestNs)) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + } + }) + + When("kibishii is the sample workload", func() { + It("Local backups and restic repos (if Velero was installed with Restic) will be deleted once the corresponding backup storage location is deleted", func() { + if VeleroCfg.AdditionalBSLProvider == "" { + Skip("no additional BSL provider given, not running multiple BackupStorageLocation with unique credentials tests") + } + + if VeleroCfg.AdditionalBSLBucket == "" { + Skip("no additional BSL bucket given, not running multiple BackupStorageLocation with unique credentials tests") + } + + if VeleroCfg.AdditionalBSLCredentials == "" { + Skip("no additional BSL credentials given, not running multiple BackupStorageLocation with unique credentials tests") + } + + By(fmt.Sprintf("Add an additional plugin for provider %s", VeleroCfg.AdditionalBSLProvider), func() { + Expect(VeleroAddPluginsForProvider(context.TODO(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, VeleroCfg.AdditionalBSLProvider, VeleroCfg.AddBSLPlugins)).To(Succeed()) + }) + + additionalBsl := fmt.Sprintf("bsl-%s", UUIDgen) + secretName := fmt.Sprintf("bsl-credentials-%s", UUIDgen) + secretKey := fmt.Sprintf("creds-%s", VeleroCfg.AdditionalBSLProvider) + files := map[string]string{ + secretKey: VeleroCfg.AdditionalBSLCredentials, + } + + By(fmt.Sprintf("Create Secret for additional BSL %s", additionalBsl), func() { + Expect(CreateSecretFromFiles(context.TODO(), client, VeleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) + }) + + By(fmt.Sprintf("Create additional BSL using credential %s", secretName), func() { + Expect(VeleroCreateBackupLocation(context.TODO(), + VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, + additionalBsl, + VeleroCfg.AdditionalBSLProvider, + VeleroCfg.AdditionalBSLBucket, + VeleroCfg.AdditionalBSLPrefix, + VeleroCfg.AdditionalBSLConfig, + secretName, + secretKey, + )).To(Succeed()) + }) + + backupName_1 := "backup1-" + UUIDgen.String() + backupName_2 := "backup2-" + UUIDgen.String() + oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + + backupLocation_1 := "default" + backupLocation_2 := additionalBsl + podName_1 := "kibishii-deployment-0" + podName_2 := "kibishii-deployment-1" + + label_1 := "for=1" + // TODO remove when issue https://github.com/vmware-tanzu/velero/issues/4724 is fixed + //label_2 := "for!=1" + label_2 := "for=2" + By("Create namespace for sample workload", func() { + Expect(CreateNamespace(oneHourTimeout, client, bslDeletionTestNs)).To(Succeed()) + }) + + By("Deploy sample workload of Kibishii", func() { + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, + bslDeletionTestNs, VeleroCfg.RegistryCredentialFile)).To(Succeed()) + }) + + // Restic can not backup PV only, so pod need to be labeled also + By("Label all 2 worker-pods of Kibishii", func() { + Expect(AddLabelToPod(context.Background(), podName_1, bslDeletionTestNs, label_1)).To(Succeed()) + Expect(AddLabelToPod(context.Background(), "kibishii-deployment-1", bslDeletionTestNs, label_2)).To(Succeed()) + }) + + By("Get all 2 PVCs of Kibishii and label them seprately ", func() { + pvc, err := GetPvcByPodName(context.Background(), bslDeletionTestNs, podName_1) + Expect(err).To(Succeed()) + fmt.Println(pvc) + Expect(len(pvc)).To(Equal(1)) + pvc1 := pvc[0] + pvc, err = GetPvcByPodName(context.Background(), bslDeletionTestNs, podName_2) + Expect(err).To(Succeed()) + fmt.Println(pvc) + Expect(len(pvc)).To(Equal(1)) + pvc2 := pvc[0] + Expect(AddLabelToPvc(context.Background(), pvc1, bslDeletionTestNs, label_1)).To(Succeed()) + Expect(AddLabelToPvc(context.Background(), pvc2, bslDeletionTestNs, label_2)).To(Succeed()) + }) + + By(fmt.Sprintf("Backup one of PV of sample workload by label-1 - Kibishii by the first BSL %s", backupLocation_1), func() { + // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command + // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case + Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName_1, bslDeletionTestNs, + backupLocation_1, useVolumeSnapshots, label_1)).To(Succeed()) + }) + + By(fmt.Sprintf("Back up the other one PV of sample workload with label-2 into the additional BSL %s", backupLocation_2), func() { + Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName_2, bslDeletionTestNs, + backupLocation_2, useVolumeSnapshots, label_2)).To(Succeed()) + }) + + if useVolumeSnapshots { + if VeleroCfg.CloudProvider == "vsphere" { + // TODO - remove after upload progress monitoring is implemented + By("Waiting for vSphere uploads to complete", func() { + Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, + bslDeletionTestNs)).To(Succeed()) + }) + By(fmt.Sprintf("Snapshot CR in backup %s should be created", backupName_1), func() { + Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, + backupName_1, 1)).To(Succeed()) + }) + By(fmt.Sprintf("Snapshot CR in backup %s should be created", backupName_2), func() { + Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, + backupName_2, 1)).To(Succeed()) + }) + } + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs + By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_1), func() { + snapshotCheckPoint.ExpectCount = 1 + snapshotCheckPoint.PodName = podName_1 + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) + }) + By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_2), func() { + snapshotCheckPoint.ExpectCount = 1 + snapshotCheckPoint.PodName = podName_2 + var BSLCredentials, BSLConfig string + if VeleroCfg.CloudProvider == "vsphere" { + BSLCredentials = VeleroCfg.AdditionalBSLCredentials + BSLConfig = VeleroCfg.AdditionalBSLConfig + } else { + BSLCredentials = VeleroCfg.CloudCredentialsFile + BSLConfig = VeleroCfg.BSLConfig + } + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + BSLCredentials, VeleroCfg.AdditionalBSLBucket, + BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) + }) + } else { // For Restics + By(fmt.Sprintf("Resticrepositories for BSL %s should be created in Velero namespace", backupLocation_1), func() { + Expect(ResticRepositoriesCountShouldBe(context.Background(), + VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_1, 1)).To(Succeed()) + }) + By(fmt.Sprintf("Resticrepositories for BSL %s should be created in Velero namespace", backupLocation_2), func() { + Expect(ResticRepositoriesCountShouldBe(context.Background(), + VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_2, 1)).To(Succeed()) + }) + } + + By(fmt.Sprintf("Verify if backup %s is created or not", backupName_1), func() { + Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, + backupName_1, 10*time.Minute)).To(Succeed()) + }) + + By(fmt.Sprintf("Verify if backup %s is created or not", backupName_2), func() { + Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, + backupName_2, 10*time.Minute)).To(Succeed()) + }) + + backupsInBSL1, err := GetBackupsFromBsl(context.Background(), VeleroCfg.VeleroCLI, backupLocation_1) + Expect(err).To(Succeed()) + backupsInBSL2, err := GetBackupsFromBsl(context.Background(), VeleroCfg.VeleroCLI, backupLocation_2) + Expect(err).To(Succeed()) + backupsInBsl1AndBsl2 := append(backupsInBSL1, backupsInBSL2...) + + By(fmt.Sprintf("Get all backups from 2 BSLs %s before deleting one of them", backupLocation_1), func() { + backupsBeforeDel, err := GetAllBackups(context.Background(), VeleroCfg.VeleroCLI) + Expect(err).To(Succeed()) + Expect(cmp.Diff(backupsInBsl1AndBsl2, backupsBeforeDel, cmpopts.SortSlices(less))).Should(BeEmpty()) + + By(fmt.Sprintf("Backup1 %s should exist in cloud object store before bsl deletion", backupName_1), func() { + Expect(ObjectsShouldBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, + VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, + backupName_1, BackupObjectsPrefix)).To(Succeed()) + }) + + By(fmt.Sprintf("Delete one of backup locations - %s", backupLocation_1), func() { + Expect(DeleteBslResource(context.Background(), VeleroCfg.VeleroCLI, backupLocation_1)).To(Succeed()) + }) + + By("Get all backups from 2 BSLs after deleting one of them", func() { + backupsAfterDel, err := GetAllBackups(context.Background(), VeleroCfg.VeleroCLI) + Expect(err).To(Succeed()) + // Default BSL is deleted, so backups in additional BSL should be left only + Expect(cmp.Diff(backupsInBSL2, backupsAfterDel, cmpopts.SortSlices(less))).Should(BeEmpty()) + }) + }) + + By(fmt.Sprintf("Backup1 %s should still exist in cloud object store after bsl deletion", backupName_1), func() { + Expect(ObjectsShouldBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, + VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, + backupName_1, BackupObjectsPrefix)).To(Succeed()) + }) + + // TODO: Choose additional BSL to be deleted as an new test case + // By(fmt.Sprintf("Backup %s should still exist in cloud object store", backupName_2), func() { + // Expect(ObjectsShouldBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.AdditionalBSLCredentials, + // VeleroCfg.AdditionalBSLBucket, VeleroCfg.AdditionalBSLPrefix, VeleroCfg.AdditionalBSLConfig, + // backupName_2, BackupObjectsPrefix)).To(Succeed()) + // }) + + if useVolumeSnapshots { + if VeleroCfg.CloudProvider == "vsphere" { + By(fmt.Sprintf("Snapshot in backup %s should still exist, because snapshot CR will be deleted 24 hours later if the status is a success", backupName_2), func() { + Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, + backupName_1, 1)).To(Succeed()) + Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, + backupName_2, 1)).To(Succeed()) + }) + } + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs + By(fmt.Sprintf("Snapshot should not be deleted in cloud object store after deleting bsl %s", backupLocation_1), func() { + snapshotCheckPoint.ExpectCount = 1 + snapshotCheckPoint.PodName = podName_1 + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) + }) + By(fmt.Sprintf("Snapshot should not be deleted in cloud object store after deleting bsl %s", backupLocation_2), func() { + snapshotCheckPoint.ExpectCount = 1 + snapshotCheckPoint.PodName = podName_2 + var BSLCredentials, BSLConfig string + if VeleroCfg.CloudProvider == "vsphere" { + BSLCredentials = VeleroCfg.AdditionalBSLCredentials + BSLConfig = VeleroCfg.AdditionalBSLConfig + } else { + BSLCredentials = VeleroCfg.CloudCredentialsFile + BSLConfig = VeleroCfg.BSLConfig + } + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + BSLCredentials, VeleroCfg.AdditionalBSLBucket, + BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) + }) + } else { // For Restic + By(fmt.Sprintf("Resticrepositories for BSL %s should be deleted in Velero namespace", backupLocation_1), func() { + Expect(ResticRepositoriesCountShouldBe(context.Background(), + VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_1, 0)).To(Succeed()) + }) + By(fmt.Sprintf("Resticrepositories for BSL %s should still exist in Velero namespace", backupLocation_2), func() { + Expect(ResticRepositoriesCountShouldBe(context.Background(), + VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_2, 1)).To(Succeed()) + }) + } + fmt.Printf("|| EXPECTED || - Backup deletion test completed successfully\n") + }) + }) +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index cabb27f452..05862e3c8b 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -29,6 +29,7 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/backups" . "github.com/vmware-tanzu/velero/test/e2e/basic" . "github.com/vmware-tanzu/velero/test/e2e/basic/resources-check" + . "github.com/vmware-tanzu/velero/test/e2e/bsl-mgmt" . "github.com/vmware-tanzu/velero/test/e2e/privilegesmgmt" . "github.com/vmware-tanzu/velero/test/e2e/resource-filtering" . "github.com/vmware-tanzu/velero/test/e2e/scale" @@ -93,6 +94,8 @@ var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup dele var _ = Describe("[Backups][Deletion][Snapshot] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) +var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) +var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) func TestE2e(t *testing.T) { // Skip running E2E tests when running only "short" tests because: diff --git a/test/e2e/types.go b/test/e2e/types.go index 919b25f71d..48c93c0987 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -54,4 +54,5 @@ type SnapshotCheckPoint struct { NamespaceBackedUp string SnapshotIDList []string ExpectCount int + PodName string } diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 7a7d818a8a..422f1d81be 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -126,7 +126,7 @@ func runUpgradeTests(client TestClient, veleroCfg *VerleroConfig, backupName, re return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", upgradeNamespace) } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCfg.UpgradeFromVeleroCLI, veleroCfg.VeleroNamespace, backupName, upgradeNamespace, backupLocation, useVolumeSnapshots); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCfg.UpgradeFromVeleroCLI, veleroCfg.VeleroNamespace, backupName, upgradeNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "RunDebug" after we bump up to 1.7 in the upgrade case VeleroBackupLogs(context.Background(), veleroCfg.UpgradeFromVeleroCLI, veleroCfg.VeleroNamespace, backupName) diff --git a/test/e2e/util/common/common.go b/test/e2e/util/common/common.go new file mode 100644 index 0000000000..17e942cab5 --- /dev/null +++ b/test/e2e/util/common/common.go @@ -0,0 +1,54 @@ +package common + +import ( + "bufio" + "bytes" + "context" + "fmt" + "os/exec" +) + +type OsCommandLine struct { + Cmd string + Args []string +} + +func GetListBy2Pipes(ctx context.Context, cmdline1, cmdline2, cmdline3 OsCommandLine) ([]string, error) { + var b2 bytes.Buffer + var errVelero, errAwk error + + c1 := exec.CommandContext(ctx, cmdline1.Cmd, cmdline1.Args...) + c2 := exec.Command(cmdline2.Cmd, cmdline2.Args...) + c3 := exec.Command(cmdline3.Cmd, cmdline3.Args...) + fmt.Println(c1) + fmt.Println(c2) + fmt.Println(c3) + c2.Stdin, errVelero = c1.StdoutPipe() + if errVelero != nil { + return nil, errVelero + } + c3.Stdin, errAwk = c2.StdoutPipe() + if errAwk != nil { + return nil, errAwk + } + c3.Stdout = &b2 + _ = c3.Start() + _ = c2.Start() + _ = c1.Run() + _ = c2.Wait() + _ = c3.Wait() + + fmt.Println(&b2) + scanner := bufio.NewScanner(&b2) + var ret []string + for scanner.Scan() { + fmt.Printf("line: %s\n", scanner.Text()) + ret = append(ret, scanner.Text()) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return ret, nil +} diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 1e4a5eb919..688084b76a 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/vmware-tanzu/velero/pkg/builder" + common "github.com/vmware-tanzu/velero/test/e2e/util/common" ) // ensureClusterExists returns whether or not a kubernetes cluster exists for tests to be run on. @@ -77,3 +78,61 @@ func WaitForPods(ctx context.Context, client TestClient, namespace string, pods } return nil } + +func GetPvcByPodName(ctx context.Context, namespace, podName string) ([]string, error) { + // Example: + // NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE + // kibishii-data-kibishii-deployment-0 Bound pvc-94b9fdf2-c30f-4a7b-87bf-06eadca0d5b6 1Gi RWO kibishii-storage-class 115s + CmdLine1 := &common.OsCommandLine{ + Cmd: "kubectl", + Args: []string{"get", "pvc", "-n", namespace}, + } + CmdLine2 := &common.OsCommandLine{ + Cmd: "grep", + Args: []string{podName}, + } + CmdLine3 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print $1}"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} + +func GetPvByPvc(ctx context.Context, pvc string) ([]string, error) { + // Example: + // NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE + // pvc-3f784366-58db-40b2-8fec-77307807e74b 1Gi RWO Delete Bound bsl-deletion/kibishii-data-kibishii-deployment-0 kibishii-storage-class 6h41m + CmdLine1 := &common.OsCommandLine{ + Cmd: "kubectl", + Args: []string{"get", "pv"}, + } + + CmdLine2 := &common.OsCommandLine{ + Cmd: "grep", + Args: []string{pvc}, + } + + CmdLine3 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print $1}"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} + +func AddLabelToPv(ctx context.Context, pv, label string) error { + return exec.CommandContext(ctx, "kubectl", "label", "pv", pv, label).Run() +} + +func AddLabelToPvc(ctx context.Context, pvc, namespace, label string) error { + args := []string{"label", "pvc", pvc, "-n", namespace, label} + fmt.Println(args) + return exec.CommandContext(ctx, "kubectl", args...).Run() +} + +func AddLabelToPod(ctx context.Context, podName, namespace, label string) error { + args := []string{"label", "pod", podName, "-n", namespace, label} + fmt.Println(args) + return exec.CommandContext(ctx, "kubectl", args...).Run() +} diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 945006c54b..2b58f7cc16 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -51,7 +51,7 @@ func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespac return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, kibishiiNamespace, backupLocation, useVolumeSnapshots); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, kibishiiNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, backupName, "") return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace) } diff --git a/test/e2e/util/providers/aws_utils.go b/test/e2e/util/providers/aws_utils.go index 3cddbef734..3ada35acd4 100644 --- a/test/e2e/util/providers/aws_utils.go +++ b/test/e2e/util/providers/aws_utils.go @@ -29,7 +29,7 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" - . "github.com/vmware-tanzu/velero/test/e2e" + e2e "github.com/vmware-tanzu/velero/test/e2e" ) type AWSStorage string @@ -62,10 +62,7 @@ func (s AWSStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix if bslPrefix != "" { objectsInput.Prefix = aws.String(bslPrefix) } - s3Config := &aws.Config{ - Region: aws.String(region), - Credentials: credentials.NewSharedCredentials(cloudCredentialsFile, ""), - } + var s3Config *aws.Config if region == "minio" { s3url = config.Data()["s3Url"] s3Config = &aws.Config{ @@ -75,8 +72,12 @@ func (s AWSStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix DisableSSL: aws.Bool(true), S3ForcePathStyle: aws.Bool(true), } + } else { + s3Config = &aws.Config{ + Region: aws.String(region), + Credentials: credentials.NewSharedCredentials(cloudCredentialsFile, ""), + } } - sess, err := session.NewSession(s3Config) if err != nil { @@ -97,7 +98,7 @@ func (s AWSStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix fmt.Println("item:") fmt.Println(item) backupNameInStorage = strings.TrimPrefix(*item.Prefix, strings.Trim(bslPrefix, "/")+"/") - fmt.Println("backupNameInStorage:" + backupNameInStorage) + fmt.Println("backupNameInStorage:" + backupNameInStorage + " backupObject:" + backupObject) if strings.Contains(backupNameInStorage, backupObject) { fmt.Printf("Backup %s was found under prefix %s \n", backupObject, bslPrefix) return true, nil @@ -144,7 +145,7 @@ func (s AWSStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPr return nil } -func (s AWSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { +func (s AWSStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck e2e.SnapshotCheckPoint) error { config := flag.NewMap() config.Set(bslConfig) @@ -172,10 +173,16 @@ func (s AWSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix }, }, } + result, err := svc.DescribeSnapshots(params) if err != nil { fmt.Println(err) } + + for _, n := range result.Snapshots { + fmt.Println(n.SnapshotId) + fmt.Println(n.Tags) + } if len(result.Snapshots) != snapshotCheck.ExpectCount { return errors.New(fmt.Sprintf("Snapshot count is not as expected %d", snapshotCheck.ExpectCount)) } else { diff --git a/test/e2e/util/providers/azure_utils.go b/test/e2e/util/providers/azure_utils.go index b89f690997..a34b05a416 100644 --- a/test/e2e/util/providers/azure_utils.go +++ b/test/e2e/util/providers/azure_utils.go @@ -317,18 +317,9 @@ func mapLookup(data map[string]string) func(string) string { return data[key] } } -func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { +func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { ctx := context.Background() - config := flag.NewMap() - config.Set(bslConfig) - if err := validateConfigKeys(config.Data(), - resourceGroupConfigKey, - subscriptionIDConfigKey, - storageAccount, - ); err != nil { - return err - } if err := loadCredentialsIntoEnv(cloudCredentialsFile); err != nil { return err @@ -348,13 +339,7 @@ func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPref // set a different subscriptionId for snapshots if specified snapshotsSubscriptionID := envVars[subscriptionIDEnvVar] - if val := config.Data()[subscriptionIDConfigKey]; val != "" { - // if subscription was set in config, it is required to also set the resource group - if _, err := getRequiredValues(mapLookup(config.Data()), resourceGroupConfigKey); err != nil { - return errors.Wrap(err, "resourceGroup not specified, but is a requirement when backing up to a different subscription") - } - snapshotsSubscriptionID = val - } + // set up clients snapsClient := disk.NewSnapshotsClientWithBaseURI(env.ResourceManagerEndpoint, snapshotsSubscriptionID) snapsClient.PollingDelay = 5 * time.Second diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index f6549eea61..26fc8083cb 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -31,7 +31,7 @@ import ( type ObjectsInStorage interface { IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) (bool, error) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) error - IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error + IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error } func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { @@ -109,11 +109,11 @@ func DeleteObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPr return nil } -func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { +func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string) error { fmt.Printf("|| VERIFICATION || - Snapshots should not exist in cloud, backup %s\n", backupName) var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.ExpectCount = 0 - err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix, snapshotCheckPoint) + err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s is existed in cloud after backup as expected", backupName)) } @@ -121,9 +121,9 @@ func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBuck return nil } -func SnapshotsShouldBeCreatedInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, snapshotCheckPoint SnapshotCheckPoint) error { +func SnapshotsShouldBeCreatedInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { fmt.Printf("|| VERIFICATION || - Snapshots should exist in cloud, backup %s\n", backupName) - err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix, snapshotCheckPoint) + err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s are not existed in cloud after backup as expected", backupName)) } @@ -131,8 +131,8 @@ func SnapshotsShouldBeCreatedInCloud(cloudProvider, cloudCredentialsFile, bslBuc return nil } -func IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, snapshotCheck SnapshotCheckPoint) error { - bslPrefix = getFullPrefix(bslPrefix, subPrefix) +func IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheck SnapshotCheckPoint) error { + s, err := getProvider(cloudProvider) if err != nil { return errors.Wrapf(err, fmt.Sprintf("Cloud provider %s is not valid", cloudProvider)) @@ -140,12 +140,13 @@ func IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix if cloudProvider == "vsphere" { var retSnapshotIDs []string ctx, _ := context.WithTimeout(context.Background(), time.Minute*2) - retSnapshotIDs, err = velero.GetVsphereSnapshotIDs(ctx, time.Hour, snapshotCheck.NamespaceBackedUp) + retSnapshotIDs, err = velero.GetVsphereSnapshotIDs(ctx, time.Hour, snapshotCheck.NamespaceBackedUp, snapshotCheck.PodName) if err != nil { return errors.Wrapf(err, fmt.Sprintf("Fail to get snapshot CRs of backup%s", backupName)) } - bslPrefix = "plugins" - subPrefix = "vsphere-astrolabe-repo/ivd/data" + + bslPrefix := "plugins" + subPrefix := "vsphere-astrolabe-repo/ivd/data" if snapshotCheck.ExpectCount == 0 { for _, snapshotID := range retSnapshotIDs { err := ObjectsShouldNotBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, snapshotID, subPrefix, 5) @@ -165,7 +166,7 @@ func IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix } } } else { - err = s.IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, snapshotCheck) + err = s.IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupName, snapshotCheck) if err != nil { return errors.Wrapf(err, fmt.Sprintf("Fail to get snapshot of backup%s", backupName)) } diff --git a/test/e2e/util/providers/gcloud_utils.go b/test/e2e/util/providers/gcloud_utils.go index 8f2c54870b..cc6f2acee4 100644 --- a/test/e2e/util/providers/gcloud_utils.go +++ b/test/e2e/util/providers/gcloud_utils.go @@ -105,7 +105,7 @@ func (s GCSStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPr } } -func (s GCSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { +func (s GCSStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { ctx := context.Background() data, err := ioutil.ReadFile(cloudCredentialsFile) if err != nil { @@ -137,7 +137,8 @@ func (s GCSStorage) IsSnapshotExisted(cloudCredentialsFile, bslBucket, bslPrefix }); err != nil { return errors.Wrapf(err, "Failed listing snapshot pages") } - if snapshotCountFound != len(snapshotCheck.SnapshotIDList) { + + if snapshotCountFound != snapshotCheck.ExpectCount { return errors.New(fmt.Sprintf("Snapshot count %d is not as expected %d\n", snapshotCountFound, len(snapshotCheck.SnapshotIDList))) } else { fmt.Printf("Snapshot count %d is as expected %d\n", snapshotCountFound, len(snapshotCheck.SnapshotIDList)) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 70ebb0c1e7..807bf4029c 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -40,6 +40,7 @@ import ( cliinstall "github.com/vmware-tanzu/velero/pkg/cmd/cli/install" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" + common "github.com/vmware-tanzu/velero/test/e2e/util/common" ) const BackupObjectsPrefix = "backups" @@ -233,14 +234,17 @@ func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st } // VeleroBackupNamespace uses the veleroCLI to backup a namespace. -func VeleroBackupNamespace(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string, namespace string, backupLocation string, - useVolumeSnapshots bool) error { +func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace, backupName, namespace, backupLocation string, + useVolumeSnapshots bool, selector string) error { args := []string{ "--namespace", veleroNamespace, "create", "backup", backupName, "--include-namespaces", namespace, "--wait", } + if selector != "" { + args = append(args, "--selector", selector) + } if useVolumeSnapshots { args = append(args, "--snapshot-volumes") @@ -478,7 +482,7 @@ func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, return err } -func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace string) ([]string, error) { +func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace, podName string) ([]string, error) { checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.spec.resourceHandle.name}{\"=\"}{.status.snapshotID}{\"\\n\"}{end}'") fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) @@ -498,6 +502,9 @@ func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace if len(curLine) == 0 { continue } + if podName != "" && !strings.Contains(curLine, podName) { + continue + } snapshotID := curLine[strings.LastIndex(curLine, ":")+1:] fmt.Println("snapshotID:" + snapshotID) snapshotIDDec, _ := b64.StdEncoding.DecodeString(snapshotID) @@ -626,7 +633,7 @@ func DeleteBackupResource(ctx context.Context, veleroCLI string, backupName stri fmt.Printf("|| EXPECTED || - Backup %s was deleted successfully according to message %s\n", backupName, stderr) return nil } - return errors.Wrapf(err, "Fail to get delete backup, stdout=%s, stderr=%s", stdout, stderr) + return errors.Wrapf(err, "Fail to perform get backup, stdout=%s, stderr=%s", stdout, stderr) } time.Sleep(1 * time.Minute) } @@ -640,7 +647,8 @@ func GetBackup(ctx context.Context, veleroCLI string, backupName string) (string } func IsBackupExist(ctx context.Context, veleroCLI string, backupName string) (bool, error) { - if _, outerr, err := GetBackup(ctx, veleroCLI, backupName); err != nil { + out, outerr, err := GetBackup(ctx, veleroCLI, backupName) + if err != nil { if err != nil { if strings.Contains(outerr, "not found") { return false, nil @@ -648,6 +656,7 @@ func IsBackupExist(ctx context.Context, veleroCLI string, backupName string) (bo return false, err } } + fmt.Printf("Backup %s exist locally according to output %s", backupName, out) return true, nil } @@ -664,3 +673,118 @@ func WaitBackupDeleted(ctx context.Context, veleroCLI string, backupName string, } }) } + +func WaitForBackupCreated(ctx context.Context, veleroCLI string, backupName string, timeout time.Duration) error { + return wait.PollImmediate(10*time.Second, timeout, func() (bool, error) { + if exist, err := IsBackupExist(ctx, veleroCLI, backupName); err != nil { + return false, err + } else { + if exist { + return true, nil + } else { + return false, nil + } + } + }) +} +func GetBackupsFromBsl(ctx context.Context, veleroCLI, bslName string) ([]string, error) { + args1 := []string{"get", "backups"} + if strings.TrimSpace(bslName) != "" { + args1 = append(args1, "-l", "velero.io/storage-location="+bslName) + } + CmdLine1 := &common.OsCommandLine{ + Cmd: veleroCLI, + Args: args1, + } + + CmdLine2 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print $1}"}, + } + + CmdLine3 := &common.OsCommandLine{ + Cmd: "tail", + Args: []string{"-n", "+2"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} + +func GetAllBackups(ctx context.Context, veleroCLI string) ([]string, error) { + return GetBackupsFromBsl(ctx, veleroCLI, "") +} +func DeleteBslResource(ctx context.Context, veleroCLI string, bslName string) error { + args := []string{"backup-location", "delete", bslName, "--confirm"} + + cmd := exec.CommandContext(ctx, veleroCLI, args...) + fmt.Println("Delete backup location Command:" + cmd.String()) + stdout, stderr, err := veleroexec.RunCommand(cmd) + if err != nil { + return errors.Wrapf(err, "Fail to get delete location, stdout=%s, stderr=%s", stdout, stderr) + } + + output := strings.Replace(stdout, "\n", " ", -1) + fmt.Println("Backup location delete command output:" + output) + + fmt.Println(stdout) + fmt.Println(stderr) + return nil +} + +func SnapshotCRsCountShouldBe(ctx context.Context, namespace, backupName string, expectedCount int) error { + + checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", + "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.metadata.labels.velero\\.io\\/backup-name}{\"\\n\"}{end}'") + fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) + stdout, stderr, err := veleroexec.RunCommand(checkSnapshotCmd) + if err != nil { + fmt.Print(stdout) + fmt.Print(stderr) + return errors.Wrap(err, fmt.Sprintf("Failed getting snapshot CR of backup %s in namespace %d", backupName, expectedCount)) + } + count := 0 + stdout = strings.Replace(stdout, "'", "", -1) + arr := strings.Split(stdout, "\n") + for _, bn := range arr { + fmt.Println("Snapshot CR:" + bn) + if strings.Contains(bn, backupName) { + count++ + } + } + if count == expectedCount { + return nil + } else { + return errors.New(fmt.Sprintf("SnapshotCR count %d of backup %s in namespace %s is not as expected %d", count, backupName, namespace, expectedCount)) + } +} + +func ResticRepositoriesCountShouldBe(ctx context.Context, veleroNamespace, targetNamespace string, expectedCount int) error { + resticArr, err := GetResticRepositories(ctx, veleroNamespace, targetNamespace) + if err != nil { + return errors.Wrapf(err, "Fail to get GetResticRepositories") + } + if len(resticArr) == expectedCount { + return nil + } else { + return errors.New(fmt.Sprintf("Resticrepositories count %d in namespace %s is not as expected %d", len(resticArr), targetNamespace, expectedCount)) + } +} + +func GetResticRepositories(ctx context.Context, veleroNamespace, targetNamespace string) ([]string, error) { + CmdLine1 := &common.OsCommandLine{ + Cmd: "kubectl", + Args: []string{"get", "-n", veleroNamespace, "resticrepositories"}, + } + + CmdLine2 := &common.OsCommandLine{ + Cmd: "grep", + Args: []string{targetNamespace}, + } + + CmdLine3 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print $1}"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} From 05d97aec2d8b11c064c74ce52c1cb0031b8cb339 Mon Sep 17 00:00:00 2001 From: Ganesh Hubale Date: Tue, 8 Mar 2022 14:26:52 +0530 Subject: [PATCH 064/366] Fixed start contributing link (#4723) Signed-off-by: Ganesh Hubale Signed-off-by: Hoang, Phuong --- site/content/docs/v1.8/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/v1.8/_index.md b/site/content/docs/v1.8/_index.md index 74eece82f2..d4a88da6a3 100644 --- a/site/content/docs/v1.8/_index.md +++ b/site/content/docs/v1.8/_index.md @@ -33,7 +33,7 @@ If you encounter issues, review the [troubleshooting docs][30], [file an issue][ ## Contributing -If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8.0/start-contributing/) documentation for guidance on how to setup Velero for development. +If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8/start-contributing/) documentation for guidance on how to setup Velero for development. ## Changelog From 291f0c17e869ec431ae6bfbfdd3484e342a288d4 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 8 Mar 2022 17:04:32 +0800 Subject: [PATCH 065/366] Support multiple skip option for E2E test The GINKGO_SKIP option is updated to string that can be separated by "." for "make test-e2e". Signed-off-by: Xun Jiang Signed-off-by: Hoang, Phuong --- changelogs/unreleased/4725-jxun | 1 + test/e2e/Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4725-jxun diff --git a/changelogs/unreleased/4725-jxun b/changelogs/unreleased/4725-jxun new file mode 100644 index 0000000000..f9e0e4d94a --- /dev/null +++ b/changelogs/unreleased/4725-jxun @@ -0,0 +1 @@ +Support multiple skip option for E2E test \ No newline at end of file diff --git a/test/e2e/Makefile b/test/e2e/Makefile index b49829ab68..01099282d2 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -47,6 +47,7 @@ KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin GINKGO_FOCUS ?= GINKGO_SKIP ?= +SKIP_STR := $(foreach var, $(subst ., ,$(GINKGO_SKIP)),-skip "$(var)") VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) @@ -86,7 +87,7 @@ run: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plug-in provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" -skip="$(GINKGO_SKIP)" . -- -velerocli=$(VELERO_CLI) \ + @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" $(SKIP_STR) . -- -velerocli=$(VELERO_CLI) \ -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ From 0741360050b6ad51392c5c28c678dd960a142949 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Mon, 14 Mar 2022 17:10:54 -0400 Subject: [PATCH 066/366] Adding PriorityClass to backup podAction and restore podAction. Signed-off-by: Hoang, Phuong --- pkg/backup/pod_action.go | 12 ++++++++++-- pkg/kuberesource/kuberesource.go | 1 + pkg/restore/pod_action.go | 10 ++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/backup/pod_action.go b/pkg/backup/pod_action.go index be43c24ed9..0fd479a71d 100644 --- a/pkg/backup/pod_action.go +++ b/pkg/backup/pod_action.go @@ -56,12 +56,20 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti return nil, nil, errors.WithStack(err) } + var additionalItems []velero.ResourceIdentifier + if pod.Spec.PriorityClassName > "" { + a.log.Infof("Adding priorityclass %s to additionalItems", pod.Spec.PriorityClassName) + additionalItems = append(additionalItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.PriorityClasses, + Name: pod.Spec.PriorityClassName, + }) + } + if len(pod.Spec.Volumes) == 0 { a.log.Info("pod has no volumes") - return item, nil, nil + return item, additionalItems, nil } - var additionalItems []velero.ResourceIdentifier for _, volume := range pod.Spec.Volumes { if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" { a.log.Infof("Adding pvc %s to additionalItems", volume.PersistentVolumeClaim.ClaimName) diff --git a/pkg/kuberesource/kuberesource.go b/pkg/kuberesource/kuberesource.go index a515a70fff..c6dd36a057 100644 --- a/pkg/kuberesource/kuberesource.go +++ b/pkg/kuberesource/kuberesource.go @@ -34,4 +34,5 @@ var ( VolumeSnapshotClasses = schema.GroupResource{Group: "snapshot.storage.k8s.io", Resource: "volumesnapshotclasses"} VolumeSnapshots = schema.GroupResource{Group: "snapshot.storage.k8s.io", Resource: "volumesnapshots"} VolumeSnapshotContents = schema.GroupResource{Group: "snapshot.storage.k8s.io", Resource: "volumesnapshotcontents"} + PriorityClasses = schema.GroupResource{Group: "scheduling.k8s.io", Resource: "priorityclasses"} ) diff --git a/pkg/restore/pod_action.go b/pkg/restore/pod_action.go index 2086954ae4..4db9da722e 100644 --- a/pkg/restore/pod_action.go +++ b/pkg/restore/pod_action.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -85,6 +86,11 @@ func (a *PodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*veler if err != nil { return nil, errors.WithStack(err) } - - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + restoreExecuteOutput := velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) + if pod.Spec.PriorityClassName > "" { + a.logger.Infof("Adding priorityclass %s to AdditionalItems", pod.Spec.PriorityClassName) + restoreExecuteOutput.AdditionalItems = []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PriorityClasses, Name: pod.Spec.PriorityClassName}} + } + return restoreExecuteOutput, nil } From d2ef5cbe0a08afbb6a94faa1d2f593b545f25717 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Tue, 15 Mar 2022 19:00:49 -0400 Subject: [PATCH 067/366] Add changelog Signed-off-by: Hoang, Phuong --- changelogs/unreleased/4740-phuongatemc | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/unreleased/4740-phuongatemc diff --git a/changelogs/unreleased/4740-phuongatemc b/changelogs/unreleased/4740-phuongatemc new file mode 100644 index 0000000000..0227a96655 --- /dev/null +++ b/changelogs/unreleased/4740-phuongatemc @@ -0,0 +1,2 @@ +Add PriorityClass to the AdditionalItems of Backup's PodAction and Restore's PodAction plugin to +backup and restore PriorityClass if it is used by a Pod. From e9d5f7121cae9338a0adc1fd33b488be0dd0a9c6 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Tue, 15 Mar 2022 19:51:53 -0400 Subject: [PATCH 068/366] Add unit tests, change copyright years and revert unrelated changes. Signed-off-by: Hoang, Phuong --- changelogs/unreleased/4725-jxun | 1 - pkg/backup/pod_action.go | 2 +- pkg/backup/pod_action_test.go | 21 +++++++++++++++- pkg/kuberesource/kuberesource.go | 2 +- pkg/restore/pod_action.go | 2 +- pkg/restore/pod_action_test.go | 43 ++++++++++++++++++++++++++++---- site/content/docs/v1.8/_index.md | 2 +- test/e2e/Makefile | 3 +-- 8 files changed, 63 insertions(+), 13 deletions(-) delete mode 100644 changelogs/unreleased/4725-jxun diff --git a/changelogs/unreleased/4725-jxun b/changelogs/unreleased/4725-jxun deleted file mode 100644 index f9e0e4d94a..0000000000 --- a/changelogs/unreleased/4725-jxun +++ /dev/null @@ -1 +0,0 @@ -Support multiple skip option for E2E test \ No newline at end of file diff --git a/pkg/backup/pod_action.go b/pkg/backup/pod_action.go index 0fd479a71d..bd1005500a 100644 --- a/pkg/backup/pod_action.go +++ b/pkg/backup/pod_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2017, 2022 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/backup/pod_action_test.go b/pkg/backup/pod_action_test.go index c1a7f0e0ae..80c9127d66 100644 --- a/pkg/backup/pod_action_test.go +++ b/pkg/backup/pod_action_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2018, 2022 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -115,6 +115,25 @@ func TestPodActionExecute(t *testing.T) { {GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "foo", Name: "claim2"}, }, }, + { + name: "test priority class", + pod: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "namespace": "foo", + "name": "bar" + }, + "spec": { + "priorityClassName": "testPriorityClass" + } + } + `), + expected: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PriorityClasses, Name: "testPriorityClass"}, + }, + }, } for _, test := range tests { diff --git a/pkg/kuberesource/kuberesource.go b/pkg/kuberesource/kuberesource.go index c6dd36a057..a3a0872442 100644 --- a/pkg/kuberesource/kuberesource.go +++ b/pkg/kuberesource/kuberesource.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2018, 2022 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restore/pod_action.go b/pkg/restore/pod_action.go index 4db9da722e..adfc83c0d0 100644 --- a/pkg/restore/pod_action.go +++ b/pkg/restore/pod_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2017, 2022 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restore/pod_action_test.go b/pkg/restore/pod_action_test.go index 1d01655410..c638b72290 100644 --- a/pkg/restore/pod_action_test.go +++ b/pkg/restore/pod_action_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright 2017, 2019, 2022 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ limitations under the License. package restore import ( + "github.com/vmware-tanzu/velero/pkg/kuberesource" + //"github.com/vmware-tanzu/velero/pkg/kuberesource" "testing" "github.com/stretchr/testify/assert" @@ -34,10 +36,11 @@ func TestPodActionExecute(t *testing.T) { var priority int32 = 1 tests := []struct { - name string - obj corev1api.Pod - expectedErr bool - expectedRes corev1api.Pod + name string + obj corev1api.Pod + expectedErr bool + expectedRes corev1api.Pod + additionalItems []velero.ResourceIdentifier }{ { name: "nodeName (only) should be deleted from spec", @@ -189,6 +192,35 @@ func TestPodActionExecute(t *testing.T) { }, }, }, + { + name: "test priority class", + obj: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod-1"}, + Spec: corev1api.PodSpec{ + ServiceAccountName: "foo", + PriorityClassName: "testPriorityClass", + Volumes: []corev1api.Volume{ + {Name: "foo"}, + {Name: "foo-token-foo"}, + }, + }, + }, + expectedRes: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod-1"}, + Spec: corev1api.PodSpec{ + ServiceAccountName: "foo", + PriorityClassName: "testPriorityClass", + Volumes: []corev1api.Volume{ + {Name: "foo"}, + }, + }, + }, + additionalItems: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PriorityClasses, + Name: "testPriorityClass", + }, + }, + }, } for _, test := range tests { @@ -214,6 +246,7 @@ func TestPodActionExecute(t *testing.T) { require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(res.UpdatedItem.UnstructuredContent(), &pod)) assert.Equal(t, test.expectedRes, pod) + assert.Equal(t, test.additionalItems, res.AdditionalItems) }) } } diff --git a/site/content/docs/v1.8/_index.md b/site/content/docs/v1.8/_index.md index d4a88da6a3..74eece82f2 100644 --- a/site/content/docs/v1.8/_index.md +++ b/site/content/docs/v1.8/_index.md @@ -33,7 +33,7 @@ If you encounter issues, review the [troubleshooting docs][30], [file an issue][ ## Contributing -If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8/start-contributing/) documentation for guidance on how to setup Velero for development. +If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.8.0/start-contributing/) documentation for guidance on how to setup Velero for development. ## Changelog diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 01099282d2..b49829ab68 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -47,7 +47,6 @@ KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin GINKGO_FOCUS ?= GINKGO_SKIP ?= -SKIP_STR := $(foreach var, $(subst ., ,$(GINKGO_SKIP)),-skip "$(var)") VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) @@ -87,7 +86,7 @@ run: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plug-in provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" $(SKIP_STR) . -- -velerocli=$(VELERO_CLI) \ + @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" -skip="$(GINKGO_SKIP)" . -- -velerocli=$(VELERO_CLI) \ -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ From b8b5427388b0877055a4238073c69c84e270b0c5 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Tue, 15 Mar 2022 19:54:24 -0400 Subject: [PATCH 069/366] Fix format. Signed-off-by: Hoang, Phuong --- pkg/restore/pod_action_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/restore/pod_action_test.go b/pkg/restore/pod_action_test.go index c638b72290..0d6226fe27 100644 --- a/pkg/restore/pod_action_test.go +++ b/pkg/restore/pod_action_test.go @@ -17,8 +17,6 @@ limitations under the License. package restore import ( - "github.com/vmware-tanzu/velero/pkg/kuberesource" - //"github.com/vmware-tanzu/velero/pkg/kuberesource" "testing" "github.com/stretchr/testify/assert" @@ -28,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) From da883962d620cb97d826556d5b9c74193fecf3cc Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 17 Mar 2022 21:35:33 +0800 Subject: [PATCH 070/366] Remove Bridget from maintainer list Signed-off-by: Daniel Jiang --- .github/auto-assignees.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/auto-assignees.yml b/.github/auto-assignees.yml index d798ce4d56..ddca227743 100644 --- a/.github/auto-assignees.yml +++ b/.github/auto-assignees.yml @@ -9,7 +9,6 @@ reviewers: groups: maintainers: - - zubron - dsu-igeek - jenting - sseago From 9133ee70386c2b13838cebd5f58711928044e305 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Thu, 17 Mar 2022 09:45:06 -0400 Subject: [PATCH 071/366] clarify date/time (#4745) * clarify date/time Co-authored-by: Scott Seago Signed-off-by: Tiger Kaovilai --- site/content/community/_index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index bc57977b5a..8680073fef 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -14,9 +14,9 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Follow us on Twitter at [@projectvelero](https://twitter.com/projectvelero) * Join our Kubernetes Slack channel and talk to over 800 other community members: [#velero](https://kubernetes.slack.com/messages/velero) * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. -* Join the Velero community meetings: - * 1st and 3rd Tuesday at 12PM ET / 9AM PT ([Convert to your time zone](http://www.thetimezoneconverter.com/?t=09:00&tz=PT%20%28Pacific%20Time%29)) - [Zoom link](https://vmware.zoom.us/j/551441444?pwd=dHJyMWZtdHFPWWFJaTh5TnFuYWMvZz09) - * 2nd and 4th Tuesday at 8am China Standard Time / 8pm ET / 5pm PT ([Convert to your time zone](http://www.thetimezoneconverter.com/?t=17:00&tz=PT%20%28Pacific%20Time%29)) - [Zoom link](https://vmware.zoom.us/j/551441444?pwd=dHJyMWZtdHFPWWFJaTh5TnFuYWMvZz09) +* Join the Velero community meetings - [Zoom link](https://vmware.zoom.us/j/551441444?pwd=dHJyMWZtdHFPWWFJaTh5TnFuYWMvZz09): + * 1st and 3rd Tuesday at 12PM ET / 9AM PT ([Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=12pm)) + * 2nd and 4th Wednesday at 8am China Standard Time / Tuesday 7pm EST (8pm EDT) / Tuesday 4pm PST (5pm PDT) ([Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 0e0d42b5f2a5e196db5638974e4e539c74e4c7e3 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Fri, 18 Mar 2022 11:16:23 +0800 Subject: [PATCH 072/366] Plugin versioning design (#4698) * Add plugin versioning design doc Signed-off-by: Bridget McErlean * Use more generic versions in scenarios section Signed-off-by: Bridget McErlean * Address code review Signed-off-by: Bridget McErlean * Address code review Signed-off-by: Bridget McErlean * Modify design to allow other interface changes The previous design assumed that only method addition would be supported. It now includes guidance for making changes such as method removal or signature changes. Signed-off-by: Bridget McErlean Co-authored-by: Bridget McErlean --- design/plugin-versioning.md | 289 ++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 design/plugin-versioning.md diff --git a/design/plugin-versioning.md b/design/plugin-versioning.md new file mode 100644 index 0000000000..c43cecea3c --- /dev/null +++ b/design/plugin-versioning.md @@ -0,0 +1,289 @@ +# Plugin Versioning + +## Abstract +This proposal outlines an approach to support versioning of Velero's plugin APIs to enable changes to those APIs. +It will allow for backwards compatible changes to be made, such as the addition of new plugin methods, but also backwards incompatible changes such as method removal or method signature changes. + + +## Background +When changes are made to Velero’s plugin APIs, there is no mechanism for the Velero server to communicate the version of the API that is supported, or for plugins to communicate what version they implement. +This means that any modification to a plugin API is a backwards incompatible change as it requires all plugins which implement the API to update and implement the new method. + +There are several components involved to use plugins within Velero. +From the perspective of the core Velero codebase, all plugin kinds (e.g. `ObjectStore`, `BackupItemAction`) are defined by a single API interface and all interactions with plugins are managed by a plugin manager which provides an implementation of the plugin API interface for Velero to use. + +Velero communicates with plugins via gRPC. +The core Velero project provides a framework (using the [go-plugin project](https://github.com/hashicorp/go-plugin)) for plugin authors to use to implement their plugins which manages the creation of gRPC servers and clients. +Velero plugins import the Velero plugin library in order to use this framework. +When a change is made to a plugin API, it needs to be made to the Go interface used by the Velero codebase, and also to the rpc service definition which is compiled to form part of the framework. +As each plugin kind is defined by a single interface, when a plugin imports the latest version of the Velero framework, it will need to implement the new APIs in order to build and run successfully. +If a plugin does not use the latest version of the framework, and is used with a newer version of Velero that expects the plugin to implement those methods, this will result in a runtime error as the plugin is incompatible. + +With this proposal, we aim to break this coupling and introduce plugin API versions. + +## Scenarios to Support +The following describes interactions between Velero and its plugins that will be supported with the implementation of this proposal. +For the purposes of this list, we will refer to existing Velero and plugin versions as `v1` and all following versions as version `n`. + +Velero client communicating with plugins or plugin client calling other plugins: + +- Version `n` client will be able to communicate with Version `n` plugin +- Version `n` client will be able to communicate with all previous versions of the plugin (Version `n-1` back to `v1`) + +Velero plugins importing Velero framework: +- `v1` plugin built against Version `n` Velero framework + - A plugin may choose to only implement a `v1` API, but it must be able to be built using Version `n` of the Velero framework + + +## Goals + +- Allow plugin APIs to change without requiring all plugins to implement the latest changes (even if they upgrade the version of Velero that is imported) +- Allow plugins to choose which plugin versions they support and enable them to support multiple versions +- Support breaking changes in the plugin APIs such as method removal or method signature changes +- Establish a design process for modifying plugin APIs such as method addition and removal and signature changes +- Establish a process for newer Velero clients to use older versions of a plugin API through adaptation + +## Non Goals + +- Change how plugins are managed or added +- Allow older plugin clients to communicate with new versions of plugins + +## High-Level Design + +With each change to a plugin API, a new version of the plugin interface and the proto service definition will be created which describes the new plugin API. +The plugin framework will be adapted to allow these new plugin versions to be registered. +Plugins can opt to implement any or all versions of an API, however Velero will always attempt to use the latest version, and the plugin management will be modified to adapt earlier versions of a plugin to be compatible with the latest API where possible. +Under the existing plugin framework, any new plugin version will be treated as a new plugin with a new kind. +The plugin manager (which provides implementations of a plugin to Velero) will include an adapter layer which will manage the different versions and provide the adaptation for versions which do not implement the latest version of the plugin API. +Providing an adaptation layer enables Velero and other plugin clients to use an older version of a plugin if it can be safely adapted. +As the plugins will be able to introduce backwards incompatible changes, it will _not_ be possible for older version of Velero to use plugins which only support the latest versions of the plugin APIs. + +Although adding new rpc methods to a service is considered a backwards compatible change within gRPC, due to the way the proto definitions are compiled and included in the framework used by plugins, this will require every plugin to implement the new methods. +Instead, we are opting to treat the addition of a method to an API as one requiring versioning. + +The addition of optional fields to existing structs which are used as parameters to or return values of API methods will not be considered as a change requiring versioning. +These kinds of changes do not modify method signatures and have been safely made in the past with no impact on existing plugins. + +## Detailed Design + +The following areas will need to be adapted to support plugin versioning. + +### Plugin Interface Definitions + +To provide versioned plugins, any change to a plugin interface (method addition, removal, or signature change) will require a new versioned interface to be created. +Currently, all plugin interface definitions reside in `pkg/plugin/velero` in a file corresponding to their plugin kind. +These files will be rearranged to be grouped by kind and then versioned: `pkg/plugin/velero///`. + +The following are examples of how each change may be treated: + +#### Complete Interface Change +If the entire `ObjectStore` interface is being changed such that no previous methods are being included, a file would be added to `pkg/plugin/velero/objectstore/v2/` and would contain the new interface definition: + +``` +type ObjectStore interface { + // Only include new methods that the new API version will support + + NewMethod() + // ... +} +``` + +#### Method Addition +If a method is being added to the `ObjectStore` API, a file would be added to `pkg/plugin/velero/objectstore/v2/` and may contain a new API definition as follows: + +``` +import "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v1" + +type ObjectStore interface { + // Import all the methods from the previous version of the API if they are to be included as is + v1.ObjectStore + + // Provide definitions of any new methods + NewMethod() +``` + +#### Method Removal +If a method is being removed from the `ObjectStore` API, a file would be added to `pkg/plugin/velero/objectstore/v2/` and may contain a new API definition as follows: + +``` +type ObjectStore interface { + // Methods which are required from the previous API version must be included, for example + Init(config) + PutObject(bucket, key, body) + // ... + + // Methods which are to be removed are not included +``` + +#### Method Signature modification +If a method signature in the `ObjectStore` API is being modified, a file would be added to `pkg/plugin/velero/objectstore/v2/` and may contain a new API definition as follows: + +``` +type ObjectStore interface { + // Methods which are required from the previous API version must be included, for example + Init(config) + PutObject(bucket, key, body) + // ... + + // Provide new definitions for methods which are being modified + List(bucket, prefix, newParameter) + +} +``` + +### Proto Service Definitions + +The proto service definitions of the plugins will also be versioned and arranged by their plugin kind. +Currently, all the proto definitions reside under `pkg/plugin/proto` in a file corresponding to their plugin kind. +These files will be rearranged to be grouped by kind and then versioned: `pkg/plugin/proto//`. +The scripts to compile the proto service definitions will need to be updated to place the generated Go code under a matching directory structure. + +It is not possible to import an existing proto service into a new one, so any methods will need to be duplicated across versions if they are required by the new version. +The message definitions can be shared however, so these could be extracted from the service definition files and placed in a file that can be shared across all versions of the service. + +### Plugin Framework + +To allow plugins to register which versions of the API they implement, the plugin framework will need to be adapted to accept new versions. +Currently, the plugin manager stores a [`map[string]RestartableProcess`](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/manager.go#L69), where the string key is the binary name for the plugin process (e.g. "velero-plugin-for-aws"). +Each `RestartableProcess` contains a [`map[kindAndName]interface{}`](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/restartable_process.go#L60) which represents each of the unique plugin implementations provided by that binary. +[`kindAndName`](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/registry.go#L42) is a struct which combines the plugin kind (`ObjectStore`, `VolumeSnapshotter`) and the plugin name ("velero.io/aws", "velero.io/azure"). + +Each plugin version registration must be unique (to allow for multiple versions to be implemented within the same plugin binary). +This will be achieved by adding a specific registration method for each version to the Server interface in the plugin framework. +For example, if adding a V2 `RestoreItemAction` plugin, the Server interface would be modified to add the `RegisterRestoreItemActionV2` method. +This would require [adding a new plugin Kind const](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/framework/plugin_kinds.go#L28-L46) to represent the new plugin version, e.g. `PluginKindRestoreItemActionV2`. +It also requires the creation of a new implementation of the go-plugin interface ([example](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/framework/object_store.go)) to support that version and use the generated gRPC code from the proto definition (including a client and server implementation). +The Server will also need to be adapted to recognize this new plugin Kind and to serve the new implementation. + +Existing plugin Kind consts and registration methods will be left unchanged and will correspond to the current version of the plugin APIs (assumed to be v1). + +### Plugin Manager + +The plugin manager is responsible for managing the lifecycle of plugins. +It provides an interface which is used by Velero to retrieve an instance of a plugin kind with a specific name (e.g. `ObjectStore` with the name "velero.io/aws"). +The manager contains a registry of all available plugins which is populated during the main Velero server startup. +When the plugin manager is requested to provide a particular plugin, it checks the registry for that plugin kind and name. +If it is available in the registry, the manager retrieves a `RestartableProcess` for the plugin binary, creating it if it does not already exist. +That `RestartableProcess` is then used by individual restartable implementations of a plugin kind (e.g. `restartableObjectStore`, `restartableVolumeSnapshotter`). + +As new plugin versions are added, the plugin manager will be modified to always retrieve the latest version of a plugin kind. +This is to allow the remainder of the Velero codebase to assume that it will always interact with the latest version of a plugin. +If the latest version of a plugin is not available, it will attempt to fall back to previous versions and use an implementation adapted to the latest version if available. +It will be up to the author of new plugin versions to determine whether a previous version of a plugin can be adapted to work with the interface of the new version. + +For each plugin kind, a new `Restartable` struct will be introduced which will contain the plugin Kind and a function, `Get`, which will instantiate a restartable instance of that plugin kind and perform any adaptation required to make it compatible with the latest version. +For example, `RestartableObjectStore` or `RestartableVolumeSnapshotter`. +For each restartable plugin kind, a new function will be introduced which will return a slice of `Restartable` objects, sorted by version in descending order. + +The manager will iterate through the list of `Restartable`s and will check the registry for the given plugin kind and name. +If the requested version is not found, it will skip and continue to iterate, attempting to fetch previous versions of the plugin kind. +Once the requested version is found, the `Get` function will be called, returning the restartable implementation of the latest version of that plugin Kind. + +``` +type RestartableObjectStore struct { + kind framework.PluginKind + + // Get returns a restartable ObjectStore for the given name and process, wrapping if necessary + Get func(name string, restartableProcess RestartableProcess) v2.ObjectStore +} + +func (m *manager) restartableObjectStores() []RestartableObjectStore { + return []RestartableObjectStore{ + { + kind: framework.PluginKindObjectStoreV2, + Get: newRestartableObjectStoreV2, + }, + { + kind: framework.PluginKindObjectStore, + Get: func(name string, restartableProcess RestartableProcess) v2.ObjectStore { + // Adapt the existing restartable v1 plugin to be compatible with the v2 interface + return newAdaptedV1ObjectStore(newRestartableObjectStore(name, restartableProcess)) + }, + }, + } +} + +// GetObjectStore returns a restartableObjectStore for name. +func (m *manager) GetObjectStore(name string) (v2.ObjectStore, error) { + name = sanitizeName(name) + + for _, restartableObjStore := range m.restartableObjectStores() { + restartableProcess, err := m.getRestartableProcess(restartableObjStore.kind, name) + if err != nil { + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + continue + } + return nil, err + } + return restartableObjStore.Get(name, restartableProcess), nil + } + + return nil, fmt.Errorf("unable to get valid ObjectStore for %q", name) +} +``` + +If the previous version is not available, or can not be adapted to the latest version, it should not be included in the `restartableObjectStores` slice. +This will result in an error being returned as is currently the case when a plugin implementation for a particular kind and provider can not be found. + +There are situations where it may be beneficial to check at the point where a plugin API call is made whether it implements a specific version of the API. +This is something that can be addressed with future amendments to this design, however it does not seem to be necessary at this time. + +#### Plugin Adaptation + +When a new plugin API version is being proposed, it will be up to the author and the maintainer team to determine whether older versions of an API can be safely adapted to the latest version. +An adaptation will implement the latest version of the plugin API interface but will use the methods from the version that is being adapted. +In cases where the methods signatures remain the same, the adaptation layer will call through to the same method in the version being adapted. + +Examples where an adaptation may be safe: +- A method signature is being changed to add a new parameter but the parameter could be optional (for example, adding a context parameter). The adaptation could call through to the method provided in the previous version but omit the parameter. +- A method signature is being changed to remove a parameter, but it is safe to pass a default value to the previous version. The adaptation could call through to the method provided in the previous version but use a default value for the parameter. +- A new method is being added but does not impact any existing behaviour of Velero (for example, a new method which will allow Velero to [wait for additional items to be ready](https://github.com/vmware-tanzu/velero/blob/main/design/wait-for-additional-items.md)). The adaptation would return a value which allows the existing behaviour to be performed. +- A method is being deleted as it is no longer used. The adaptation would call through to any methods which are still included but would omit the deleted method in the adaptation. + +Examples where an adaptation may not be safe: +- A new method is added which is used to provide new critical functionality in Velero. If this functionality can not be replicated using existing plugin methods in previous API versions, this should not be adapted and instead the plugin manager should return an error indicating that the plugin implementation can not be found. + +### Restartable Plugin Process + +As new versions of plugins are added, new restartable implementations of plugins will also need to be created. +These are currently located within "pkg/plugin/clientmgmt" but will be rearranged to be grouped by kind and version like other plugin files. + +## Versioning Considerations + +It should be noted that if changes are being made to a plugin's API, it will only be necessary to bump the API version once within a release cycle, regardless of how many changes are made within that cycle. +This is because the changes will only be available to consumers when they upgrade to the next minor version of the Velero library. +New plugin API versions will not be introduced or backported to patch releases. + +Once a new minor or major version of Velero has been released however, any further changes will need to follow the process above and use a new API version. + +## Alternatives Considered + +### Relying on gRPC’s backwards compatibility when adding new methods + +One approach for adapting the plugin APIs would have been to rely on the fact that adding methods to gRPC services is a backwards compatible change. +This approach would allow older clients to communicate with newer plugins as the existing interface would still be provided. +This was considered but ruled out as our current framework would require any plugin that recompiles using the latest version of the framework to adapt to the new version. +Also, without specific versioned interfaces, it would require checking plugin implementations at runtime for the specific methods that are supported. + +## Compatibility + +This design doc aims to allow plugin API changes to be made in a manner that may provide some backwards compatibility. +Older versions of Velero will not be able to make use of new plugin versions however may continue to use previous versions of a plugin API if supported by the plugin. + +All compatibility concerns are addressed earlier in the document. + +## Implementation + +This design document primarily outlines an approach to allow future plugin API changes to be made. +However, there are changes to the existing code base that will be made to allow plugin authors to more easily propose and introduce changes to these APIs. + +* Plugin interface definitions (currently in `pkg/plugin/velero`) will be rearranged to be grouped by kind and then versioned: `pkg/plugin/velero///`. +* Proto definitions (currently in `pkg/plugin/proto`) will be rearranged to be grouped by kind and then versioned: `pkg/plugin/proto//`. + * This will also require changes to the `make update` build task to correctly find the new proto location and output to the versioned directories. + +It is anticipated that changes to the plugin APIs will be made as part of the 1.9 release cycle. +To assist with this work, an additional follow-up task to the ones listed above would be to prepare a V2 version of each of the existing plugins. +These new versions will not yet provide any new API methods but will provide a layout for new additions to be made + +## Open Issues From 09ec3ba9947da3ad8f6f14032de1d42567c5582f Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Mon, 21 Mar 2022 02:29:03 -0400 Subject: [PATCH 073/366] Insert all restore errors and warnings into restore log (#4743) This allows a user inspecting the restore logs to see any errors or warnings generated by the restore so that they will be seen even without having to use the describe cli. Signed-off-by: Scott Seago --- changelogs/unreleased/4743-sseago | 1 + pkg/controller/restore_controller.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 changelogs/unreleased/4743-sseago diff --git a/changelogs/unreleased/4743-sseago b/changelogs/unreleased/4743-sseago new file mode 100644 index 0000000000..8772e78085 --- /dev/null +++ b/changelogs/unreleased/4743-sseago @@ -0,0 +1 @@ +Insert all restore errors and warnings into restore log. diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 5b83e5a132..ca1308d21a 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -486,6 +486,30 @@ func (c *restoreController) runValidatedRestore(restore *api.Restore, info backu } restoreWarnings, restoreErrors := c.restorer.RestoreWithResolvers(restoreReq, actionsResolver, snapshotItemResolver, c.snapshotLocationLister, pluginManager) + + // log errors and warnings to the restore log + for _, msg := range restoreErrors.Velero { + restoreLog.Errorf("Velero restore error: %v", msg) + } + for _, msg := range restoreErrors.Cluster { + restoreLog.Errorf("Cluster resource restore error: %v", msg) + } + for ns, errs := range restoreErrors.Namespaces { + for _, msg := range errs { + restoreLog.Errorf("Namespace %v, resource restore error: %v", ns, msg) + } + } + for _, msg := range restoreWarnings.Velero { + restoreLog.Warnf("Velero restore warning: %v", msg) + } + for _, msg := range restoreWarnings.Cluster { + restoreLog.Warnf("Cluster resource restore warning: %v", msg) + } + for ns, errs := range restoreWarnings.Namespaces { + for _, msg := range errs { + restoreLog.Warnf("Namespace %v, resource restore warning: %v", ns, msg) + } + } restoreLog.Info("restore completed") // re-instantiate the backup store because credentials could have changed since the original From e7a1c3d4dc38b2761f9c78913df0f2e2ed6e866f Mon Sep 17 00:00:00 2001 From: Jonas Rosland Date: Mon, 21 Mar 2022 04:58:13 -0400 Subject: [PATCH 074/366] Remove blocking indexing of our versioned docs (#4750) Signed-off-by: Jonas Rosland --- site/layouts/docs/docs.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/site/layouts/docs/docs.html b/site/layouts/docs/docs.html index 40bd58d36b..938ce660db 100644 --- a/site/layouts/docs/docs.html +++ b/site/layouts/docs/docs.html @@ -2,10 +2,6 @@ {{ partial "head-docs.html" . }} -{{ if ne .Params.version "master" }} - - -{{ end }} {{ $fileName := "" }} {{ with .File }}{{ $fileName = .LogicalName }}{{ end }} {{ if eq $fileName "_index.md" }} From 0171a91366dd45950a2c5df2d35bf6e0977dbdda Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Mon, 21 Mar 2022 12:01:26 -0400 Subject: [PATCH 075/366] Correct copyright comment and string compare Signed-off-by: Hoang, Phuong --- pkg/backup/pod_action.go | 4 ++-- pkg/backup/pod_action_test.go | 2 +- pkg/kuberesource/kuberesource.go | 2 +- pkg/restore/pod_action.go | 4 ++-- pkg/restore/pod_action_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/backup/pod_action.go b/pkg/backup/pod_action.go index bd1005500a..d0e834fbb6 100644 --- a/pkg/backup/pod_action.go +++ b/pkg/backup/pod_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2022 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti } var additionalItems []velero.ResourceIdentifier - if pod.Spec.PriorityClassName > "" { + if pod.Spec.PriorityClassName != "" { a.log.Infof("Adding priorityclass %s to additionalItems", pod.Spec.PriorityClassName) additionalItems = append(additionalItems, velero.ResourceIdentifier{ GroupResource: kuberesource.PriorityClasses, diff --git a/pkg/backup/pod_action_test.go b/pkg/backup/pod_action_test.go index 80c9127d66..1618b30890 100644 --- a/pkg/backup/pod_action_test.go +++ b/pkg/backup/pod_action_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2022 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/kuberesource/kuberesource.go b/pkg/kuberesource/kuberesource.go index a3a0872442..c2c2d84eeb 100644 --- a/pkg/kuberesource/kuberesource.go +++ b/pkg/kuberesource/kuberesource.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2022 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restore/pod_action.go b/pkg/restore/pod_action.go index adfc83c0d0..d4bdc13847 100644 --- a/pkg/restore/pod_action.go +++ b/pkg/restore/pod_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2022 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -87,7 +87,7 @@ func (a *PodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*veler return nil, errors.WithStack(err) } restoreExecuteOutput := velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) - if pod.Spec.PriorityClassName > "" { + if pod.Spec.PriorityClassName != "" { a.logger.Infof("Adding priorityclass %s to AdditionalItems", pod.Spec.PriorityClassName) restoreExecuteOutput.AdditionalItems = []velero.ResourceIdentifier{ {GroupResource: kuberesource.PriorityClasses, Name: pod.Spec.PriorityClassName}} diff --git a/pkg/restore/pod_action_test.go b/pkg/restore/pod_action_test.go index 0d6226fe27..f1aa83c1a3 100644 --- a/pkg/restore/pod_action_test.go +++ b/pkg/restore/pod_action_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019, 2022 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From bd17d328851db1d422bbc0067558bff1c1e142f9 Mon Sep 17 00:00:00 2001 From: rick <1450685+LinuxSuRen@users.noreply.github.com> Date: Tue, 22 Mar 2022 21:43:40 +0800 Subject: [PATCH 076/366] Fix the golang lint error: Variable 'session' collides with imported package name Signed-off-by: rick <1450685+LinuxSuRen@users.noreply.github.com> --- pkg/restic/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/restic/config.go b/pkg/restic/config.go index 4cde8336aa..1600f39fa8 100644 --- a/pkg/restic/config.go +++ b/pkg/restic/config.go @@ -113,14 +113,14 @@ func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) func getBucketRegion(bucket string) (string, error) { var region string - session, err := session.NewSession() + sess, err := session.NewSession() if err != nil { return "", errors.WithStack(err) } for _, partition := range endpoints.DefaultPartitions() { for regionHint := range partition.Regions() { - region, _ = s3manager.GetBucketRegion(context.Background(), session, bucket, regionHint) + region, _ = s3manager.GetBucketRegion(context.Background(), sess, bucket, regionHint) // we only need to try a single region hint per partition, so break after the first break From 1b8da68fc29da908fb944fb5bcb188bfdef89533 Mon Sep 17 00:00:00 2001 From: Tobias Giese Date: Thu, 28 Oct 2021 19:19:05 +0200 Subject: [PATCH 077/366] metrics: add items gauges Signed-off-by: Tobias Giese --- changelogs/unreleased/4296-tobiasgiese | 1 + pkg/controller/backup_controller.go | 10 +++++-- pkg/metrics/metrics.go | 39 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/4296-tobiasgiese diff --git a/changelogs/unreleased/4296-tobiasgiese b/changelogs/unreleased/4296-tobiasgiese new file mode 100644 index 0000000000..28b8ef7634 --- /dev/null +++ b/changelogs/unreleased/4296-tobiasgiese @@ -0,0 +1 @@ +Add metrics backup_items_total and backup_items_errors diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 034f56d431..dfe7ed0017 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -627,15 +627,15 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } + backup.Status.Warnings = logCounter.GetCount(logrus.WarnLevel) + backup.Status.Errors = logCounter.GetCount(logrus.ErrorLevel) + recordBackupMetrics(backupLog, backup.Backup, backupFile, c.metrics) if err := gzippedLogFile.Close(); err != nil { c.logger.WithField(Backup, kubeutil.NamespaceAndName(backup)).WithError(err).Error("error closing gzippedLogFile") } - backup.Status.Warnings = logCounter.GetCount(logrus.WarnLevel) - backup.Status.Errors = logCounter.GetCount(logrus.ErrorLevel) - // Assign finalize phase as close to end as possible so that any errors // logged to backupLog are captured. This is done before uploading the // artifacts to object storage so that the JSON representation of the @@ -685,6 +685,10 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterVolumeSnapshotAttempts(backupScheduleName, backup.Status.VolumeSnapshotsAttempted) serverMetrics.RegisterVolumeSnapshotSuccesses(backupScheduleName, backup.Status.VolumeSnapshotsCompleted) serverMetrics.RegisterVolumeSnapshotFailures(backupScheduleName, backup.Status.VolumeSnapshotsAttempted-backup.Status.VolumeSnapshotsCompleted) + if backup.Status.Progress != nil { + serverMetrics.RegisterBackupItemsTotalGauge(backupScheduleName, backup.Status.Progress.TotalItems) + } + serverMetrics.RegisterBackupItemsErrorsGauge(backupScheduleName, backup.Status.Errors) } func persistBackup(backup *pkgbackup.Request, diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 005560ad8d..072db9134a 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -43,6 +43,8 @@ const ( backupDeletionSuccessTotal = "backup_deletion_success_total" backupDeletionFailureTotal = "backup_deletion_failure_total" backupLastSuccessfulTimestamp = "backup_last_successful_timestamp" + backupItemsTotalGauge = "backup_items_total" + backupItemsErrorsGauge = "backup_items_errors" restoreTotal = "restore_total" restoreAttemptTotal = "restore_attempt_total" restoreValidationFailedTotal = "restore_validation_failed_total" @@ -179,6 +181,22 @@ func NewServerMetrics() *ServerMetrics { }, []string{scheduleLabel}, ), + backupItemsTotalGauge: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: metricNamespace, + Name: backupItemsTotalGauge, + Help: "Total number of items backed up", + }, + []string{scheduleLabel}, + ), + backupItemsErrorsGauge: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: metricNamespace, + Name: backupItemsErrorsGauge, + Help: "Total number of errors encountered during backup", + }, + []string{scheduleLabel}, + ), restoreTotal: prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: metricNamespace, @@ -337,6 +355,12 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[backupDeletionFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } + if c, ok := m.metrics[backupItemsTotalGauge].(*prometheus.GaugeVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[backupItemsErrorsGauge].(*prometheus.GaugeVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } if c, ok := m.metrics[restoreAttemptTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } @@ -486,6 +510,21 @@ func (m *ServerMetrics) RegisterBackupDeletionSuccess(backupSchedule string) { } } +// RegisterBackupItemsTotalGauge records the number of items to be backed up. +func (m *ServerMetrics) RegisterBackupItemsTotalGauge(backupSchedule string, items int) { + if c, ok := m.metrics[backupItemsTotalGauge].(*prometheus.GaugeVec); ok { + c.WithLabelValues(backupSchedule).Set(float64(items)) + } +} + +// RegisterBackupItemsErrorsGauge records the number of all error messages that were generated during +// execution of the backup. +func (m *ServerMetrics) RegisterBackupItemsErrorsGauge(backupSchedule string, items int) { + if c, ok := m.metrics[backupItemsErrorsGauge].(*prometheus.GaugeVec); ok { + c.WithLabelValues(backupSchedule).Set(float64(items)) + } +} + // toSeconds translates a time.Duration value into a float64 // representing the number of seconds in that duration. func toSeconds(d time.Duration) float64 { From 1577a817fd6830d534f80da412fc63e4bd7b12b9 Mon Sep 17 00:00:00 2001 From: Jonas Rosland Date: Wed, 23 Mar 2022 11:20:32 -0400 Subject: [PATCH 078/366] Update community managers in MAINTAINERS.md Signed-off-by: Jonas Rosland --- MAINTAINERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 2de9607ac9..c7638fce4d 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -32,5 +32,5 @@ | Technical Lead | Daniel Jiang (reasonerjt) | | Kubernetes CSI Liaison | | | Deployment | JenTing Hsiao (jenting) | -| Community Management | Jonas Rosland (jonasrosland) | +| Community Management | Orlin Vasilev (OrlinVasilev) | | Product Management | Eleanor Millman (eleanor-millman) | From d1fdaecc94359009dc83307f1b6f6e93c565b755 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Fri, 25 Mar 2022 05:01:55 -0400 Subject: [PATCH 079/366] Add labels to expired backups failing garbage collection. (#4757) * Add bsl related TTL gc errors to labelSelectors * if backup label map is nil, make map * clear label if not BSL error Signed-off-by: Tiger Kaovilai --- changelogs/unreleased/4757-kaovilai | 1 + pkg/controller/gc_controller.go | 26 +++++++++++++++++++++- site/content/docs/main/how-velero-works.md | 9 ++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4757-kaovilai diff --git a/changelogs/unreleased/4757-kaovilai b/changelogs/unreleased/4757-kaovilai new file mode 100644 index 0000000000..34229c0447 --- /dev/null +++ b/changelogs/unreleased/4757-kaovilai @@ -0,0 +1 @@ +Garbage collector now adds labels to backups that failed to delete for BSLNotFound, BSLCannotGet, BSLReadOnly reasons. \ No newline at end of file diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index b65f7ede62..d365dcf411 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -39,7 +39,11 @@ import ( ) const ( - GCSyncPeriod = 60 * time.Minute + GCSyncPeriod = 60 * time.Minute + garbageCollectionFailure = "velero.io/gc-failure" + gcFailureBSLNotFound = "BSLNotFound" + gcFailureBSLCannotGet = "BSLCannotGet" + gcFailureBSLReadOnly = "BSLReadOnly" ) // gcController creates DeleteBackupRequests for expired backups. @@ -134,6 +138,10 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") + if backup.Labels == nil { + backup.Labels = make(map[string]string) + } + loc := &velerov1api.BackupStorageLocation{} if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: ns, @@ -141,15 +149,31 @@ func (c *gcController) processQueueItem(key string) error { }, loc); err != nil { if apierrors.IsNotFound(err) { log.Warnf("Backup cannot be garbage-collected because backup storage location %s does not exist", backup.Spec.StorageLocation) + backup.Labels[garbageCollectionFailure] = gcFailureBSLNotFound + } else { + backup.Labels[garbageCollectionFailure] = gcFailureBSLCannotGet + } + if err := c.kbClient.Update(context.Background(), backup); err != nil { + log.WithError(err).Error("error updating backup labels") } return errors.Wrap(err, "error getting backup storage location") } if loc.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { log.Infof("Backup cannot be garbage-collected because backup storage location %s is currently in read-only mode", loc.Name) + backup.Labels[garbageCollectionFailure] = gcFailureBSLReadOnly + if err := c.kbClient.Update(context.Background(), backup); err != nil { + log.WithError(err).Error("error updating backup labels") + } return nil } + // remove gc fail error label after this point + delete(backup.Labels, garbageCollectionFailure) + if err := c.kbClient.Update(context.Background(), backup); err != nil { + log.WithError(err).Error("error updating backup labels") + } + selector := labels.SelectorFromSet(labels.Set(map[string]string{ velerov1api.BackupNameLabel: label.GetValidName(backup.Name), velerov1api.BackupUIDLabel: string(backup.UID), diff --git a/site/content/docs/main/how-velero-works.md b/site/content/docs/main/how-velero-works.md index 250ab2dab5..c6b2f2a712 100644 --- a/site/content/docs/main/how-velero-works.md +++ b/site/content/docs/main/how-velero-works.md @@ -71,6 +71,15 @@ When you create a backup, you can specify a TTL (time to live) by adding the fla The TTL flag allows the user to specify the backup retention period with the value specified in hours, minutes and seconds in the form `--ttl 24h0m0s`. If not specified, a default TTL value of 30 days will be applied. +If backup fails to delete, a label `velero.io/gc-failure=` will be added to the backup custom resource. + +You can use this label to filter and select backups that failed to delete. + +Implemented reasons are: +- BSLNotFound: Backup storage location not found +- BSLCannotGet: Backup storage location cannot be retrieved from the API server for reasons other than not found +- BSLReadOnly: Backup storage location is read-only + ## Object storage sync Velero treats object storage as the source of truth. It continuously checks to see that the correct backup resources are always present. If there is a properly formatted backup file in the storage bucket, but no corresponding backup resource in the Kubernetes API, Velero synchronizes the information from object storage to Kubernetes. From 2ae502481f4e4365c7f6b6b100779190573e0410 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Fri, 25 Mar 2022 13:09:01 -0400 Subject: [PATCH 080/366] add bsl and vsl parameter links Signed-off-by: Shubham Pampattiwar --- site/content/docs/main/supported-providers.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/content/docs/main/supported-providers.md b/site/content/docs/main/supported-providers.md index 5e75dc1e24..86475c389f 100644 --- a/site/content/docs/main/supported-providers.md +++ b/site/content/docs/main/supported-providers.md @@ -9,13 +9,13 @@ Velero supports a variety of storage providers for different backup and snapshot {{< table caption="Velero supported providers" >}} -| Provider | Object Store | Volume Snapshotter | Plugin Provider Repo | Setup Instructions | -|-----------------------------------|---------------------|------------------------------|-----------------------------------------|-------------------------------| -| [Amazon Web Services (AWS)](https://aws.amazon.com) | AWS S3 | AWS EBS | [Velero plugin for AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws) | [AWS Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-aws#setup) | -| [Google Cloud Platform (GCP)](https://cloud.google.com) | Google Cloud Storage| Google Compute Engine Disks | [Velero plugin for GCP](https://github.com/vmware-tanzu/velero-plugin-for-gcp) | [GCP Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-gcp#setup) | -| [Microsoft Azure](https://azure.com) | Azure Blob Storage | Azure Managed Disks | [Velero plugin for Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure) | [Azure Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#setup) | -| [VMware vSphere](https://www.vmware.com/ca/products/vsphere.html) | 🚫 | vSphere Volumes | [VMware vSphere](https://github.com/vmware-tanzu/velero-plugin-for-vsphere) | [vSphere Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-vsphere#velero-plugin-for-vsphere-installation-and-configuration-details) | -| [Container Storage Interface (CSI)](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/)| 🚫 | CSI Volumes | [Velero plugin for CSI](https://github.com/vmware-tanzu/velero-plugin-for-csi/) | [CSI Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-csi#kinds-of-plugins-included) | +| Provider | Object Store | Volume Snapshotter | Plugin Provider Repo | Setup Instructions | Parameters | +|-----------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|-----------------------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Amazon Web Services (AWS)](https://aws.amazon.com) | AWS S3 | AWS EBS | [Velero plugin for AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws) | [AWS Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-aws#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/backupstoragelocation.md)
[VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/volumesnapshotlocation.md) | +| [Google Cloud Platform (GCP)](https://cloud.google.com) | [Google Cloud Storage](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/backupstoragelocation.md) | [Google Compute Engine Disks](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/volumesnapshotlocation.md) | [Velero plugin for GCP](https://github.com/vmware-tanzu/velero-plugin-for-gcp) | [GCP Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-gcp#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/backupstoragelocation.md)
[VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/volumesnapshotlocation.md) | +| [Microsoft Azure](https://azure.com) | Azure Blob Storage | Azure Managed Disks | [Velero plugin for Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure) | [Azure Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/backupstoragelocation.md)
[VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/volumesnapshotlocation.md) | +| [VMware vSphere](https://www.vmware.com/ca/products/vsphere.html) | 🚫 | vSphere Volumes | [VMware vSphere](https://github.com/vmware-tanzu/velero-plugin-for-vsphere) | [vSphere Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-vsphere#velero-plugin-for-vsphere-installation-and-configuration-details) | 🚫 | +| [Container Storage Interface (CSI)](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/)| 🚫 | CSI Volumes | [Velero plugin for CSI](https://github.com/vmware-tanzu/velero-plugin-for-csi/) | [CSI Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-csi#kinds-of-plugins-included) | 🚫 | {{< /table >}} Contact: [#Velero Slack](https://kubernetes.slack.com/messages/velero), [GitHub Issues](https://github.com/vmware-tanzu/velero/issues) From fa646888ef23a01883acc514a39ab695a515b636 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sun, 27 Mar 2022 10:39:35 +0800 Subject: [PATCH 081/366] Add parameter for e2e test to support modify kibishii install path. Signed-off-by: Xun Jiang --- changelogs/unreleased/4778-jxun | 1 + test/e2e/Makefile | 4 +++- test/e2e/backup/backup.go | 4 ++-- test/e2e/backups/deletion.go | 6 +++--- test/e2e/bsl-mgmt/deletion.go | 2 +- test/e2e/e2e_suite_test.go | 1 + test/e2e/types.go | 1 + test/e2e/upgrade/upgrade.go | 2 +- test/e2e/util/kibishii/kibishii_utils.go | 12 ++++++------ 9 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/4778-jxun diff --git a/changelogs/unreleased/4778-jxun b/changelogs/unreleased/4778-jxun new file mode 100644 index 0000000000..e805735b22 --- /dev/null +++ b/changelogs/unreleased/4778-jxun @@ -0,0 +1 @@ +Add parameter for e2e test to support modify kibishii install path. \ No newline at end of file diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 01099282d2..9a6209aafa 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -66,6 +66,7 @@ CLOUD_PROVIDER ?= OBJECT_STORE_PROVIDER ?= INSTALL_VELERO ?= true REGISTRY_CREDENTIAL_FILE ?= +KIBISHII_DIRECTORY ?= # Flags to create an additional BSL for multiple credentials tests ADDITIONAL_BSL_PLUGINS ?= @@ -109,7 +110,8 @@ run: ginkgo -additional-bsl-prefix=$(ADDITIONAL_BSL_PREFIX) \ -additional-bsl-config=$(ADDITIONAL_BSL_CONFIG) \ -install-velero=$(INSTALL_VELERO) \ - -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) + -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ + -kibishii-directory=$(KIBISHII_DIRECTORY) build: ginkgo mkdir -p $(OUTPUT_DIR) diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 21ac1c5098..7bf79b9a1b 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -72,7 +72,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { restoreName = "restore-" + UUIDgen.String() // Even though we are using Velero's CloudProvider plugin for object storage, the kubernetes cluster is running on // KinD. So use the kind installation for Kibishii. - Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile)).To(Succeed(), + Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace") }) @@ -125,7 +125,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { backupName = fmt.Sprintf("%s-%s", backupName, UUIDgen) restoreName = fmt.Sprintf("%s-%s", restoreName, UUIDgen) } - Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, bsl, useVolumeSnapshots, VeleroCfg.RegistryCredentialFile)).To(Succeed(), + Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, bsl, useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace using BSL %s", bsl) } }) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 13c254ff08..0f02ef33e0 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -75,7 +75,7 @@ func backup_deletion_test(useVolumeSnapshots bool) { When("kibishii is the sample workload", func() { It("Deleted backups are deleted from object storage and backups deleted from object storage can be deleted locally", func() { backupName = "backup-" + UUIDgen.String() - Expect(runBackupDeletionTests(client, VeleroCfg.VeleroCLI, VeleroCfg.CloudProvider, VeleroCfg.VeleroNamespace, backupName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig)).To(Succeed(), + Expect(runBackupDeletionTests(client, VeleroCfg.VeleroCLI, VeleroCfg.CloudProvider, VeleroCfg.VeleroNamespace, backupName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, VeleroCfg.KibishiiDirectory)).To(Succeed(), "Failed to run backup deletion test") }) }) @@ -83,7 +83,7 @@ func backup_deletion_test(useVolumeSnapshots bool) { // runUpgradeTests runs upgrade test on the provider by kibishii. func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNamespace, backupName, backupLocation string, - useVolumeSnapshots bool, registryCredentialFile, bslPrefix, bslConfig string) error { + useVolumeSnapshots bool, registryCredentialFile, bslPrefix, bslConfig, kibishiiDirectory string) error { oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) if err := CreateNamespace(oneHourTimeout, client, deletionTest); err != nil { @@ -95,7 +95,7 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa } }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile); err != nil { + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile, kibishiiDirectory); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", deletionTest) } err := ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, backupName, BackupObjectsPrefix, 1) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 78135e8616..92d85123a7 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -140,7 +140,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, - bslDeletionTestNs, VeleroCfg.RegistryCredentialFile)).To(Succeed()) + bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed()) }) // Restic can not backup PV only, so pod need to be labeled also diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 05862e3c8b..588641b0b3 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -55,6 +55,7 @@ func init() { flag.StringVar(&VeleroCfg.VeleroNamespace, "velero-namespace", "velero", "namespace to install Velero into") flag.BoolVar(&VeleroCfg.InstallVelero, "install-velero", true, "install/uninstall velero during the test. Optional.") flag.StringVar(&VeleroCfg.RegistryCredentialFile, "registry-credential-file", "", "file containing credential for the image registry, follows the same format rules as the ~/.docker/config.json file. Optional.") + flag.StringVar(&VeleroCfg.KibishiiDirectory, "kibishii-directory", "github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/", "The file directory or URL path to install Kibishii. Optional.") // Flags to create an additional BSL for multiple credentials test flag.StringVar(&VeleroCfg.AdditionalBSLProvider, "additional-bsl-object-store-provider", "", "Provider of object store plugin for additional backup storage location. Required if testing multiple credentials support.") diff --git a/test/e2e/types.go b/test/e2e/types.go index 48c93c0987..253ea067bc 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -48,6 +48,7 @@ type VerleroConfig struct { Plugins string AddBSLPlugins string InstallVelero bool + KibishiiDirectory string } type SnapshotCheckPoint struct { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 422f1d81be..0a35ea107e 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -122,7 +122,7 @@ func runUpgradeTests(client TestClient, veleroCfg *VerleroConfig, backupName, re fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", upgradeNamespace)) } }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, veleroCfg.CloudProvider, upgradeNamespace, veleroCfg.RegistryCredentialFile); err != nil { + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, veleroCfg.CloudProvider, upgradeNamespace, veleroCfg.RegistryCredentialFile, veleroCfg.KibishiiDirectory); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", upgradeNamespace) } diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 2b58f7cc16..8d5c490407 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -37,7 +37,7 @@ const ( // RunKibishiiTests runs kibishii tests on the provider. func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespace, backupName, restoreName, backupLocation string, - useVolumeSnapshots bool, registryCredentialFile string) error { + useVolumeSnapshots bool, registryCredentialFile string, kibishiiDirectory string) error { oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) if err := CreateNamespace(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", kibishiiNamespace) @@ -47,7 +47,7 @@ func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespac fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", kibishiiNamespace)) } }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile); err != nil { + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile, kibishiiDirectory); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } @@ -90,10 +90,10 @@ func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespac return nil } -func installKibishii(ctx context.Context, namespace string, cloudPlatform string) error { +func installKibishii(ctx context.Context, namespace string, cloudPlatform string, kibishiiDirectory string) error { // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories kibishiiInstallCmd := exec.CommandContext(ctx, "kubectl", "apply", "-n", namespace, "-k", - "github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) + kibishiiDirectory+cloudPlatform) _, stderr, err := veleroexec.RunCommand(kibishiiInstallCmd) if err != nil { return errors.Wrapf(err, "failed to install kibishii, stderr=%s", stderr) @@ -149,7 +149,7 @@ func waitForKibishiiPods(ctx context.Context, client TestClient, kibishiiNamespa return WaitForPods(ctx, client, kibishiiNamespace, []string{"jump-pad", "etcd0", "etcd1", "etcd2", "kibishii-deployment-0", "kibishii-deployment-1"}) } -func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile string) error { +func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, kibishiiDirectory string) error { serviceAccountName := "default" // wait until the service account is created before patch the image pull secret @@ -161,7 +161,7 @@ func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClie return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, kibishiiNamespace) } - if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName); err != nil { + if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, kibishiiDirectory); err != nil { return errors.Wrap(err, "Failed to install Kibishii workload") } From 2939914113e823092fc795b2b85aa4987812386b Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 28 Mar 2022 10:18:15 +0800 Subject: [PATCH 082/366] Ensure the restore hook applied to new namespace based on the mapping fixes #4720 Signed-off-by: Daniel Jiang --- changelogs/unreleased/4779-reasonerjt | 1 + internal/hook/item_hook_handler.go | 8 ++ internal/hook/item_hook_handler_test.go | 94 ++++++++++++++++++++-- pkg/restore/init_restorehook_pod_action.go | 3 +- 4 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/4779-reasonerjt diff --git a/changelogs/unreleased/4779-reasonerjt b/changelogs/unreleased/4779-reasonerjt new file mode 100644 index 0000000000..869257e84d --- /dev/null +++ b/changelogs/unreleased/4779-reasonerjt @@ -0,0 +1 @@ +Ensure the restore hook applied to new namespace based on the mapping \ No newline at end of file diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 5631b3359b..96cb18bc66 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -104,6 +104,7 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( groupResource schema.GroupResource, obj runtime.Unstructured, resourceRestoreHooks []ResourceRestoreHook, + namespaceMapping map[string]string, ) (runtime.Unstructured, error) { // We only support hooks on pods right now if groupResource != kuberesource.Pods { @@ -141,6 +142,13 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( namespace := metadata.GetNamespace() labels := labels.Set(metadata.GetLabels()) + // Apply the hook according to the target namespace in which the pod will be restored + // more details see https://github.com/vmware-tanzu/velero/issues/4720 + if namespaceMapping != nil { + if n, ok := namespaceMapping[namespace]; ok { + namespace = n + } + } for _, rh := range resourceRestoreHooks { if !rh.Selector.applicableTo(groupResource, namespace, labels) { continue diff --git a/internal/hook/item_hook_handler_test.go b/internal/hook/item_hook_handler_test.go index c1704d3650..267c413a11 100644 --- a/internal/hook/item_hook_handler_test.go +++ b/internal/hook/item_hook_handler_test.go @@ -1395,11 +1395,12 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { func TestHandleRestoreHooks(t *testing.T) { testCases := []struct { - name string - podInput corev1api.Pod - restoreHooks []ResourceRestoreHook - expectedPod *corev1api.Pod - expectedError error + name string + podInput corev1api.Pod + restoreHooks []ResourceRestoreHook + namespaceMapping map[string]string + expectedPod *corev1api.Pod + expectedError error }{ { name: "should handle hook from annotation no hooks in spec on pod with no init containers", @@ -1840,6 +1841,87 @@ func TestHandleRestoreHooks(t *testing.T) { }, restoreHooks: []ResourceRestoreHook{}, }, + { + name: "should not apply init container when the namespace mapping is provided and the hook points to the original namespace", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: nil, + expectedPod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("default"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + }, + }, + }, + namespaceMapping: map[string]string{"default": "new"}, + }, + { + name: "should apply init container when the namespace mapping is provided and the hook points to the new namespace", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: nil, + expectedPod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("new"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + }, + }, + }, + namespaceMapping: map[string]string{"default": "new"}, + }, } for _, tc := range testCases { @@ -1847,7 +1929,7 @@ func TestHandleRestoreHooks(t *testing.T) { handler := InitContainerRestoreHookHandler{} podMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.podInput) assert.NoError(t, err) - actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks) + actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks, tc.namespaceMapping) assert.Equal(t, tc.expectedError, err) actualPod := new(corev1api.Pod) err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) diff --git a/pkg/restore/init_restorehook_pod_action.go b/pkg/restore/init_restorehook_pod_action.go index e9cd33d351..f994d811de 100644 --- a/pkg/restore/init_restorehook_pod_action.go +++ b/pkg/restore/init_restorehook_pod_action.go @@ -49,11 +49,12 @@ func (a *InitRestoreHookPodAction) Execute(input *velero.RestoreItemActionExecut a.logger.Infof("Executing InitRestoreHookPodAction") // handle any init container restore hooks for the pod restoreHooks, err := hook.GetRestoreHooksFromSpec(&input.Restore.Spec.Hooks) + nsMapping := input.Restore.Spec.NamespaceMapping if err != nil { return nil, errors.WithStack(err) } hookHandler := hook.InitContainerRestoreHookHandler{} - postHooksItem, err := hookHandler.HandleRestoreHooks(a.logger, kuberesource.Pods, input.Item, restoreHooks) + postHooksItem, err := hookHandler.HandleRestoreHooks(a.logger, kuberesource.Pods, input.Item, restoreHooks, nsMapping) if err != nil { return nil, errors.WithStack(err) } From 54a0ee6496d010cf8b9155326d9e871f007001ab Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 23 Mar 2022 06:45:16 +0000 Subject: [PATCH 083/366] Support multi-upgrade-path in upgrade E2E test Signed-off-by: danfengl --- test/e2e/Makefile | 8 +- test/e2e/upgrade/upgrade.go | 243 ++++++++++++++++++++---------------- 2 files changed, 145 insertions(+), 106 deletions(-) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 9a6209aafa..f84ca3b535 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -54,8 +54,14 @@ VELERO_VERSION ?= $(VERSION) PLUGINS ?= RESTIC_HELPER_IMAGE ?= #Released version only +UPGRADE_FROM_VELERO_VERSION ?= v1.7.1,v1.8.1 +# UPGRADE_FROM_VELERO_CLI can has the same format(a list divided by comma) with UPGRADE_FROM_VELERO_VERSION +# Upgrade tests will be executed sequently according to the list by UPGRADE_FROM_VELERO_VERSION +# So although length of UPGRADE_FROM_VELERO_CLI list is not equal with UPGRADE_FROM_VELERO_VERSION +# Script will still read UPGRADE_FROM_VELERO_CLI list to match UPGRADE_FROM_VELERO_VERSION list from beginning +# to the end, nil string will be set if UPGRADE_FROM_VELERO_CLI is shorter than UPGRADE_FROM_VELERO_VERSION UPGRADE_FROM_VELERO_CLI ?= -UPGRADE_FROM_VELERO_VERSION ?= v1.7.1 + VELERO_NAMESPACE ?= velero CREDS_FILE ?= BSL_BUCKET ?= diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 0a35ea107e..9354d15cc3 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -19,12 +19,12 @@ import ( "context" "flag" "fmt" + "strings" "time" "github.com/google/uuid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/pkg/errors" . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" @@ -32,143 +32,176 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) +type UpgradeFromVelero struct { + UpgradeFromVeleroVersion string + UpgradeFromVeleroCLI string +} + const ( upgradeNamespace = "upgrade-workload" ) +func GetUpgradePathList() []UpgradeFromVelero { + var upgradeFromVeleroList []UpgradeFromVelero + UpgradeFromVeleroVersionList := strings.Split(VeleroCfg.UpgradeFromVeleroVersion, ",") + UpgradeFromVeleroCliList := strings.Split(VeleroCfg.UpgradeFromVeleroCLI, ",") + + for _, upgradeFromVeleroVersion := range UpgradeFromVeleroVersionList { + upgradeFromVeleroList = append(upgradeFromVeleroList, + UpgradeFromVelero{upgradeFromVeleroVersion, ""}) + } + for i, upgradeFromVeleroCli := range UpgradeFromVeleroCliList { + if i == len(UpgradeFromVeleroVersionList)-1 { + break + } + upgradeFromVeleroList[i].UpgradeFromVeleroCLI = upgradeFromVeleroCli + } + return upgradeFromVeleroList +} + func BackupUpgradeRestoreWithSnapshots() { - BackupUpgradeRestoreTest(true) + for _, upgradeFromVelero := range GetUpgradePathList() { + BackupUpgradeRestoreTest(true, upgradeFromVelero) + } } func BackupUpgradeRestoreWithRestic() { - BackupUpgradeRestoreTest(false) + for _, upgradeFromVelero := range GetUpgradePathList() { + BackupUpgradeRestoreTest(false, upgradeFromVelero) + } } -func BackupUpgradeRestoreTest(useVolumeSnapshots bool) { +func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero UpgradeFromVelero) { var ( - backupName, restoreName, upgradeFromVeleroCLI string + backupName, restoreName string ) client, err := NewTestClient() Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") - BeforeEach(func() { + if !VeleroCfg.InstallVelero { + Skip("Upgrade test should not be triggered if VeleroCfg.InstallVelero is set to false") + } if (len(VeleroCfg.UpgradeFromVeleroVersion)) == 0 { Skip("An original velero version is required to run upgrade test, please run test with upgrade-from-velero-version=") } if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { Skip("Volume snapshots not supported on kind") } - - var err error - flag.Parse() - UUIDgen, err = uuid.NewRandom() - Expect(err).To(Succeed()) - if VeleroCfg.InstallVelero { - //Set VeleroImage and ResticHelperImage to blank - //VeleroImage and ResticHelperImage should be the default value in originalCli - tmpCfg := VeleroCfg - tmpCfg.VeleroImage = "" - tmpCfg.ResticHelperImage = "" - tmpCfg.Plugins = "" - //Assume tag of velero server image is identical to velero CLI version - //Download velero CLI if it's empty according to velero CLI version - if (len(VeleroCfg.UpgradeFromVeleroCLI)) == 0 { - tmpCfg.VeleroCLI, err = InstallVeleroCLI(VeleroCfg.UpgradeFromVeleroVersion) - upgradeFromVeleroCLI = tmpCfg.VeleroCLI - Expect(err).To(Succeed()) - } - Expect(VeleroInstall(context.Background(), &tmpCfg, "", useVolumeSnapshots)).To(Succeed()) - Expect(CheckVeleroVersion(context.Background(), tmpCfg.VeleroCLI, tmpCfg.UpgradeFromVeleroVersion)).To(Succeed()) - } else { - Skip("Upgrade test is skipped since user don't want to install any other velero") + if VeleroCfg.VeleroCLI == "" { + Skip("VeleroCLI should be provide") } }) - AfterEach(func() { if VeleroCfg.InstallVelero { - err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) - Expect(err).To(Succeed()) + By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { + DeleteNamespace(context.Background(), client, upgradeNamespace, true) + }) + By("Uninstall Velero", func() { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + }) } }) - When("kibishii is the sample workload", func() { It("should be successfully backed up and restored to the default BackupStorageLocation", func() { + flag.Parse() + UUIDgen, err = uuid.NewRandom() + Expect(err).To(Succeed()) + + if upgradeFromVelero.UpgradeFromVeleroCLI == "" { + //Assume tag of velero server image is identical to velero CLI version + //Download velero CLI if it's empty according to velero CLI version + By(fmt.Sprintf("Install the expected old version Velero CLI (%s) for installing Velero", + upgradeFromVelero.UpgradeFromVeleroVersion), func() { + upgradeFromVelero.UpgradeFromVeleroCLI, err = InstallVeleroCLI(upgradeFromVelero.UpgradeFromVeleroVersion) + Expect(err).To(Succeed()) + }) + } + By(fmt.Sprintf("Install the expected old version Velero (%s) for upgrade", + upgradeFromVelero.UpgradeFromVeleroVersion), func() { + //Set VeleroImage and ResticHelperImage to blank + //VeleroImage and ResticHelperImage should be the default value in originalCli + tmpCfgForOldVeleroInstall := VeleroCfg + tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion + tmpCfgForOldVeleroInstall.VeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI + tmpCfgForOldVeleroInstall.VeleroImage = "" + tmpCfgForOldVeleroInstall.ResticHelperImage = "" + tmpCfgForOldVeleroInstall.Plugins = "" + + Expect(VeleroInstall(context.Background(), &tmpCfgForOldVeleroInstall, "", + false)).To(Succeed()) + Expect(CheckVeleroVersion(context.Background(), tmpCfgForOldVeleroInstall.VeleroCLI, + tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion)).To(Succeed()) + }) + backupName = "backup-" + UUIDgen.String() restoreName = "restore-" + UUIDgen.String() - tmpCfg := VeleroCfg - if (len(VeleroCfg.UpgradeFromVeleroCLI)) == 0 { - tmpCfg.UpgradeFromVeleroCLI = upgradeFromVeleroCLI - Expect(err).To(Succeed()) + tmpCfg1 := VeleroCfg + tmpCfg1.UpgradeFromVeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI + tmpCfg1.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion + oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + + By("Create namespace for sample workload", func() { + Expect(CreateNamespace(oneHourTimeout, client, upgradeNamespace)).To(Succeed(), + fmt.Sprintf("Failed to create namespace %s to install Kibishii workload", upgradeNamespace)) + }) + + By("Deploy sample workload of Kibishii", func() { + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, tmpCfg1.CloudProvider, + upgradeNamespace, tmpCfg1.RegistryCredentialFile, tmpCfg1.KibishiiDirectory)).To(Succeed()) + }) + + By(fmt.Sprintf("Backup namespace %s", upgradeNamespace), func() { + Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg1.UpgradeFromVeleroCLI, + tmpCfg1.VeleroNamespace, backupName, upgradeNamespace, "", + useVolumeSnapshots, "")).ShouldNot(HaveOccurred(), func() string { + err = VeleroBackupLogs(context.Background(), tmpCfg1.UpgradeFromVeleroCLI, + tmpCfg1.VeleroNamespace, backupName) + return "Get backup logs" + }) + }) + + if useVolumeSnapshots && VeleroCfg.CloudProvider == "vsphere" { + // TODO - remove after upload progress monitoring is implemented + By("Waiting for vSphere uploads to complete", func() { + Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, + upgradeNamespace)).To(Succeed()) + }) } - Expect(runUpgradeTests(client, &tmpCfg, backupName, restoreName, "", useVolumeSnapshots)).To(Succeed(), - "Failed to successfully backup and restore Kibishii namespace") - }) - }) -} - -// runUpgradeTests runs upgrade test on the provider by kibishii. -func runUpgradeTests(client TestClient, veleroCfg *VerleroConfig, backupName, restoreName, backupLocation string, - useVolumeSnapshots bool) error { - if veleroCfg.VeleroCLI == "" { - return errors.New("empty") - } - oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) - if err := CreateNamespace(oneHourTimeout, client, upgradeNamespace); err != nil { - return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", upgradeNamespace) - } - defer func() { - if err := DeleteNamespace(context.Background(), client, upgradeNamespace, true); err != nil { - fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", upgradeNamespace)) - } - }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, veleroCfg.CloudProvider, upgradeNamespace, veleroCfg.RegistryCredentialFile, veleroCfg.KibishiiDirectory); err != nil { - return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", upgradeNamespace) - } - - if err := VeleroBackupNamespace(oneHourTimeout, veleroCfg.UpgradeFromVeleroCLI, veleroCfg.VeleroNamespace, backupName, upgradeNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { - // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command - // TODO move to "RunDebug" after we bump up to 1.7 in the upgrade case - VeleroBackupLogs(context.Background(), veleroCfg.UpgradeFromVeleroCLI, veleroCfg.VeleroNamespace, backupName) - return errors.Wrapf(err, "Failed to backup kibishii namespace %s", upgradeNamespace) - } - if veleroCfg.CloudProvider == "vsphere" && useVolumeSnapshots { - // Wait for uploads started by the Velero Plug-in for vSphere to complete - // TODO - remove after upload progress monitoring is implemented - fmt.Println("Waiting for vSphere uploads to complete") - if err := WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, upgradeNamespace); err != nil { - return errors.Wrapf(err, "Error waiting for uploads to complete") - } - } - fmt.Printf("Simulating a disaster by removing namespace %s\n", upgradeNamespace) - if err := DeleteNamespace(oneHourTimeout, client, upgradeNamespace, true); err != nil { - return errors.Wrapf(err, "failed to delete namespace %s", upgradeNamespace) - } - - // the snapshots of AWS may be still in pending status when do the restore, wait for a while - // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 - // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed - if veleroCfg.CloudProvider == "aws" && useVolumeSnapshots { - fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") - time.Sleep(5 * time.Minute) - } - - if err := VeleroInstall(context.Background(), veleroCfg, "", useVolumeSnapshots); err != nil { - return errors.Wrapf(err, "Failed to install velero from image %s", veleroCfg.VeleroImage) - } - if err := CheckVeleroVersion(context.Background(), veleroCfg.VeleroCLI, veleroCfg.VeleroVersion); err != nil { - return errors.Wrapf(err, "Velero install version mismatch.") - } - if err := VeleroRestore(oneHourTimeout, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, restoreName, backupName); err != nil { - RunDebug(context.Background(), veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, "", restoreName) - return errors.Wrapf(err, "Restore %s failed from backup %s", restoreName, backupName) - } - - if err := KibishiiVerifyAfterRestore(client, upgradeNamespace, oneHourTimeout); err != nil { - return errors.Wrapf(err, "Error verifying kibishii after restore") - } + By(fmt.Sprintf("Simulating a disaster by removing namespace %s\n", upgradeNamespace), func() { + Expect(DeleteNamespace(oneHourTimeout, client, upgradeNamespace, true)).To(Succeed(), + fmt.Sprintf("failed to delete namespace %s", upgradeNamespace)) + }) + + // the snapshots of AWS may be still in pending status when do the restore, wait for a while + // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 + // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed + if tmpCfg1.CloudProvider == "aws" && useVolumeSnapshots { + fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") + time.Sleep(5 * time.Minute) + } - fmt.Printf("Upgrade test completed successfully\n") - return nil + By(fmt.Sprintf("Upgrade Velero by CLI %s", tmpCfg1.VeleroCLI), func() { + Expect(VeleroInstall(context.Background(), &tmpCfg1, "", useVolumeSnapshots)).To(Succeed()) + Expect(CheckVeleroVersion(context.Background(), tmpCfg1.VeleroCLI, + tmpCfg1.VeleroVersion)).To(Succeed()) + }) + + By(fmt.Sprintf("Restore %s", upgradeNamespace), func() { + Expect(VeleroRestore(oneHourTimeout, tmpCfg1.VeleroCLI, + tmpCfg1.VeleroNamespace, restoreName, backupName)).To(Succeed(), func() string { + RunDebug(context.Background(), tmpCfg1.VeleroCLI, + tmpCfg1.VeleroNamespace, "", restoreName) + return "Fail to restore workload" + }) + }) + + By(fmt.Sprintf("Verify workload %s after restore ", upgradeNamespace), func() { + Expect(KibishiiVerifyAfterRestore(client, upgradeNamespace, + oneHourTimeout)).To(Succeed(), "Fail to verify workload after restore") + }) + }) + }) } From 2c5c9246e68bdab06827e6dd2f90519b4fdd29f2 Mon Sep 17 00:00:00 2001 From: danfengl Date: Mon, 28 Mar 2022 08:49:07 +0000 Subject: [PATCH 084/366] Add CSI plugin in E2E test Signed-off-by: danfengl --- test/e2e/Makefile | 3 ++ test/e2e/backup/backup.go | 13 +++--- test/e2e/backups/deletion.go | 15 +++++-- test/e2e/bsl-mgmt/deletion.go | 2 +- test/e2e/e2e_suite_test.go | 5 ++- test/e2e/types.go | 1 + test/e2e/upgrade/upgrade.go | 2 +- .../util/csi/AzureVolumeSnapshotClass.yaml | 8 ++++ test/e2e/util/k8s/common.go | 5 +++ test/e2e/util/kibishii/kibishii_utils.go | 43 +++++++++++-------- test/e2e/util/velero/install.go | 9 ++++ 11 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 test/e2e/util/csi/AzureVolumeSnapshotClass.yaml diff --git a/test/e2e/Makefile b/test/e2e/Makefile index f84ca3b535..f6d8e7afe8 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -82,6 +82,8 @@ ADDITIONAL_BSL_BUCKET ?= ADDITIONAL_BSL_PREFIX ?= ADDITIONAL_BSL_CONFIG ?= +FEATURES ?= + .PHONY:ginkgo ginkgo: # Make sure ginkgo is in $GOPATH/bin go get github.com/onsi/ginkgo/ginkgo @@ -115,6 +117,7 @@ run: ginkgo -additional-bsl-bucket=$(ADDITIONAL_BSL_BUCKET) \ -additional-bsl-prefix=$(ADDITIONAL_BSL_PREFIX) \ -additional-bsl-config=$(ADDITIONAL_BSL_CONFIG) \ + -features=$(FEATURES) \ -install-velero=$(INSTALL_VELERO) \ -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ -kibishii-directory=$(KIBISHII_DIRECTORY) diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 7bf79b9a1b..f5f3146d65 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -42,7 +42,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { var ( backupName, restoreName string ) - + kibishiiNamespace := "kibishii-workload" client, err := NewTestClient() Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") @@ -55,11 +55,14 @@ func BackupRestoreTest(useVolumeSnapshots bool) { UUIDgen, err = uuid.NewRandom() Expect(err).To(Succeed()) if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", useVolumeSnapshots)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, "EnableCSI", useVolumeSnapshots)).To(Succeed()) } }) AfterEach(func() { + DeleteNamespace(context.Background(), client, kibishiiNamespace, true) + Expect(err).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", kibishiiNamespace)) + if VeleroCfg.InstallVelero { err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) Expect(err).To(Succeed()) @@ -72,11 +75,11 @@ func BackupRestoreTest(useVolumeSnapshots bool) { restoreName = "restore-" + UUIDgen.String() // Even though we are using Velero's CloudProvider plugin for object storage, the kubernetes cluster is running on // KinD. So use the kind installation for Kibishii. - Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed(), + Expect(RunKibishiiTests(client, VeleroCfg, backupName, restoreName, "", kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace") }) - It("should successfully back up and restore to an additional BackupStorageLocation with unique credentials", func() { + XIt("should successfully back up and restore to an additional BackupStorageLocation with unique credentials", func() { if VeleroCfg.AdditionalBSLProvider == "" { Skip("no additional BSL provider given, not running multiple BackupStorageLocation with unique credentials tests") } @@ -125,7 +128,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { backupName = fmt.Sprintf("%s-%s", backupName, UUIDgen) restoreName = fmt.Sprintf("%s-%s", restoreName, UUIDgen) } - Expect(RunKibishiiTests(client, VeleroCfg.CloudProvider, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backupName, restoreName, bsl, useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed(), + Expect(RunKibishiiTests(client, VeleroCfg, backupName, restoreName, bsl, kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace using BSL %s", bsl) } }) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 0f02ef33e0..d2c216d7dc 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -75,16 +75,23 @@ func backup_deletion_test(useVolumeSnapshots bool) { When("kibishii is the sample workload", func() { It("Deleted backups are deleted from object storage and backups deleted from object storage can be deleted locally", func() { backupName = "backup-" + UUIDgen.String() - Expect(runBackupDeletionTests(client, VeleroCfg.VeleroCLI, VeleroCfg.CloudProvider, VeleroCfg.VeleroNamespace, backupName, "", useVolumeSnapshots, VeleroCfg.RegistryCredentialFile, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, VeleroCfg.KibishiiDirectory)).To(Succeed(), + Expect(runBackupDeletionTests(client, VeleroCfg, backupName, "", useVolumeSnapshots, VeleroCfg.KibishiiDirectory)).To(Succeed(), "Failed to run backup deletion test") }) }) } // runUpgradeTests runs upgrade test on the provider by kibishii. -func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNamespace, backupName, backupLocation string, - useVolumeSnapshots bool, registryCredentialFile, bslPrefix, bslConfig, kibishiiDirectory string) error { +func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupName, backupLocation string, + useVolumeSnapshots bool, kibishiiDirectory string) error { oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + veleroCLI := VeleroCfg.VeleroCLI + providerName := VeleroCfg.CloudProvider + veleroNamespace := VeleroCfg.VeleroNamespace + registryCredentialFile := VeleroCfg.RegistryCredentialFile + bslPrefix := VeleroCfg.BSLPrefix + bslConfig := VeleroCfg.BSLConfig + veleroFeatures := VeleroCfg.Features if err := CreateNamespace(oneHourTimeout, client, deletionTest); err != nil { return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", deletionTest) @@ -95,7 +102,7 @@ func runBackupDeletionTests(client TestClient, veleroCLI, providerName, veleroNa } }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile, kibishiiDirectory); err != nil { + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile, veleroFeatures, kibishiiDirectory); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", deletionTest) } err := ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, backupName, BackupObjectsPrefix, 1) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 92d85123a7..6c7d1b78a1 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -140,7 +140,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, - bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.KibishiiDirectory)).To(Succeed()) + bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, VeleroCfg.KibishiiDirectory)).To(Succeed()) }) // Restic can not backup PV only, so pod need to be labeled also diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 588641b0b3..95900a773e 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -63,14 +63,15 @@ func init() { flag.StringVar(&VeleroCfg.AdditionalBSLPrefix, "additional-bsl-prefix", "", "prefix under which all Velero data should be stored within the bucket for additional backup storage location. Optional.") flag.StringVar(&VeleroCfg.AdditionalBSLConfig, "additional-bsl-config", "", "configuration to use for the additional backup storage location. Format is key1=value1,key2=value2") flag.StringVar(&VeleroCfg.AdditionalBSLCredentials, "additional-bsl-credentials-file", "", "file containing credentials for additional backup storage location provider. Required if testing multiple credentials support.") + flag.StringVar(&VeleroCfg.Features, "features", "", "Comma-separated list of features to enable for this Velero process.") } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) // Test backup and restore of Kibishi using restic -var _ = Describe("[Restic] Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", BackupRestoreWithRestic) +var _ = Describe("[Basic][Restic] Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", BackupRestoreWithRestic) -var _ = Describe("[Snapshot] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreWithSnapshots) +var _ = Describe("[Basic][Snapshot] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreWithSnapshots) var _ = Describe("[Basic] Backup/restore of cluster resources", ResourcesCheckTest) diff --git a/test/e2e/types.go b/test/e2e/types.go index 253ea067bc..3e4cd71688 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -49,6 +49,7 @@ type VerleroConfig struct { AddBSLPlugins string InstallVelero bool KibishiiDirectory string + Features string } type SnapshotCheckPoint struct { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 9354d15cc3..4aa31d8787 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -149,7 +149,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, tmpCfg1.CloudProvider, - upgradeNamespace, tmpCfg1.RegistryCredentialFile, tmpCfg1.KibishiiDirectory)).To(Succeed()) + upgradeNamespace, tmpCfg1.RegistryCredentialFile, tmpCfg1.Features, tmpCfg1.KibishiiDirectory)).To(Succeed()) }) By(fmt.Sprintf("Backup namespace %s", upgradeNamespace), func() { diff --git a/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml b/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml new file mode 100644 index 0000000000..6b0d970eb4 --- /dev/null +++ b/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml @@ -0,0 +1,8 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshotClass +metadata: + name: velero + labels: + velero.io/csi-volumesnapshot-class: "true" +driver: disk.csi.azure.com +deletionPolicy: Retain \ No newline at end of file diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 688084b76a..06d7cdaea3 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -136,3 +136,8 @@ func AddLabelToPod(ctx context.Context, podName, namespace, label string) error fmt.Println(args) return exec.CommandContext(ctx, "kubectl", args...).Run() } + +func KubectlApplyByFile(ctx context.Context, file string) error { + args := []string{"apply", "-f", file, "--force=true"} + return exec.CommandContext(ctx, "kubectl", args...).Run() +} diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 8d5c490407..5b67e795bc 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -20,38 +20,43 @@ import ( "fmt" "os/exec" "strconv" + "strings" "time" "github.com/pkg/errors" - "golang.org/x/net/context" - veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" + . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" + "golang.org/x/net/context" ) const ( - kibishiiNamespace = "kibishii-workload" - jumpPadPod = "jump-pad" + jumpPadPod = "jump-pad" ) // RunKibishiiTests runs kibishii tests on the provider. -func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespace, backupName, restoreName, backupLocation string, - useVolumeSnapshots bool, registryCredentialFile string, kibishiiDirectory string) error { +func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, restoreName, backupLocation, kibishiiNamespace string, + useVolumeSnapshots bool) error { oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + veleroCLI := VeleroCfg.VeleroCLI + providerName := VeleroCfg.CloudProvider + veleroNamespace := VeleroCfg.VeleroNamespace + registryCredentialFile := VeleroCfg.RegistryCredentialFile + veleroFeatures := VeleroCfg.Features + kibishiiDirectory := VeleroCfg.KibishiiDirectory + if err := CreateNamespace(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", kibishiiNamespace) } - defer func() { - if err := DeleteNamespace(context.Background(), client, kibishiiNamespace, true); err != nil { - fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", kibishiiNamespace)) - } - }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile, kibishiiDirectory); err != nil { + + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, + kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, kibishiiNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, + kibishiiNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, backupName, "") return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace) } @@ -90,10 +95,14 @@ func RunKibishiiTests(client TestClient, providerName, veleroCLI, veleroNamespac return nil } -func installKibishii(ctx context.Context, namespace string, cloudPlatform string, kibishiiDirectory string) error { +func installKibishii(ctx context.Context, namespace string, cloudPlatform, veleroFeatures, kibishiiDirectory string) error { + if strings.EqualFold(cloudPlatform, "azure") && strings.EqualFold(veleroFeatures, "EnableCSI") { + cloudPlatform = "azure-csi" + } // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories kibishiiInstallCmd := exec.CommandContext(ctx, "kubectl", "apply", "-n", namespace, "-k", - kibishiiDirectory+cloudPlatform) + //"github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) + "github.com/danfengliu/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) _, stderr, err := veleroexec.RunCommand(kibishiiInstallCmd) if err != nil { return errors.Wrapf(err, "failed to install kibishii, stderr=%s", stderr) @@ -149,7 +158,7 @@ func waitForKibishiiPods(ctx context.Context, client TestClient, kibishiiNamespa return WaitForPods(ctx, client, kibishiiNamespace, []string{"jump-pad", "etcd0", "etcd1", "etcd2", "kibishii-deployment-0", "kibishii-deployment-1"}) } -func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, kibishiiDirectory string) error { +func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory string) error { serviceAccountName := "default" // wait until the service account is created before patch the image pull secret @@ -161,7 +170,7 @@ func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClie return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, kibishiiNamespace) } - if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, kibishiiDirectory); err != nil { + if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, veleroFeatures, kibishiiDirectory); err != nil { return errors.Wrap(err, "Failed to install Kibishii workload") } diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 9c5c30c4af..5d445441f4 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -24,6 +24,7 @@ import ( "io/ioutil" "os" "os/exec" + "strings" "time" "github.com/pkg/errors" @@ -199,6 +200,14 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption } if len(options.Features) > 0 { args = append(args, "--features", options.Features) + if strings.EqualFold(options.Features, "EnableCSI") { + if strings.EqualFold(options.ProviderName, "Azure") { + if err := KubectlApplyByFile(ctx, "util/csi/AzureVolumeSnapshotClass.yaml"); err != nil { + return err + } + } + + } } if err := createVelereResources(ctx, cli, namespace, args, options.RegistryCredentialFile, options.ResticHelperImage); err != nil { From e24c8845c483d6b7a4dee2d4d04b4a8421988949 Mon Sep 17 00:00:00 2001 From: half-life666 Date: Wed, 23 Mar 2022 15:29:52 +0800 Subject: [PATCH 085/366] Skip podvolumerestore creation when restore excludes pv/pvc Signed-off-by: half-life666 --- changelogs/unreleased/4769-half-life666 | 1 + pkg/restore/restore.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4769-half-life666 diff --git a/changelogs/unreleased/4769-half-life666 b/changelogs/unreleased/4769-half-life666 new file mode 100644 index 0000000000..374f5ce3cb --- /dev/null +++ b/changelogs/unreleased/4769-half-life666 @@ -0,0 +1 @@ +Skip podvolumerestore creation when restore excludes pv/pvc diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 7eb8c8136f..67681eea5e 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1296,7 +1296,10 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } - if len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { + // Do not create podvolumerestore when current restore excludes pv/pvc + if ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumeClaims.String()) && + ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumes.String()) && + len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { restorePodVolumeBackups(ctx, createdObj, originalNamespace) } } From 119af85325ebe170c287751f96b409c812afd08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Thu, 31 Mar 2022 17:14:28 +0800 Subject: [PATCH 086/366] Fix bug when pushing the build-image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checkout 2 commits to avoid mismatch of image tag when pushing the build-image Signed-off-by: Wenkai Yin(尹文开) --- .github/workflows/push-builder.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push-builder.yml b/.github/workflows/push-builder.yml index ef257ae8c8..513937a44f 100644 --- a/.github/workflows/push-builder.yml +++ b/.github/workflows/push-builder.yml @@ -12,7 +12,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 + with: + # The default value is "1" which fetches only a single commit. If we merge PR without squash or rebase, + # there are at least two commits: the first one is the merge commit and the second one is the real commit + # contains the changes. + # As we use the Dockerfile's commit ID as the tag of the build-image, fetching only 1 commit causes the merge + # commit ID to be the tag. + # While when running make commands locally, as the local git repository usually contains all commits, the Dockerfile's + # commit ID is the second one. This is mismatch with the images in Dockerhub + fetch-depth: 2 - name: Build run: make build-image From 04f59ce003437269ae8dc9a9496d04df395f5863 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 31 Mar 2022 22:47:22 +0800 Subject: [PATCH 087/366] Bump up to v1 API for CSI snapshot Signed-off-by: Daniel Jiang --- changelogs/unreleased/4800-reasonerjt | 1 + go.mod | 2 +- go.sum | 4 +-- pkg/cmd/cli/backup/describe.go | 12 +++---- pkg/cmd/server/server.go | 34 ++++++++++---------- pkg/cmd/util/output/backup_describer.go | 8 ++--- pkg/controller/backup_controller.go | 20 ++++++------ pkg/controller/backup_deletion_controller.go | 10 +++--- pkg/controller/backup_sync_controller.go | 2 +- pkg/persistence/mocks/backup_store.go | 6 ++-- pkg/persistence/object_store.go | 14 ++++---- 11 files changed, 57 insertions(+), 56 deletions(-) create mode 100644 changelogs/unreleased/4800-reasonerjt diff --git a/changelogs/unreleased/4800-reasonerjt b/changelogs/unreleased/4800-reasonerjt new file mode 100644 index 0000000000..c4b1922ce6 --- /dev/null +++ b/changelogs/unreleased/4800-reasonerjt @@ -0,0 +1 @@ +Bump up to v1 API for CSI snapshot \ No newline at end of file diff --git a/go.mod b/go.mod index fc39dd2b9e..2c7517d35f 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/hashicorp/go-hclog v0.12.0 github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 github.com/joho/godotenv v1.3.0 - github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0 + github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.16.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index fcfe0b81a7..5be785c5fc 100644 --- a/go.sum +++ b/go.sum @@ -451,8 +451,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0 h1:ipLtV9ubLEYx42YvwDa12eVPQvjuGZoPdbCozGzVNRc= -github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= +github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= +github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= diff --git a/pkg/cmd/cli/backup/describe.go b/pkg/cmd/cli/backup/describe.go index f266d64679..82ca472980 100644 --- a/pkg/cmd/cli/backup/describe.go +++ b/pkg/cmd/cli/backup/describe.go @@ -24,8 +24,8 @@ import ( "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" - snapshotv1beta1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -86,17 +86,17 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { fmt.Fprintf(os.Stderr, "error getting PodVolumeBackups for backup %s: %v\n", backup.Name, err) } - var csiClient *snapshotv1beta1client.Clientset + var csiClient *snapshotv1client.Clientset // declare vscList up here since it may be empty and we'll pass the empty Items field into DescribeBackup - vscList := new(snapshotv1beta1api.VolumeSnapshotContentList) + vscList := new(snapshotv1api.VolumeSnapshotContentList) if features.IsEnabled(velerov1api.CSIFeatureFlag) { clientConfig, err := f.ClientConfig() cmd.CheckError(err) - csiClient, err = snapshotv1beta1client.NewForConfig(clientConfig) + csiClient, err = snapshotv1client.NewForConfig(clientConfig) cmd.CheckError(err) - vscList, err = csiClient.SnapshotV1beta1().VolumeSnapshotContents().List(context.TODO(), opts) + vscList, err = csiClient.SnapshotV1().VolumeSnapshotContents().List(context.TODO(), opts) if err != nil { fmt.Fprintf(os.Stderr, "error getting VolumeSnapshotContent objects for backup %s: %v\n", backup.Name, err) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 5a00648c4a..6e53d0dee3 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -45,10 +45,10 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" - snapshotv1beta1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" - snapshotv1beta1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" - snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1beta1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + snapshotv1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/pkg/backup" @@ -230,7 +230,7 @@ type server struct { dynamicClient dynamic.Interface sharedInformerFactory informers.SharedInformerFactory csiSnapshotterSharedInformerFactory *CSIInformerFactoryWrapper - csiSnapshotClient *snapshotv1beta1client.Clientset + csiSnapshotClient *snapshotv1client.Clientset ctx context.Context cancelFunc context.CancelFunc logger logrus.FieldLogger @@ -290,9 +290,9 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s return nil, err } - var csiSnapClient *snapshotv1beta1client.Clientset + var csiSnapClient *snapshotv1client.Clientset if features.IsEnabled(velerov1api.CSIFeatureFlag) { - csiSnapClient, err = snapshotv1beta1client.NewForConfig(clientConfig) + csiSnapClient, err = snapshotv1client.NewForConfig(clientConfig) if err != nil { cancelFunc() return nil, err @@ -535,27 +535,27 @@ func (s *server) initRestic() error { return nil } -func (s *server) getCSISnapshotListers() (snapshotv1beta1listers.VolumeSnapshotLister, snapshotv1beta1listers.VolumeSnapshotContentLister) { +func (s *server) getCSISnapshotListers() (snapshotv1listers.VolumeSnapshotLister, snapshotv1listers.VolumeSnapshotContentLister) { // Make empty listers that will only be populated if CSI is properly enabled. - var vsLister snapshotv1beta1listers.VolumeSnapshotLister - var vscLister snapshotv1beta1listers.VolumeSnapshotContentLister + var vsLister snapshotv1listers.VolumeSnapshotLister + var vscLister snapshotv1listers.VolumeSnapshotContentLister var err error // If CSI is enabled, check for the CSI groups and generate the listers // If CSI isn't enabled, return empty listers. if features.IsEnabled(velerov1api.CSIFeatureFlag) { - _, err = s.discoveryClient.ServerResourcesForGroupVersion(snapshotv1beta1api.SchemeGroupVersion.String()) + _, err = s.discoveryClient.ServerResourcesForGroupVersion(snapshotv1api.SchemeGroupVersion.String()) switch { case apierrors.IsNotFound(err): // CSI is enabled, but the required CRDs aren't installed, so halt. - s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", velerov1api.CSIFeatureFlag, snapshotv1beta1api.SchemeGroupVersion.String()) + s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", velerov1api.CSIFeatureFlag, snapshotv1api.SchemeGroupVersion.String()) case err == nil: // CSI is enabled, and the resources were found. // Instantiate the listers fully s.logger.Debug("Creating CSI listers") // Access the wrapped factory directly here since we've already done the feature flag check above to know it's safe. - vsLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1beta1().VolumeSnapshots().Lister() - vscLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1beta1().VolumeSnapshotContents().Lister() + vsLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshots().Lister() + vscLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshotContents().Lister() case err != nil: cmd.CheckError(err) } @@ -929,16 +929,16 @@ func (s *server) runProfiler() { // CSIInformerFactoryWrapper is a proxy around the CSI SharedInformerFactory that checks the CSI feature flag before performing operations. type CSIInformerFactoryWrapper struct { - factory snapshotv1beta1informers.SharedInformerFactory + factory snapshotv1informers.SharedInformerFactory } -func NewCSIInformerFactoryWrapper(c snapshotv1beta1client.Interface) *CSIInformerFactoryWrapper { +func NewCSIInformerFactoryWrapper(c snapshotv1client.Interface) *CSIInformerFactoryWrapper { // If no namespace is specified, all namespaces are watched. // This is desirable for VolumeSnapshots, as we want to query for all VolumeSnapshots across all namespaces using this informer w := &CSIInformerFactoryWrapper{} if features.IsEnabled(velerov1api.CSIFeatureFlag) { - w.factory = snapshotv1beta1informers.NewSharedInformerFactoryWithOptions(c, 0) + w.factory = snapshotv1informers.NewSharedInformerFactoryWithOptions(c, 0) } return w } diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 86d686ba26..1ec0928316 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -26,7 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/fatih/color" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -45,7 +45,7 @@ func DescribeBackup( backup *velerov1api.Backup, deleteRequests []velerov1api.DeleteBackupRequest, podVolumeBackups []velerov1api.PodVolumeBackup, - volumeSnapshotContents []snapshotv1beta1api.VolumeSnapshotContent, + volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, @@ -513,7 +513,7 @@ func (v *volumesByPod) Sorted() []*podVolumeGroup { return v.volumesByPodSlice } -func DescribeCSIVolumeSnapshots(d *Describer, details bool, volumeSnapshotContents []snapshotv1beta1api.VolumeSnapshotContent) { +func DescribeCSIVolumeSnapshots(d *Describer, details bool, volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent) { if !features.IsEnabled(velerov1api.CSIFeatureFlag) { return } @@ -535,7 +535,7 @@ func DescribeCSIVolumeSnapshots(d *Describer, details bool, volumeSnapshotConten } } -func DescribeVSC(d *Describer, details bool, vsc snapshotv1beta1api.VolumeSnapshotContent) { +func DescribeVSC(d *Describer, details bool, vsc snapshotv1api.VolumeSnapshotContent) { if vsc.Status == nil { d.Printf("Volume Snapshot Content %s cannot be described because its status is nil\n", vsc.Name) return diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 6adfeeec77..2de400d605 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -38,8 +38,8 @@ import ( kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/tools/cache" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" - snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1beta1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -83,8 +83,8 @@ type backupController struct { metrics *metrics.ServerMetrics backupStoreGetter persistence.ObjectBackupStoreGetter formatFlag logging.Format - volumeSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister - volumeSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister + volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister + volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister } func NewBackupController( @@ -104,8 +104,8 @@ func NewBackupController( defaultSnapshotLocations map[string]string, metrics *metrics.ServerMetrics, formatFlag logging.Format, - volumeSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, - volumeSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, + volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, + volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, backupStoreGetter persistence.ObjectBackupStoreGetter, ) Interface { c := &backupController{ @@ -602,8 +602,8 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { // Empty slices here so that they can be passed in to the persistBackup call later, regardless of whether or not CSI's enabled. // This way, we only make the Lister call if the feature flag's on. - var volumeSnapshots []*snapshotv1beta1api.VolumeSnapshot - var volumeSnapshotContents []*snapshotv1beta1api.VolumeSnapshotContent + var volumeSnapshots []*snapshotv1api.VolumeSnapshot + var volumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent if features.IsEnabled(velerov1api.CSIFeatureFlag) { selector := label.NewSelectorForBackup(backup.Name) @@ -700,8 +700,8 @@ func persistBackup(backup *pkgbackup.Request, backupContents, backupLog *os.File, backupStore persistence.BackupStore, log logrus.FieldLogger, - csiVolumeSnapshots []*snapshotv1beta1api.VolumeSnapshot, - csiVolumeSnapshotContents []*snapshotv1beta1api.VolumeSnapshotContent, + csiVolumeSnapshots []*snapshotv1api.VolumeSnapshot, + csiVolumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent, ) []error { persistErrs := []error{} backupJSON := new(bytes.Buffer) diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 90707be704..2ca0fda470 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -24,7 +24,7 @@ import ( jsonpatch "github.com/evanphx/json-patch" snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" - snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1beta1" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -69,8 +69,8 @@ type backupDeletionController struct { podvolumeBackupLister velerov1listers.PodVolumeBackupLister kbClient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister - csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister - csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister + csiSnapshotLister snapshotv1listers.VolumeSnapshotLister + csiSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister csiSnapshotClient *snapshotterClientSet.Clientset processRequestFunc func(*velerov1api.DeleteBackupRequest) error clock clock.Clock @@ -93,8 +93,8 @@ func NewBackupDeletionController( podvolumeBackupLister velerov1listers.PodVolumeBackupLister, kbClient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, - csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, - csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, + csiSnapshotLister snapshotv1listers.VolumeSnapshotLister, + csiSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, csiSnapshotClient *snapshotterClientSet.Clientset, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupStoreGetter persistence.ObjectBackupStoreGetter, diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index aeb848ac0d..9107449cce 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -294,7 +294,7 @@ func (c *backupSyncController) run() { for _, snapCont := range snapConts { // TODO: Reset ResourceVersion prior to persisting VolumeSnapshotContents snapCont.ResourceVersion = "" - created, err := c.csiSnapshotClient.SnapshotV1beta1().VolumeSnapshotContents().Create(context.TODO(), snapCont, metav1.CreateOptions{}) + created, err := c.csiSnapshotClient.SnapshotV1().VolumeSnapshotContents().Create(context.TODO(), snapCont, metav1.CreateOptions{}) switch { case err != nil && kuberrs.IsAlreadyExists(err): log.Debugf("volumesnapshotcontent %s already exists in cluster", snapCont.Name) diff --git a/pkg/persistence/mocks/backup_store.go b/pkg/persistence/mocks/backup_store.go index b41125e56a..9a6eefacfc 100644 --- a/pkg/persistence/mocks/backup_store.go +++ b/pkg/persistence/mocks/backup_store.go @@ -21,7 +21,7 @@ import ( mock "github.com/stretchr/testify/mock" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" persistence "github.com/vmware-tanzu/velero/pkg/persistence" @@ -274,12 +274,12 @@ func (_m *BackupStore) PutRestoreResults(backup string, restore string, results return r0 } -func (_m *BackupStore) GetCSIVolumeSnapshots(backup string) ([]*snapshotv1beta1api.VolumeSnapshot, error) { +func (_m *BackupStore) GetCSIVolumeSnapshots(backup string) ([]*snapshotv1api.VolumeSnapshot, error) { panic("Not implemented") return nil, nil } -func (_m *BackupStore) GetCSIVolumeSnapshotContents(backup string) ([]*snapshotv1beta1api.VolumeSnapshotContent, error) { +func (_m *BackupStore) GetCSIVolumeSnapshotContents(backup string) ([]*snapshotv1api.VolumeSnapshotContent, error) { panic("Not implemented") return nil, nil } diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 744f2baaaf..b60b9491b3 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -24,7 +24,7 @@ import ( "strings" "time" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -63,8 +63,8 @@ type BackupStore interface { GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error) GetPodVolumeBackups(name string) ([]*velerov1api.PodVolumeBackup, error) GetBackupContents(name string) (io.ReadCloser, error) - GetCSIVolumeSnapshots(name string) ([]*snapshotv1beta1api.VolumeSnapshot, error) - GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1beta1api.VolumeSnapshotContent, error) + GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error) + GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1api.VolumeSnapshotContent, error) // BackupExists checks if the backup metadata file exists in object storage. BackupExists(bucket, backupName string) (bool, error) @@ -371,7 +371,7 @@ func decode(jsongzReader io.Reader, into interface{}) error { return nil } -func (s *objectBackupStore) GetCSIVolumeSnapshots(name string) ([]*snapshotv1beta1api.VolumeSnapshot, error) { +func (s *objectBackupStore) GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error) { res, err := tryGet(s.objectStore, s.bucket, s.layout.getCSIVolumeSnapshotKey(name)) if err != nil { return nil, err @@ -382,14 +382,14 @@ func (s *objectBackupStore) GetCSIVolumeSnapshots(name string) ([]*snapshotv1bet } defer res.Close() - var csiSnaps []*snapshotv1beta1api.VolumeSnapshot + var csiSnaps []*snapshotv1api.VolumeSnapshot if err := decode(res, &csiSnaps); err != nil { return nil, err } return csiSnaps, nil } -func (s *objectBackupStore) GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1beta1api.VolumeSnapshotContent, error) { +func (s *objectBackupStore) GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1api.VolumeSnapshotContent, error) { res, err := tryGet(s.objectStore, s.bucket, s.layout.getCSIVolumeSnapshotContentsKey(name)) if err != nil { return nil, err @@ -400,7 +400,7 @@ func (s *objectBackupStore) GetCSIVolumeSnapshotContents(name string) ([]*snapsh } defer res.Close() - var snapConts []*snapshotv1beta1api.VolumeSnapshotContent + var snapConts []*snapshotv1api.VolumeSnapshotContent if err := decode(res, &snapConts); err != nil { return nil, err } From 658699636dd3e931b4dee7e0398c35221aad6a38 Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 30 Mar 2022 06:06:58 +0000 Subject: [PATCH 088/366] Support CSI plugin in E2E test Signed-off-by: danfengl --- .github/workflows/e2e-test-kind.yaml | 2 +- test/e2e/Makefile | 3 +- test/e2e/backup/backup.go | 8 ++- test/e2e/backups/deletion.go | 5 +- test/e2e/backups/sync_backups.go | 4 +- test/e2e/basic/enable_api_group_versions.go | 3 +- test/e2e/bsl-mgmt/deletion.go | 9 ++- test/e2e/e2e_suite_test.go | 4 +- test/e2e/privilegesmgmt/ssr.go | 2 +- test/e2e/test/test.go | 4 +- test/e2e/upgrade/upgrade.go | 42 ++++++------ .../util/csi/AzureVolumeSnapshotClass.yaml | 2 +- test/e2e/util/kibishii/kibishii_utils.go | 25 ++++--- test/e2e/util/velero/install.go | 8 +-- test/e2e/util/velero/velero_utils.go | 67 +++++++++++-------- 15 files changed, 108 insertions(+), 80 deletions(-) diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 38fcd1d2af..f889f9e7cf 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -121,7 +121,7 @@ jobs: CREDS_FILE=/tmp/credential BSL_BUCKET=bucket \ ADDITIONAL_OBJECT_STORE_PROVIDER=aws ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ ADDITIONAL_CREDS_FILE=/tmp/credential ADDITIONAL_BSL_BUCKET=additional-bucket \ - GINKGO_FOCUS=Basic VELERO_IMAGE=velero:pr-test \ + GINKGO_FOCUS='Basic\].+\[ClusterResource' VELERO_IMAGE=velero:pr-test \ make -C test/e2e run timeout-minutes: 30 - name: Upload debug bundle diff --git a/test/e2e/Makefile b/test/e2e/Makefile index f6d8e7afe8..79af0d5712 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -72,7 +72,8 @@ CLOUD_PROVIDER ?= OBJECT_STORE_PROVIDER ?= INSTALL_VELERO ?= true REGISTRY_CREDENTIAL_FILE ?= -KIBISHII_DIRECTORY ?= +KIBISHII_DIRECTORY ?= github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/ + # Flags to create an additional BSL for multiple credentials tests ADDITIONAL_BSL_PLUGINS ?= diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index f5f3146d65..730f6abf93 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -55,7 +55,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { UUIDgen, err = uuid.NewRandom() Expect(err).To(Succeed()) if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "EnableCSI", useVolumeSnapshots)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) } }) @@ -79,7 +79,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { "Failed to successfully backup and restore Kibishii namespace") }) - XIt("should successfully back up and restore to an additional BackupStorageLocation with unique credentials", func() { + It("should successfully back up and restore to an additional BackupStorageLocation with unique credentials", func() { if VeleroCfg.AdditionalBSLProvider == "" { Skip("no additional BSL provider given, not running multiple BackupStorageLocation with unique credentials tests") } @@ -92,7 +92,9 @@ func BackupRestoreTest(useVolumeSnapshots bool) { Skip("no additional BSL credentials given, not running multiple BackupStorageLocation with unique credentials tests") } - Expect(VeleroAddPluginsForProvider(context.TODO(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, VeleroCfg.AdditionalBSLProvider, VeleroCfg.AddBSLPlugins)).To(Succeed()) + Expect(VeleroAddPluginsForProvider(context.TODO(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, VeleroCfg.AdditionalBSLProvider, + VeleroCfg.AddBSLPlugins, VeleroCfg.Features)).To(Succeed()) // Create Secret for additional BSL secretName := fmt.Sprintf("bsl-credentials-%s", UUIDgen) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index d2c216d7dc..a1d7a0cc39 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -61,7 +61,7 @@ func backup_deletion_test(useVolumeSnapshots bool) { UUIDgen, err = uuid.NewRandom() Expect(err).To(Succeed()) if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", useVolumeSnapshots)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) } }) @@ -102,7 +102,8 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa } }() - if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile, veleroFeatures, kibishiiDirectory); err != nil { + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, + registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", deletionTest) } err := ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, backupName, BackupObjectsPrefix, 1) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index ae0756ff9b..a329b51147 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -61,7 +61,7 @@ func BackupsSyncTest() { BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) } }) @@ -94,7 +94,7 @@ func BackupsSyncTest() { By("Install velero", func() { VeleroCfg.ObjectStoreProvider = "" - Expect(VeleroInstall(test.ctx, &VeleroCfg, "", false)).To(Succeed()) + Expect(VeleroInstall(test.ctx, &VeleroCfg, false)).To(Succeed()) }) By("Check all backups in object storage are synced to Velero", func() { diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index a251b278d8..46a7aeb268 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -59,7 +59,8 @@ func APIGropuVersionsTest() { // TODO: install Velero once for the test suite once feature flag is // removed and velero installation becomes the same as other e2e tests. if VeleroCfg.InstallVelero { - err = VeleroInstall(context.Background(), &VeleroCfg, "EnableAPIGroupVersions", false) + VeleroCfg.Features = "EnableAPIGroupVersions" + err = VeleroInstall(context.Background(), &VeleroCfg, false) Expect(err).NotTo(HaveOccurred()) } }) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 6c7d1b78a1..5bc6d35959 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -64,7 +64,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { UUIDgen, err = uuid.NewRandom() Expect(err).To(Succeed()) if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", useVolumeSnapshots)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) } }) @@ -93,7 +93,9 @@ func BslDeletionTest(useVolumeSnapshots bool) { } By(fmt.Sprintf("Add an additional plugin for provider %s", VeleroCfg.AdditionalBSLProvider), func() { - Expect(VeleroAddPluginsForProvider(context.TODO(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, VeleroCfg.AdditionalBSLProvider, VeleroCfg.AddBSLPlugins)).To(Succeed()) + Expect(VeleroAddPluginsForProvider(context.TODO(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, VeleroCfg.AdditionalBSLProvider, + VeleroCfg.AddBSLPlugins, VeleroCfg.Features)).To(Succeed()) }) additionalBsl := fmt.Sprintf("bsl-%s", UUIDgen) @@ -140,7 +142,8 @@ func BslDeletionTest(useVolumeSnapshots bool) { By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, - bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, VeleroCfg.KibishiiDirectory)).To(Succeed()) + bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, + VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) }) // Restic can not backup PV only, so pod need to be labeled also diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 95900a773e..9186ca3aa9 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -56,7 +56,7 @@ func init() { flag.BoolVar(&VeleroCfg.InstallVelero, "install-velero", true, "install/uninstall velero during the test. Optional.") flag.StringVar(&VeleroCfg.RegistryCredentialFile, "registry-credential-file", "", "file containing credential for the image registry, follows the same format rules as the ~/.docker/config.json file. Optional.") flag.StringVar(&VeleroCfg.KibishiiDirectory, "kibishii-directory", "github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/", "The file directory or URL path to install Kibishii. Optional.") - + //vmware-tanzu-experiments // Flags to create an additional BSL for multiple credentials test flag.StringVar(&VeleroCfg.AdditionalBSLProvider, "additional-bsl-object-store-provider", "", "Provider of object store plugin for additional backup storage location. Required if testing multiple credentials support.") flag.StringVar(&VeleroCfg.AdditionalBSLBucket, "additional-bsl-bucket", "", "name of the object storage bucket for additional backup storage location. Required if testing multiple credentials support.") @@ -73,7 +73,7 @@ var _ = Describe("[Basic][Restic] Velero tests on cluster using the plugin provi var _ = Describe("[Basic][Snapshot] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreWithSnapshots) -var _ = Describe("[Basic] Backup/restore of cluster resources", ResourcesCheckTest) +var _ = Describe("[Basic][ClusterResource] Backup/restore of cluster resources", ResourcesCheckTest) var _ = Describe("[Scale] Backup/restore of 2500 namespaces", MultiNSBackupRestore) diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index 91af0f8b85..40be8c1587 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -45,7 +45,7 @@ func SSRTest() { BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) } }) diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 5648c06def..de27e2bb6b 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -79,7 +79,7 @@ func TestFunc(test VeleroBackupRestoreTest) func() { BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) } }) AfterEach(func() { @@ -107,7 +107,7 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { flag.Parse() if VeleroCfg.InstallVelero { if countIt == 0 { - Expect(VeleroInstall(context.Background(), &VeleroCfg, "", false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) } countIt++ } diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 4aa31d8787..5bbb7c21f1 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -118,6 +118,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade Expect(err).To(Succeed()) }) } + By(fmt.Sprintf("Install the expected old version Velero (%s) for upgrade", upgradeFromVelero.UpgradeFromVeleroVersion), func() { //Set VeleroImage and ResticHelperImage to blank @@ -129,17 +130,17 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade tmpCfgForOldVeleroInstall.ResticHelperImage = "" tmpCfgForOldVeleroInstall.Plugins = "" - Expect(VeleroInstall(context.Background(), &tmpCfgForOldVeleroInstall, "", - false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &tmpCfgForOldVeleroInstall, + useVolumeSnapshots)).To(Succeed()) Expect(CheckVeleroVersion(context.Background(), tmpCfgForOldVeleroInstall.VeleroCLI, tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion)).To(Succeed()) }) backupName = "backup-" + UUIDgen.String() restoreName = "restore-" + UUIDgen.String() - tmpCfg1 := VeleroCfg - tmpCfg1.UpgradeFromVeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI - tmpCfg1.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion + tmpCfg := VeleroCfg + tmpCfg.UpgradeFromVeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI + tmpCfg.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) By("Create namespace for sample workload", func() { @@ -148,16 +149,17 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade }) By("Deploy sample workload of Kibishii", func() { - Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, tmpCfg1.CloudProvider, - upgradeNamespace, tmpCfg1.RegistryCredentialFile, tmpCfg1.Features, tmpCfg1.KibishiiDirectory)).To(Succeed()) + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, tmpCfg.CloudProvider, + upgradeNamespace, tmpCfg.RegistryCredentialFile, tmpCfg.Features, + tmpCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) }) By(fmt.Sprintf("Backup namespace %s", upgradeNamespace), func() { - Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg1.UpgradeFromVeleroCLI, - tmpCfg1.VeleroNamespace, backupName, upgradeNamespace, "", + Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg.UpgradeFromVeleroCLI, + tmpCfg.VeleroNamespace, backupName, upgradeNamespace, "", useVolumeSnapshots, "")).ShouldNot(HaveOccurred(), func() string { - err = VeleroBackupLogs(context.Background(), tmpCfg1.UpgradeFromVeleroCLI, - tmpCfg1.VeleroNamespace, backupName) + err = VeleroBackupLogs(context.Background(), tmpCfg.UpgradeFromVeleroCLI, + tmpCfg.VeleroNamespace, backupName) return "Get backup logs" }) }) @@ -178,22 +180,22 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade // the snapshots of AWS may be still in pending status when do the restore, wait for a while // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed - if tmpCfg1.CloudProvider == "aws" && useVolumeSnapshots { + if tmpCfg.CloudProvider == "aws" && useVolumeSnapshots { fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") time.Sleep(5 * time.Minute) } - By(fmt.Sprintf("Upgrade Velero by CLI %s", tmpCfg1.VeleroCLI), func() { - Expect(VeleroInstall(context.Background(), &tmpCfg1, "", useVolumeSnapshots)).To(Succeed()) - Expect(CheckVeleroVersion(context.Background(), tmpCfg1.VeleroCLI, - tmpCfg1.VeleroVersion)).To(Succeed()) + By(fmt.Sprintf("Upgrade Velero by CLI %s", tmpCfg.VeleroCLI), func() { + Expect(VeleroInstall(context.Background(), &tmpCfg, useVolumeSnapshots)).To(Succeed()) + Expect(CheckVeleroVersion(context.Background(), tmpCfg.VeleroCLI, + tmpCfg.VeleroVersion)).To(Succeed()) }) By(fmt.Sprintf("Restore %s", upgradeNamespace), func() { - Expect(VeleroRestore(oneHourTimeout, tmpCfg1.VeleroCLI, - tmpCfg1.VeleroNamespace, restoreName, backupName)).To(Succeed(), func() string { - RunDebug(context.Background(), tmpCfg1.VeleroCLI, - tmpCfg1.VeleroNamespace, "", restoreName) + Expect(VeleroRestore(oneHourTimeout, tmpCfg.VeleroCLI, + tmpCfg.VeleroNamespace, restoreName, backupName)).To(Succeed(), func() string { + RunDebug(context.Background(), tmpCfg.VeleroCLI, + tmpCfg.VeleroNamespace, "", restoreName) return "Fail to restore workload" }) }) diff --git a/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml b/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml index 6b0d970eb4..4e3ca494f3 100644 --- a/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml +++ b/test/e2e/util/csi/AzureVolumeSnapshotClass.yaml @@ -5,4 +5,4 @@ metadata: labels: velero.io/csi-volumesnapshot-class: "true" driver: disk.csi.azure.com -deletionPolicy: Retain \ No newline at end of file +deletionPolicy: Delete \ No newline at end of file diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 5b67e795bc..7ad137cbc1 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -24,11 +24,12 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/net/context" + veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" - "golang.org/x/net/context" ) const ( @@ -51,7 +52,8 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re } if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, - kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory); err != nil { + kibishiiNamespace, registryCredentialFile, veleroFeatures, + kibishiiDirectory, useVolumeSnapshots); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } @@ -95,15 +97,19 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re return nil } -func installKibishii(ctx context.Context, namespace string, cloudPlatform, veleroFeatures, kibishiiDirectory string) error { - if strings.EqualFold(cloudPlatform, "azure") && strings.EqualFold(veleroFeatures, "EnableCSI") { +func installKibishii(ctx context.Context, namespace string, cloudPlatform, veleroFeatures, + kibishiiDirectory string, useVolumeSnapshots bool) error { + if strings.EqualFold(cloudPlatform, "azure") && + strings.EqualFold(veleroFeatures, "EnableCSI") && + useVolumeSnapshots { cloudPlatform = "azure-csi" } // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories kibishiiInstallCmd := exec.CommandContext(ctx, "kubectl", "apply", "-n", namespace, "-k", - //"github.com/vmware-tanzu-experiments/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) - "github.com/danfengliu/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) + kibishiiDirectory+cloudPlatform) + _, stderr, err := veleroexec.RunCommand(kibishiiInstallCmd) + fmt.Printf("Install Kibishii cmd: %s\n", kibishiiInstallCmd) if err != nil { return errors.Wrapf(err, "failed to install kibishii, stderr=%s", stderr) } @@ -158,7 +164,9 @@ func waitForKibishiiPods(ctx context.Context, client TestClient, kibishiiNamespa return WaitForPods(ctx, client, kibishiiNamespace, []string{"jump-pad", "etcd0", "etcd1", "etcd2", "kibishii-deployment-0", "kibishii-deployment-1"}) } -func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory string) error { +func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, + providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, + kibishiiDirectory string, useVolumeSnapshots bool) error { serviceAccountName := "default" // wait until the service account is created before patch the image pull secret @@ -170,7 +178,8 @@ func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClie return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, kibishiiNamespace) } - if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, veleroFeatures, kibishiiDirectory); err != nil { + if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, veleroFeatures, + kibishiiDirectory, useVolumeSnapshots); err != nil { return errors.Wrap(err, "Failed to install Kibishii workload") } diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 5d445441f4..30b1b1c427 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -49,7 +49,7 @@ type installOptions struct { ResticHelperImage string } -func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, features string, useVolumeSnapshots bool) error { +func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnapshots bool) error { if veleroCfg.CloudProvider != "kind" { if veleroCfg.ObjectStoreProvider != "" { return errors.New("For cloud platforms, object store plugin cannot be overridden") // Can't set an object store provider that is different than your cloud @@ -61,7 +61,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, features strin } } - providerPluginsTmp, err := getProviderPlugins(ctx, veleroCfg.VeleroCLI, veleroCfg.ObjectStoreProvider, veleroCfg.Plugins) + providerPluginsTmp, err := getProviderPlugins(ctx, veleroCfg.VeleroCLI, veleroCfg.ObjectStoreProvider, veleroCfg.Plugins, veleroCfg.Features) if err != nil { return errors.WithMessage(err, "Failed to get provider plugins") } @@ -82,7 +82,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, features strin } veleroInstallOptions, err := getProviderVeleroInstallOptions(veleroCfg.ObjectStoreProvider, veleroCfg.CloudCredentialsFile, veleroCfg.BSLBucket, - veleroCfg.BSLPrefix, veleroCfg.BSLConfig, veleroCfg.VSLConfig, providerPluginsTmp, features) + veleroCfg.BSLPrefix, veleroCfg.BSLConfig, veleroCfg.VSLConfig, providerPluginsTmp, veleroCfg.Features) if err != nil { return errors.WithMessagef(err, "Failed to get Velero InstallOptions for plugin provider %s", veleroCfg.ObjectStoreProvider) } @@ -200,7 +200,7 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption } if len(options.Features) > 0 { args = append(args, "--features", options.Features) - if strings.EqualFold(options.Features, "EnableCSI") { + if strings.EqualFold(options.Features, "EnableCSI") && options.UseVolumeSnapshots { if strings.EqualFold(options.ProviderName, "Azure") { if err := KubectlApplyByFile(ctx, "util/csi/AzureVolumeSnapshotClass.yaml"); err != nil { return err diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 807bf4029c..4ca48d4152 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -48,44 +48,50 @@ const PluginsObjectsPrefix = "plugins" var pluginsMatrix = map[string]map[string][]string{ "v1.4": { - "aws": {"velero/velero-plugin-for-aws:v1.1.0"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.1.2"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.1.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.0.2"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.1.0"}, + "aws": {"velero/velero-plugin-for-aws:v1.1.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.1.2"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.1.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.0.2"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.1.0"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.1.2", "velero/velero-plugin-for-csi:v0.1.1 "}, }, "v1.5": { - "aws": {"velero/velero-plugin-for-aws:v1.1.0"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.1.2"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.1.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.1.1"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.1.0"}, + "aws": {"velero/velero-plugin-for-aws:v1.1.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.1.2"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.1.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.1.1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.1.0"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.1.2", "velero/velero-plugin-for-csi:v0.1.2 "}, }, "v1.6": { - "aws": {"velero/velero-plugin-for-aws:v1.2.1"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.2.1"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.2.1", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.1.1"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.2.1"}, + "aws": {"velero/velero-plugin-for-aws:v1.2.1"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.2.1"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.2.1", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.1.1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.2.1"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.3.0", "velero/velero-plugin-for-csi:v0.1.2 "}, }, "v1.7": { - "aws": {"velero/velero-plugin-for-aws:v1.3.0"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.3.0"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.3.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.0"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.3.0"}, + "aws": {"velero/velero-plugin-for-aws:v1.3.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.3.0"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.3.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.0"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.3.0"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.3.0", "velero/velero-plugin-for-csi:v0.2.0"}, }, "v1.8": { - "aws": {"velero/velero-plugin-for-aws:v1.4.0"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0"}, - "vsphere": {"velero/velero-plugin-for-aws:v1.4.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, - "gcp": {"velero/velero-plugin-for-gcp:v1.4.0"}, + "aws": {"velero/velero-plugin-for-aws:v1.4.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.4.0"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.4.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.4.0"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.4.0", "velero/velero-plugin-for-csi:v0.2.0"}, }, "main": { - "aws": {"velero/velero-plugin-for-aws:main"}, - "azure": {"velero/velero-plugin-for-microsoft-azure:main"}, - "vsphere": {"velero/velero-plugin-for-aws:main", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, - "gcp": {"velero/velero-plugin-for-gcp:main"}, + "aws": {"velero/velero-plugin-for-aws:main"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:main"}, + "vsphere": {"velero/velero-plugin-for-aws:main", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.3.1"}, + "gcp": {"velero/velero-plugin-for-gcp:main"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:main", "velero/velero-plugin-for-csi:main"}, }, } -func getProviderPluginsByVersion(version, providerName string) ([]string, error) { +func GetProviderPluginsByVersion(version, providerName, feature string) ([]string, error) { var cloudMap map[string][]string arr := strings.Split(version, ".") if len(arr) >= 3 { @@ -97,6 +103,9 @@ func getProviderPluginsByVersion(version, providerName string) ([]string, error) return nil, errors.Errorf("fail to get plugins by version: main") } } + if strings.EqualFold(providerName, "azure") && strings.EqualFold(feature, "EnableCSI") { + providerName = "azure-csi" + } plugins, ok := cloudMap[providerName] if !ok { return nil, errors.Errorf("fail to get plugins by version: %s and provider %s", version, providerName) @@ -380,7 +389,7 @@ func VeleroCreateBackupLocation(ctx context.Context, return VeleroCmdExec(ctx, veleroCLI, args) } -func getProviderPlugins(ctx context.Context, veleroCLI, objectStoreProvider, providerPlugins string) ([]string, error) { +func getProviderPlugins(ctx context.Context, veleroCLI, objectStoreProvider, providerPlugins, feature string) ([]string, error) { // Fetch the plugins for the provider before checking for the object store provider below. var plugins []string if len(providerPlugins) > 0 { @@ -390,7 +399,7 @@ func getProviderPlugins(ctx context.Context, veleroCLI, objectStoreProvider, pro if err != nil { return nil, errors.WithMessage(err, "failed to get velero version") } - plugins, err = getProviderPluginsByVersion(version, objectStoreProvider) + plugins, err = GetProviderPluginsByVersion(version, objectStoreProvider, feature) if err != nil { return nil, errors.WithMessagef(err, "Fail to get plugin by provider %s and version %s", objectStoreProvider, version) } @@ -400,8 +409,8 @@ func getProviderPlugins(ctx context.Context, veleroCLI, objectStoreProvider, pro // VeleroAddPluginsForProvider determines which plugins need to be installed for a provider and // installs them in the current Velero installation, skipping over those that are already installed. -func VeleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNamespace string, provider string, addPlugins string) error { - plugins, err := getProviderPlugins(ctx, veleroCLI, provider, addPlugins) +func VeleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNamespace string, provider string, addPlugins, feature string) error { + plugins, err := getProviderPlugins(ctx, veleroCLI, provider, addPlugins, feature) fmt.Printf("addPlugins cmd =%v\n", addPlugins) fmt.Printf("provider cmd = %v\n", provider) fmt.Printf("plugins cmd = %v\n", plugins) From d5e299779a67cb3ebe5d6687e4e181495ed12313 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Fri, 1 Apr 2022 14:54:58 -0400 Subject: [PATCH 089/366] Rename binary generated by go install cmd/* - go install cmd/velero/velero.go - go install cmd/velero-restic-restore-helper/velero-restic-restore-helper.go Will generate binary in `$(go env GOPATH)/bin/` with the correct name. build.sh still works the same. Signed-off-by: Tiger Kaovilai --- .../{main.go => velero-restic-restore-helper.go} | 0 cmd/velero/{main.go => velero.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cmd/velero-restic-restore-helper/{main.go => velero-restic-restore-helper.go} (100%) rename cmd/velero/{main.go => velero.go} (100%) diff --git a/cmd/velero-restic-restore-helper/main.go b/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go similarity index 100% rename from cmd/velero-restic-restore-helper/main.go rename to cmd/velero-restic-restore-helper/velero-restic-restore-helper.go diff --git a/cmd/velero/main.go b/cmd/velero/velero.go similarity index 100% rename from cmd/velero/main.go rename to cmd/velero/velero.go From 7b35f94f84b47a95810f2037a08aa7d300e3320d Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 6 Apr 2022 03:53:20 +0000 Subject: [PATCH 090/366] Add debug switcher for E2E test Signed-off-by: danfengl --- test/e2e/Makefile | 4 +++- test/e2e/backup/backup.go | 9 ++++----- test/e2e/backups/deletion.go | 6 ++++-- test/e2e/backups/sync_backups.go | 4 +++- test/e2e/basic/enable_api_group_versions.go | 6 ++++-- test/e2e/bsl-mgmt/deletion.go | 12 +++++++----- test/e2e/e2e_suite_test.go | 1 + test/e2e/privilegesmgmt/ssr.go | 4 +++- test/e2e/test/test.go | 6 ++++-- test/e2e/types.go | 1 + test/e2e/upgrade/upgrade.go | 16 +++++++++------- test/e2e/util/kibishii/kibishii_utils.go | 15 +++++++++++++-- 12 files changed, 56 insertions(+), 28 deletions(-) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 79af0d5712..152f6674a8 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -84,6 +84,7 @@ ADDITIONAL_BSL_PREFIX ?= ADDITIONAL_BSL_CONFIG ?= FEATURES ?= +DEBUG_E2E_TEST ?= false .PHONY:ginkgo ginkgo: # Make sure ginkgo is in $GOPATH/bin @@ -121,7 +122,8 @@ run: ginkgo -features=$(FEATURES) \ -install-velero=$(INSTALL_VELERO) \ -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ - -kibishii-directory=$(KIBISHII_DIRECTORY) + -kibishii-directory=$(KIBISHII_DIRECTORY) \ + -debug-e2e-test=$(DEBUG_E2E_TEST) build: ginkgo mkdir -p $(OUTPUT_DIR) diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 730f6abf93..757c2e589b 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -60,12 +60,11 @@ func BackupRestoreTest(useVolumeSnapshots bool) { }) AfterEach(func() { - DeleteNamespace(context.Background(), client, kibishiiNamespace, true) - Expect(err).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", kibishiiNamespace)) - if VeleroCfg.InstallVelero { - err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) - Expect(err).To(Succeed()) + if !VeleroCfg.Debug { + err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) + Expect(err).To(Succeed()) + } } }) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index a1d7a0cc39..69c8bd3558 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -67,8 +67,10 @@ func backup_deletion_test(useVolumeSnapshots bool) { AfterEach(func() { if VeleroCfg.InstallVelero { - err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) - Expect(err).To(Succeed()) + if !VeleroCfg.Debug { + err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) + Expect(err).To(Succeed()) + } } }) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index a329b51147..8d1eb3d166 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -67,7 +67,9 @@ func BackupsSyncTest() { AfterEach(func() { if VeleroCfg.InstallVelero { - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + if !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + } } }) diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 46a7aeb268..1eac63b024 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -76,8 +76,10 @@ func APIGropuVersionsTest() { Expect(err).NotTo(HaveOccurred()) if VeleroCfg.InstallVelero { - err = VeleroUninstall(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) - Expect(err).NotTo(HaveOccurred()) + if !VeleroCfg.Debug { + err = VeleroUninstall(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) + Expect(err).NotTo(HaveOccurred()) + } } }) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 5bc6d35959..a034b2da3d 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -70,11 +70,13 @@ func BslDeletionTest(useVolumeSnapshots bool) { AfterEach(func() { if VeleroCfg.InstallVelero { - Expect(DeleteNamespace(context.Background(), client, bslDeletionTestNs, - true)).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", - bslDeletionTestNs)) - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace)).To(Succeed()) + if !VeleroCfg.Debug { + Expect(DeleteNamespace(context.Background(), client, bslDeletionTestNs, + true)).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", + bslDeletionTestNs)) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + } } }) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 9186ca3aa9..a0d4c3d59f 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -64,6 +64,7 @@ func init() { flag.StringVar(&VeleroCfg.AdditionalBSLConfig, "additional-bsl-config", "", "configuration to use for the additional backup storage location. Format is key1=value1,key2=value2") flag.StringVar(&VeleroCfg.AdditionalBSLCredentials, "additional-bsl-credentials-file", "", "file containing credentials for additional backup storage location provider. Required if testing multiple credentials support.") flag.StringVar(&VeleroCfg.Features, "features", "", "Comma-separated list of features to enable for this Velero process.") + flag.BoolVar(&VeleroCfg.Debug, "debug-e2e-test", false, "Switch to control namespace cleaning.") } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index 40be8c1587..90d7cd7f7f 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -51,7 +51,9 @@ func SSRTest() { AfterEach(func() { if VeleroCfg.InstallVelero { - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + if !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + } } }) diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index de27e2bb6b..995b4fda70 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -84,7 +84,9 @@ func TestFunc(test VeleroBackupRestoreTest) func() { }) AfterEach(func() { if VeleroCfg.InstallVelero { - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) + if !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) + } } }) It(test.GetTestMsg().Text, func() { @@ -115,7 +117,7 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { AfterEach(func() { if VeleroCfg.InstallVelero { - if countIt == len(tests) { + if countIt == len(tests) && !VeleroCfg.Debug { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) } } diff --git a/test/e2e/types.go b/test/e2e/types.go index 3e4cd71688..5a0a8ae9bb 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -50,6 +50,7 @@ type VerleroConfig struct { InstallVelero bool KibishiiDirectory string Features string + Debug bool } type SnapshotCheckPoint struct { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 5bbb7c21f1..0d98908d55 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -94,13 +94,15 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade }) AfterEach(func() { if VeleroCfg.InstallVelero { - By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { - DeleteNamespace(context.Background(), client, upgradeNamespace, true) - }) - By("Uninstall Velero", func() { - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace)).To(Succeed()) - }) + if !VeleroCfg.Debug { + By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { + DeleteNamespace(context.Background(), client, upgradeNamespace, true) + }) + By("Uninstall Velero", func() { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + }) + } } }) When("kibishii is the sample workload", func() { diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 7ad137cbc1..d99b1b939a 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -46,11 +46,22 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re registryCredentialFile := VeleroCfg.RegistryCredentialFile veleroFeatures := VeleroCfg.Features kibishiiDirectory := VeleroCfg.KibishiiDirectory - + if _, err := GetNamespace(context.Background(), client, kibishiiNamespace); err == nil { + fmt.Printf("Workload namespace %s exists, delete it first.", kibishiiNamespace) + if err = DeleteNamespace(context.Background(), client, kibishiiNamespace, true); err != nil { + fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", kibishiiNamespace)) + } + } if err := CreateNamespace(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", kibishiiNamespace) } - + defer func() { + if !veleroCfg.Debug { + if err := DeleteNamespace(context.Background(), client, kibishiiNamespace, true); err != nil { + fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", kibishiiNamespace)) + } + } + }() if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots); err != nil { From 016ac129c6605b70c5c323d2bab8f5bc457ac9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 11 Mar 2022 10:16:41 +0800 Subject: [PATCH 091/366] Refactor schedule controller with kubebuilder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor schedule controller with kubebuilder fixes #4671 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4748-ywk253100 | 1 + config/crd/v1/bases/velero.io_schedules.yaml | 24 +- config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 28 ++- hack/update-generated-crd-code.sh | 1 + .../v1/{schedule.go => schedule_types.go} | 11 + pkg/cmd/server/server.go | 21 +- pkg/controller/schedule_controller.go | 228 ++++++------------ pkg/controller/schedule_controller_test.go | 175 +++----------- pkg/util/kube/periodical_enqueue_source.go | 88 +++++++ .../kube/periodical_enqueue_source_test.go | 64 +++++ 11 files changed, 328 insertions(+), 315 deletions(-) create mode 100644 changelogs/unreleased/4748-ywk253100 rename pkg/apis/velero/v1/{schedule.go => schedule_types.go} (78%) create mode 100644 pkg/util/kube/periodical_enqueue_source.go create mode 100644 pkg/util/kube/periodical_enqueue_source_test.go diff --git a/changelogs/unreleased/4748-ywk253100 b/changelogs/unreleased/4748-ywk253100 new file mode 100644 index 0000000000..0b6ea993ff --- /dev/null +++ b/changelogs/unreleased/4748-ywk253100 @@ -0,0 +1 @@ +Refactor schedule controller with kubebuilder \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index afb7b14a07..9fe76d7b56 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -16,7 +16,27 @@ spec: singular: schedule scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - description: Name of the schedule + jsonPath: .metadata.name + name: Name + type: string + - description: Status of the schedule + jsonPath: .status.phase + name: Status + type: string + - description: A Cron expression defining when to run the Backup + jsonPath: .spec.schedule + name: Schedule + type: string + - description: The last time a Backup was run for this schedule + jsonPath: .status.lastBackup + name: LastBackup + type: date + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: description: Schedule is a Velero resource that represents a pre-scheduled @@ -393,6 +413,8 @@ spec: type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index cb45808fcb..a87995de86 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWty\x0f\xceV\r\xa9\xddI*I\xe9\xb6kgSJv=\xae\x913\x97\xa99@DS\xec\x98\x04\x18\xa0)YI忧\x1a \xf4\xa4\x1ev\xd5Lx\xb1\x85G\xe3\xeb\xaf\x1f\xe8&GY\x96\x8dTK\x9f\xd0y\xb2f\x02\xaa%|a4\xf2\xcb\xe7\xcf\u007f\xf69\xd9\xf1\xf2\xc7\xd13\x19=\x81\xbbγm>\xa2\xb7\x9d+\xf0\x1eK2\xc4dͨAVZ\xb1\x9a\x8c\x00\x941\x96\x95\f{\xf9\tPX\xc3\xce\xd65\xbal\x81&\u007f\xee\xe68\xef\xa8\xd6\xe8\x82\xf0t\xf4\xf2\x87\xfcO\xf9\x0f#\x80\xc2a\xd8\xfeD\rzVM;\x01\xd3\xd5\xf5\b\xc0\xa8\x06'\xd0Z\xbd\xb4uנC\xcf֡ϗX\xa3\xb39ّo\xb1\x90S\x17\xcev\xed\x04\xb6\x13qs\x8f(j\xf3h\xf5\xa7 \xe7c\x94\x13\xa6j\xf2\xfc\xf7\xc1\xe9_\xc9sX\xd2֝S\xf5\x00\x8e0\xeb\xc9,\xbaZ\xb9\xe3\xf9\x11\x80/l\x8b\x13x\x10(\xad*P\x8f\x00z\x02\x02\xb4\xacWq\xf9c\x94UTب\x88\x19\xc0\xb6h~z\x9c~\xfa\xfdlo\x18\xa0u\xb6EǔԋώYwF\x014\xfa\xc2Qˁ\xf4[\x11\x18W\x81\x16{\xa2\a\xae0\x81B\xddc\x00[\x02W\xe4\xc1a\xebУ\x89\x16\xde\x13\f\xb2H\x19\xb0\xf3\u007fb\xc19\xccЉ\x18\xf0\x95\xedj-n\xb0D\xc7ర\vC\xff\xde\xc8\xf6\xc06\x1cZ+ƞ\xe3\xedC\x86\xd1\x19U\xc3R\xd5\x1d\xbe\x03e44j\r\x0e\xe5\x14\xe8̎\xbc\xb0\xc4\xe7\xf0\x9bu\bdJ;\x81\x8a\xb9\xf5\x93\xf1xA\x9cܹ\xb0M\xd3\x19\xe2\xf58x&\xcd;\xb6Ώ5.\xb1\x1e{Zd\xca\x15\x151\x16\xdc9\x1c\xab\x96\xb2\x00\xdd\x04\x97\xce\x1b\xfd\x9d\xeb\x03\xc0\xdf\xeea\xe5\xb5\xd8ֳ#\xb3ؙ\b\xcev\xc6\x02\xe2m@\x1eT\xbf5j\xb1%Z\x86\x84\x9d\x8f\u007f\x99=A::\x18\xe3\x90\xfd\xc0\xfbv\xa3ߚ@\b#S\xa2\x8bF,\x9dm\x82L4\xba\xb5d8\xfc(jBsH\xbf\xef\xe6\r\xb1\xd8\xfd_\x1dz\x16[\xe5p\x17b\x1c\xe6\b]\xab\x15\xa3\xceaj\xe0N5X\xdf)\x8f_\xdd\x00´τ\xd8\xebL\xb0\x9b\x9e\x0e\x17G\xd6v&R\n9a\xafô0k\xb1\x10\xf3\t\x83\xb2\x95J*Bl@i\x1d\xa8\xa3\xf5\xf9\x9e\xe8\xe1Еg\xae\x8a箝\xb1uj\x81\xbf\xda(\xf3p\xd1\x01\xb6\x9f\x87\xf6$p\x92Yb\x18c/\x1c|\\y$\x14\xa0N\x9bW\x15:\f{$\x8bQ!\xeee=\xb1uk\x11\x1cT\xd2\xf9\x91\x84\x13\x86\b*[}A\x8dG\xdb\a\x84\xc3\x12\x1d\x1aq\xf7\x98!Z\x1b\xf2\b+2),b\x8a\x05\xb6\x03Z\xcc#\xeaa\x88\xa7\xa9\x873\xd9s\x10\xf0O\x8fӔ1\x13\xc3=t>>\xf7\x02=\U00094135~T\\]q\xf6\xed\xb4\x8c\x87\x85\xdc\xc1\x16\x14\xb4\x84\x05\xee%c \xe3\x19\x95\x06[\x0eJ\x94[\x1b$\xc0\x1c\xf6;\xde\xc5Lѧ\xa4m\n\x17\xeaAI\x8e\"\r\u007f\x9b}x\x18\xffu\x88\xf9\x8d\x16\xa0\x8a\x02\xbd\bR\x8c\r\x1a~\a\xbe+*P^\xd4 \x87z&3y\xa3\f\x95\xe89\xef\xcf@\xe7?\xbf\xff2\xcc\x1e\xc0/\xd6\x01\xbe\xa8\xa6\xad\xf1\x1dPd|\x93\xfe\x92ϐ\x8ftl$\u008a\xb8\xa2\xc3KkÀxW\xaf\xf6*\xa8\xcb\xea\x19\xc1\xf6\xeav\b5=\xe3\x04n$\xcaw`\xfeG\x02\xeb\xbf7'\xa4\xfe.\x06Ѝ,\xba\x89\xe06\xf7\xddnDnAr\xa5\x18\xd8\xd1b\x81.\x14\bCOHޒ\x12\xbf\a\xeb\x84\x01cwD\x04\xc1b\xbd\x98\x8fP\x1f\x81\xfe\xfc\xfe\xcbI\xc4\xfb|\x01\x19\x8d/\xf0\x1e\xc8DnZ\xab\xbf\xcf\xe1)x\xc7ڰz\x91\x93\x8a\xcaz<Ŭ5\xf5Zt\xae\xd4\x12\xc1\xdb\x06a\x85u\x9d\xc5zC\xc3J\xad\x85\x85d8\xf17\x05\xadr|\xd6[S\x95\xf1\xf4\xe1\xfe\xc3$\"\x13\x87Z\x84|'\xb7SIR5H\xb9\x10\xef\xbc\xe0\x8dG\x97fz|\x17݇-\x14\x952\v\x8c\xfa\"\x94\x9d\xdcB\xf9\xed[\xe2\xf8\xf8\xeaO\xcf@\tp\x988\xfeo\x97\xe8\x95ʅJ\xf5\n\xe5\x1ev\xbc\xfc\xacr\xd2\x188\x83\x8cA?m\v/\xaa\x15ز\x1f\xdb%\xba%\xe1j\xbc\xb2\xee\x99\xcc\"\x13\xd7̢\x0f\xf8q(\xed\xc7߅?o\xd6%\x14\xe4\xd7*\x14\x16\u007f\v\xad\xe4\x1c?~\x93R\xa9V\xbc\xfe\x1e\xbb\x9d\xf5\x05\xcc\xe1^\t\x8bUEE\x95\x9a\x80>Ǟ\b&\x92\x8aS\xc7Ԭ\xcc\xfa\xab\xbb\xb2\x10\xda9A\xb4\xce\xfan3SF\xcb\xff\x9e<\xcb\xf8\x9b\x18\xec\xe8\xaa\xf0\xfd\xc7\xf4\xfe\xdb8xGo\x8a\xd5\x13\x85n\xf4\x91\xd6N\xb5PY\x12\xba\vu\xd9ǽũ\xae\x1c\xa8\v7k^U\x18z\xa3Z_Y\x9e\xde_\xc01\xdb,L\x18\xb6\x06\xe8\xcb\xc1$K\x1c\xf7l\x15x\x06O\x14u\x01K\xac\xed\x87j\xec\x1eI\xac9\u0088Ե\x01\xcfp\xb0\xbe\x16\xa1\xb4dR@\xed#̆;\x87\x835\xad\xd5\a#\xfb\x9ep0\xb95\xcd\xc1DT\U000aad8a\x15w\xfe5\x8dUؐ\x98\x8d\xf1ͽ\x98Pܾ\xb9\xb5*\xac\x14\x8e\xfb\xaf\x98\xce[\xf9\xeexGx\x8f\xe1tD\xc7\xd4`\xe8W\x02\x0eX)\x9f\x0e\x19\xb2(\xecȋ[CN\x15q\xa8CY'Ug\xa9\xa8F\r\x9b\x97\\\xf0$\x1dfh\xe8o\x87\xaa\x98$\xa8\xf3\xa8C\xef9\x00\xfax_i]\xa3x\x02\xd2\xc6g\"\xe2h\x85\xe9\xeaZ\xcdk\x9c\x00\xbb\xeex\xfaL\x005\xe8\xbdZ\\\x8a\xa0\xdf\xe2\xaa\xd8\xf1\xf5[@\xcdmǛ\x96\xaf\x0f\xa5\x9e\x8a[\xdf{\xc1\xeb\xda\xceJ\xf9KP\x1ee͐\xc7m\x82\xfa\xbc\xcbɃ\xa6k\x8e\x8f\xc9\xe0\x01W\x03\xa3S\xf3\xe8\xec¡?\xb6L\x96\f8\xd0\x04d\xf0K\xf0\x8eW\x11\xd0\x1ft\x89\x83~\x19T\xb6N\xdemY\xd5`\xbaf\x8eN\x88\x98\xaf\x19}b$\xa5\x86\xa1\x1e:\xd4\xde[&\xb7\x12R\xb6\x8b\xa2\xfan\xa2P&\xbcR\x12\xffe\v\x9a|[\xab\xf5\x80ܤI\xb8^\xc5}%\x8e\xb6\x1e\x93\xa2P\xc2?̽\xb6\xf7\x0f\xa0\xee\xad9Q\r\xa6\x90!\xc3\u007f\xfcÙۘ\f\xe3\xe2 \x95\xf6\xf3B\xe8\xcfr\xca\xd79\xe1̅\xefY9\xbe6\xed\xcd\xf6\x16_\xcaxA\xf4p\xbe\xdbM]ljj\xff\x98o\x99\xa3\x06\x89:\x1a\f\xc8\xf5\x8e\xec\xfe\xbdY?\xb2\xbd\xd9T!\xc5\x1c\xea\x87\xc3O\r77{_\x0e\xc2\xcf\xc2\x1aM\xf13\t|\xfe2\x82\xfe]ڧ\xf49@\x06\xff\x17\x00\x00\xff\xffñ\x1b\xae\xa0\x19\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xc1n\xe36\x10\xbd\xfb+\x06\xdb\xc3^*y\x17=\xb4ЭM[ h\x12,\x9cE.E\x0f\x145\xb2\xa7\xa1H\x96\x1c:u\xbf\xbe\x18J\x8aeY\x897\v\xacn&g\x1e\xdf̛\x19ҫ\xa2(V\xca\xd3\x03\x86H\xceV\xa0<ῌV~\xc5\xf2\xf1\xa7X\x92[\xef?\xae\x1e\xc96\x15\\\xa5Ȯ\xdb`t)h\xfc\x15[\xb2\xc4\xe4\xec\xaaCV\x8dbU\xad\x00\x94\xb5\x8e\x95,G\xf9\t\xa0\x9d\xe5\xe0\x8c\xc1Plі\x8f\xa9\xc6:\x91i0d\xf0\xf1\xe8\xfd\x87\xf2\xc7\xf2\xc3\n@\a\xcc\ue7e9\xc3Ȫ\xf3\x15\xd8d\xcc\n\xc0\xaa\x0e+\b\x18\x99t@\xef\"\xb1\v\x84\xb1ܣ\xc1\xe0Jr\xab\xe8Q˱\xdb\xe0\x92\xaf\xe0\xb8\xd1{\x0f\x94\xfap6\x19h3\x02\x1d\xf2\x96\xa1\xc8\u007f,n\xdfP\xe4l\xe2M\n\xca,\x11\xc9ۑ\xec6\x19\x15\xce\f䀨\x9d\xc7\n\ue10bW\x1a\x9b\x15\xc0\x90\x82̭\x18\x82\xdc\u007f\xec\xb1\xf4\x0e;Փ\x06p\x1e\xedϟ\xae\x1f~\xb8?Y\x06\xf0\xc1y\fLc|\xfd7\x11v\xb2\n\xd0`ԁ<紿\x17\xc0\xde\n\x1aQ\x14#\xf0\x0eGR\xd8\f\x1c\xc0\xb5\xc0;\x8a\x10\xd0\a\x8ch{\x8dO\x80A\x8c\x94\x05W\xff\x8d\x9aK\xb8\xc7 0\x10w.\x99F\na\x8f\x81!\xa0v[K\xff=cG`\x97\x0f5\x8aqH\xf2\xf1#\xcb\x18\xac2\xb0W&\xe1\xf7\xa0l\x03\x9d:@@9\x05\x92\x9d\xe0e\x93X\u00ad\v\bd[W\xc1\x8e\xd9\xc7j\xbd\xde\x12\x8f\x05\xad]\xd7%K|X\xe7ڤ:\xb1\vq\xdd\xe0\x1e\xcd:ҶPA\xef\x88Qs\n\xb8V\x9e\x8aL\xdd\xe6\xa2.\xbb\xe6\xbb0\xb4@|\u007f\u0095\x0f\xa2m\xe4@v;\xd9\xc8\xd5\xf6\x8a\x02Rn@\x11\xd4\xe0\xdaGqL\xb4,Iv6\xbf\xdd\u007f\x86\xf1\xe8,\xc6<\xfb9\xefG\xc7x\x94@\x12F\xb6\xc5Ћ\xd8\x06\xd7eL\xb4\x8dwd9\xffІ\xd0\xce\xd3\x1fS\xdd\x11\x8b\xee\xff$\x8c,Z\x95p\x95\xbb\x1cj\x84\xe4\x1b\xc5ؔpm\xe1Juh\xaeT\xc4o.\x80d:\x16\x92\xd8/\x93`:\xa0\xe6\xc6}\xd6&\x1b\xe3\fyA\xaf\xf9\\\xb8\xf7\xa8E>ɠ\xb8RK:\xf7\x06\xb4.\x80:\xb3/O\xa0\x97[W\xbeZ\xe9\xc7\xe4\xef\xd9\x05\xb5\xc5\x1b\xd7c\u038df\xdc~Y\xf2\x19\xc9\xc9d\xe9\xdb\x18\x97\rϰ\x01x\xa7xҿ\xac\xc8>\x8f\x81\xc5x^\x11!\v\xa1\xa4\x9d\xad\xb2\x1a\u007f\xcf\x15e\xf5\xe1BL\xb7\v.\x12\xd2\xce=\x81k\x19\xed\x14t\xe0\xba\x10I\x8d\x10\x92}\x13\xd9~~_7Rx-a\xb8@t33\x1f\xf3\xde&c\x06\xacB\xbb\xce+\xa6\xda\xe0\xf2\x91\xf2I\xd9P\x8fr\xe8{\xff\xeb\xf3\xbdw&u\xf8|\xdd\\\x88\xe0\xe1\xd4zZ8\xfd\xc2@EB\x81pzq\x9e~C\xadD\xf0\xae\x19H\f\x05\x1d%\xbe7\xc4 \x92S\xc0\xd9\x04-\x96\xdbcf\xb3Tm3\x93\xb9Ƴ\xedY\xfe\xbeh|\xb0\xe2\x14\xdf2@\xb2Øl\x9dB@\xcb\x03L\xbeQ\xbfz\x84\x18\x15y\xd2>\xf2\xa2\xbaP\x017\xe7\x1e#1\x01\x03\x96\x85i\xbf=\xa9\xf9-\x94E[\xea\xb4օNq\x05ra\x14\x02tf!\xef-\xacnP5\xe7}\\\xc0\x9d\xe3\xe5\xad\x17#\\슳\xc5(\xef\x92f\xa2s\xec\x1byX9\xf6\x90\xd2\x1a=cs7\u007f\xbd\xbf{w\xf2\x18\xcf?\xb5\xb3\r\xf5\u007f=\xe0ϿV=*6\x0f\xe3\x03[\x16\xff\x0f\x00\x00\xff\xff\x83\xf9\xd9\xe0\xf4\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85\xb3%&4ۣ\xd1Uy\x05\xed\v?\xa8\xc6\xc4SqW\x8f\xe7G\xb9\xb4\xee\xe7\xde\xe3O\xd2:~U\xe6\x95\x11yg>~j\xa5z\xacra\xda\xe7g\x006\xd1%^\xc1g\x9a\xaa\x14\t\xa6g\x005a<\xf5\xaaF}\xf7\xde\xc3H2,\x84\xc7\t@\x97\xa8>|\xb9}\xf8\xfd]\xef1@\x8a61\xb2t̞\x80\x1eH\v\x02\x1e\x98@0\xb5(\xc0e\u0081\xc1ҠE\xe5\xa8Gip\x150L\x1b\x90\x00\xda@\x89F\xeaT&\xf0\xa3H\x9e\xaa\xd2\x0f\xb6\x99\xae\xf2\x146\b\xa6R\xebf@it\x89\xc6\xc9\xc0B\xdf:*\xd3y:\xc0\xf8\x1d\x11\xe5{AJ\xba\x82\x16\\\x86\x811\x98\xd6|\x00\xbd\x05\x97I\xdb\xe2\xcf\xe2\xef\x01\x06\xea$\x14\xe8\xcd\xdf1qk\xb8CC`\x02։V;4āD?*\xf9\xcf\x06\xb6\x05\xa7y\xd2\\8\xac\xe5\xda6\xa9\x1c\x1a%r؉\xbc\xc2\v\x10*\x85B\xec\xc1 \xcd\x02\x95\xea\xc0\xe3.v\r\u007f\xd6\x06A\xaa\xad\xbe\x82̹\xd2^]^>J\x17\x96J\xa2\x8b\xa2R\xd2\xed/Y\xeb\xe5\xa6r\xda\xd8\xcb\x14w\x98_Z\xf9\xb8\x12&ɤ\xc3\xc4U\x06/E)W\x8c\xba\xe2\xe5\xb2.\xd2\xff\v\x12\xb5\xefz\xb8\xba=\xe9\x97uF\xaa\xc7\xce\vV\xe8#\x12 \xcd\xf6\n\xe3\x87z*ZF\xd3#\xe2\xceכ\xbb\xfb\xae2I;\xe4>\xf3\xbd\xa3a\xad\b\x88aRm\xd1x!n\x8d.\x18&\xaa\xb4\xd4R9\xfe\x91\xe4\x12Ր\xfd\xb6\xda\x14ґ\xdc\xffQ\xa1u$\xab5\\\xb3\xfd =\xac\xcaT8L\xd7p\xab\xe0Z\x14\x98_\v\x8b\xdf]\x00\xc4i\xbb\"\xc6Ɖ\xa0k\xfa\x86\x9d=\xd7:/\x82\x99\x9a\x90WX\xe3w%&\xbd%C\xe3\xe4V&\xbc0`\xabMk\x02lך\x856\xbej\xa16=\xd4}\xf8\xfc\xa8\xb5\xb96Z\x01\xbe\x90\xec\xdb\xd5L\xba\U000dc862\x15f*Ex\x1e\xc0\x84\xdaĬ\x0f\xdeLp\x93_aQ\xd2r\x9dA\xf1\xbe\xeeF(\x12\x8f\xd2\xc6\x1dy\x83\x82\x8dyӵU\x83\x03\xa3\xc2\xd3eH\xfc\xdaɴ\xb6\x1a\a\xdc<\xceQ\x8f\xd8VT\xb9{\xd0yU\xa0\xbd\xd7_\xd1:\x99\x8c\xf5\x1c\x10\xf1qt`\x907Z\xe2\xb0\xcb\xd0\xd0\xe2\xe4\x17l\xefF\xe1\x02\xaf\x19\x8b)\x9b<\xf1\x84 `\xe39@\xb63ϡ\xd4)\xec\xfcL\xb0\xd9\a\xa4\x0fe\xd3\xcag\xa3u\x8eb\x8ck\xf8\x92\xe4U\x8ai\xe3\xf2F\xf92\xa0\xf6\xe6`\x10\a\aB*\xd22rń\xaaj\xdeN\xd0\xc9\xfeJ\x18\x042\x14Ry\x98 Y\x05k\x92lj\x92\x0e\x8b\t<\x8fj\xa4o\x14\x84\x88M\x8eW\xe0L5\xa6\xeb\x01\x860F\xec\x8f\xf0,\x04PKX\u058c\xa9\xcdy.\x13$f5F\x9b\xb9Ƭ\x99\xa0\xef\u007f\x90a\x99\xd6O1L\xfa\x13\xf5k\x9d\x13$\x1c\xa7\xc2\x063\xb1\x93\xda\xd8a\x84\x83/\x98T\x0e\xa7֑p\x90\xca\xed\x16\r\xc1*3a\xd1\x06\x93r\x8cY\xc7M\x045s\\\xf0\at\xb5B'\xe117\xa6HaS<\t\x15\x18q\xb2\xd8U\tR\xa5r'\xd3J\xe4 \x95uB%\x9e>\xd1\xe07N\x1f\xcc)\xc4\x01\xfe\xde\x00\a*HJ=Ϧ\x15R8Zh3\xae\x1c\xa1\x1d\x82\x99f\xc3F\x90\x05\xd4S\xee\xa8m\x86\"\xfa\x1a\x95\x94]jkw.ZI\xf9\xa00\x17\x1b\xcc\xc1b\x8e\x89\xd3f\x9a=1J\xe0[\xac\xfd\x9c\xe0\xec\x88%m}\x06)\xea\xac\x11m\x9b\xd3\xf0\x9c\xc9$\xf3\xf1\x1bi\x19ÂT\xa3e\x8b!\xca2\xdf\x1f#\x1ab4\xa3\x9el\xceh\xb4-\xc2|\f\xe1N\x19\x92\xb6E\xda\xe0\xb6\xcdX\xe3>\xd7\x1b\xb5ycz\x0fM\xf5M\xca~{0\xfc\xf5\x95\x9d\xd8-i\u007fw\xbb\x05,J\xb7\xbf\x00\xe9\xc2\xd3\x18\xa8\x14`\xb5x\xfc\xc6\x04w\xdaj\xb9\x1d\x8e~\xf5\xd5\xf2*Rk\xd0\xf8\x8d\b\x8d\x9d\xd5]\xed\xab\x16\t\xecSw\xe4\x05\xc8m#\xb0\xf4\x02\xb62wȱ\xd4\x1c\xa2\x9d@gVr\xafɠX\xdfK\xad\x10.\xc9n\x9a-mĈ\x01\xaf\x86\x00|\\\x1e\xf60,\x83\b\x90\xd0\x04\x15\x9c\x05\x91\x06\v\x9f]\xb9\xe7\xf5\xd1>\xe1\b\xf0\xc3珘α\f\xe25\xf5\x80\xa8\x0f\x83H\xa7\x8b\x02\x13\x18\x05\xb2C\x14\x87i\xcd\x1e\xcf\xe7\xd0.@\xc0\x13\xee}d5\xba\xb9\x1ck$Zр4\xc8\t=6#O\xb8gPu\x86.\n\xde\x12U\xf1\xed\t\xf7\xb1]\aL%\xfc\xea\x1c\x85\xe7.=`*b\x96R\xdb\x1a\xa6\xd6k\a\x9c\x8e#\x16\x96\x19\xa5\xd0\x02\xc7O$\xbb\x11X/-\xfd\x84\xfbw\u058b\x8fVM&\xcb\x05\x1c \x83\r\x16y\x85\x85|\xec\x83\xc8e\xdaL\xc6\xebd\x01\xc4[u\x01\x9f\xb5\xa3?7/\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xf8\xc9we\xb1'\xe2D\x06\xfb\xc1\xbc,\x95w\vėE\xf3\xb78\xb0\v%\x15m\xc4&-\xdc*ڟy\xfe,\x11S\x86\x019\x8fVQY\xce\xe8*\xadV\xec\xa6\xc3l\v\x80v\xf1\xaaE\xa5MOR\x17\v!\x8e\xa2X\xa3wO\xdeʿ9ȅ\x1fk\x06\xcb\\$\x98BZq\xbe\x9d\x13\xef\xc2\xe1\xa3L\xa0@\xf3\x88P\x92߈W\xaa\x05\x96ܷ\x13\xb40>\xb4\b\xadv\vi\x1cb+Z\xf5\x91=\x83\x98\xa3\xbaOdُw\x8f\xa3\x92\xdd;\xc7CQ\xdc\x17iʩg\x91\u007fY\xe8Y\x16\xca\xeb0\x06\xf1H\xfa\xf0\xa3\x10\x9c\xec\xfd\x17\xb9WV\xef\u007f\xc7yC!\x8d]\xc3\a>\xdc̱;>d\t;SE\x81$L\xa4\x05ғ\x9d\xc8)| \xe3\xad\x00s\x1fL\xe8\xedA\x04\x15gb\x9e3m\xbd\xcf\xdfJ\xcc\xf9t\xeb\xfc\t\xf7\xe7\x17\a\xd6\xeb\xfcV\x9d\xc7\xc1$\x9b\u007f`\xb4\x9a\xa8E\xab|\x0f\xe7\xfc\xee\x9c\x03\xb3%K\xe4\x84\xe0m\x81VGw\xe5\xe3\xe7%[\x01\xdak\x87\xa8\x85\x067\x87\xb4\x14\xc2\xcfQ\x11\xadӥ\xb6n\x11Z_\xb4u>\x01\xd8\v\xb7G2\x841\xbb\xbf:k\bb\xebЀuڄ\x03Q2\xbb\x83\x049I\xde\xce˞D\xddd#=`\xdad\x9e\xb7\x16\xc2\xdb\xf4s\u007fRJ\xff\xcf\xc3L8Xbإ\xd1\tZ;\xafJ\x91\x9ec&a\xdb$k\x85\u07fcm\xa3LsL*9\xb4e\xa18\xb1\xf6\x84\x8d\xcd\xcdK'\xefLf\x88~Ǩ\xf2)8\x02\x17\xbd\x14\x85\x18\x1e\xceG\xa3{\xedG\x87\x05X\x03\xf3\x1b&\xf3X\xb1QY\x167\xd7*\xf9k\v<\n\xa9ny\"x\xff݂\x15\b\xa6\x1cO\xdd\xca\\\x87\xf1\xad@\x9a\a\xb1\xfbW\bG͚\xcfj\f\xf6${x\x92\x11/)\xa0`Zi\xd7M\xd6\xd43\xbd\xb3\xb0\x95ƺ\x16\xe1\x05P\xa5\xe5\xe3\xe4\xef\xbb\xc7T7Ɯ\xbc\xc5\xfcŏ\xee\xa4\x153\xfd\\\x17F,\xd9X\a\xe6gb\x87 \xb7 \x1d\xa0Jt\xa58\xe1E悦Y\x00\xd1\v\xd1;\x93H\x9f\xd9\x19\xac\xaa\"\x9e!+\xd6N\xa9f\xb3c\xdd!?\t\x19\x97\x9d\x82\xd3\xc4\xead\x81\xba\x9au\xedm\xebWv\xf8ѽ\n\x98B\xbcȢ*@\x14$\x96%\xfb\xc6-cӔ\xcbxY?\v\xe9\xf8Џ`\x93x\x96Y\xd3D\x17e\x8e\x0ea\x83[m\xd8\x1eX\x99b\x13>\xd4\xf2\x1f\xad7\x99j\x02\xb6B\xe6\x95Y`\xa3\x17Kf龭6O\xaf\xbf\x19\x8bGd\xc5̌L\xba/\b\x9a\xe7\xfdGi\x96\x85\xcc_\f\xbe~hZ\x1aIZ\xaa\xe7\xa2\xd3Y\x98\x1c\xbd\xf6\xa3\xd3Zy\x85\xdaO\x85\xa7\xb3P\x19\x93\xb7\xf0\xb4io\xe1\xe9[x\xfa\x16\x9e\x0e\xda[x\xfa\x16\x9e\xbe\x85\xa7\xe3\xed-<\xed\xb4\xb7\xf04\xda\u007f\xc4`\xe8\xbf::\xd2!\n\xab\xc8\x12\x8c9\xb4g\xe6\xaa+\x8d\xae\xf3\xca:4K*\xa4o\xc7G\x8e\xd4\xd0'\xbeˊ\xbf֚Қ\xb6t\xa5uzM\xc94-ɰ\x98\xfc\xb7\x18\x11Qxt\x19\xf4t\xb5}l\x01\xdd\\\xd9\\\xbfv\xbc)W\xf3\xffM0\xc4\xe90}-=\xff\x8dO\xb7\xe6\xaa_\xfb\xc6\xfb\x80\x80\U0006fcae<\xb2\xacm\xa6\x98\xedx!\xfe\x94\x87\x0f\xbc\x1c\x1c.\xf4\x99iz\x85߿j^FT\x9bMטէ\x96\xe8\xc4\xee\xfd\xba\xff\xc6\xe9\xba\xe2l\x82\xb2g\xe92\xff\xfd\x11m]\xd5c\xb7\xac=\xe8i\xfd\xedߐ\xc7\x13\x10\xb5\x01%s/\x80\x00\xa1\xc7~\xf8\xa5\xf4[\xe4\x93\xd7\xf9\xfcF-\xbe.mi5ZS?4\xefU\xbe\xa1\x06m\xd9\a\x02\xb3\xf5f1HCL\x95\xd9x\xfd\xd8\f\xd4%\xb5e\xb1{\xf0\x88:\xb2\xf8\xea\xb18\xf6\x00\u007f\xb1\x1b[3\x16\x1d\xb5\xc5և}\x9f\xaa\xb0\xc8Z\xb0N\x85\xd7,\xc8\x13+\xc0\xa2\x19\x16W\xed\x15]\xe3թܚ\xe7֑ʮ\xf1z\xadY\x90c\xf5\\1UZQ\xb8F\xd7f5\x15W\xf3\x99\xc4o\xaa\xc8z\xfd\xda\xef\u05cc\xf3\x8f\xd7WEUUE\xed\x05\xe6q\x8e\xaa\x9bZZ-\x15\xc5ե\x95QM\xd5ӑ\x89\xa3\xea\xa1\x0ek\x9d\x8e\x912[\x055]\xe1t\f\xecX\xedSD]\xd3\x11\x90݊\xa7\xc5a\xc0\xac6\xcdt\x18\xff\xaa>\xb4y_\x9b\xff74\xf0[\x89\xd6&E3\xbb+Y\x82\xfa,\xda\xfd|\xe5`\xfe\xc1\xe7:\xe1cr\xea\xd5\xdd\xf1LEQ\xba\xf9|$\x81\x9f\xa5J\xbd&\xd1b\xe9\xc44|C\x05\x17\x985a\xd6t\xc5m\x1b\xd1\x0ev[\x16KAF=\x85\xcd\xdeg\x85\xec\x1anD\x925\x1d' \xf2̙\xb0\xb4\xb3/\x84\x83\xf3f\x1b{\x19Fғ\xf35\xc0O\xba\xc9 t>\x0f\x9d\x80keQ\xe6{\xa8,\xc2y\x1fз\xed\xc2&u\xc7*Q\xdaL\x87\x9b\x04\"6bw\xfd\x11#\xf9\x92p\x8f@\x92\xeb*mf8\"n\xa1\xf6\xf0\xe5\x81#9\xfez:i\xbf2\xaf#\xb5\xb0\xaf\x1a|\x84>\x01r\xea\xf2\x88E,\x9bΪX\xa7\x8dx\xc4O\xda߫\x11ó\xfe\x88\xde\xd5*\xb5]\r9ֺhrR\x93=mC\x80\xed\xd1K\xbd\xda\xda$\x14a;ergֹsy\x04q\xf7\xf7\x9f\xb2\xb3Y\xee\b\xf1XNLo'a\tku\"\xd9\x16q\x8a\x82OX\xbe\xdf\x1d\v\xc7\"\xb2#&\xa3\xb2\xf8˳B\xf35,T{\xab\xbc\xa4fn]\xf9\xcb\xc1\xc0 \xe01\xf3A\xf6o\xd0}\xcc署A\xd6\xdf\xfd\xe5\xcd83.\xdcCsȺ\x99\xf5?\xbd\xf6\xc7#\xea\xd5\xf8\xd5/\xab\xe66\x9a\xb3\b\xceZ'\\ec.\xf8᎐\x88\xd2U\xa6v\xafIe\xf8\xc2\t\x02\x82\xfe>\x86Ӯ\xf8ɅuQ\xb2\xfc\xd4tl3\f\xd6\xf9\xf3\xa8`\xa0\xe0YX\xbe;ǟF\x8d\xba\xe6沠\xc9\xdbs\xbcw\xbd\x82T8\\\x11\xfc\xd3\xc49\xba\x0e\xf8\x82\x8e\x19J\xbfP\x9f\xe6t\xbcf4\x0f\f\x17{\xdcM\xa1>~ܹ\x82\xcf\xf8<\xf2\xf4F\x11\x11\x87\x9bQ\u007f\xa6\x89)g(Ʈ7;J\xe2\xae\x19\xc5\a\xca#֢o\xe6\x06\xdd\a\x99j\x91\xe7\x1d\x88\xfe\xf0xL\xac\xff/\xb7>m\x94\x10M\xbf;\xe81i\xb8\x8e\x1a\xad)\x835\xba\xa4\x0e\x1eZ4;L;JR\xfb\xf0\xfaI\xbb\x00E\x92`\xe9\xeaÏ\xee\xed\u007f\xe7\xe7\xfc#\\\xee\xc7?\x13\xad|Lm\xaf\xe0\xaf\u007f;\x83\xda\xd7>\x84\x1b\xfc\xe8\xe1\u007f\x02\x00\x00\xff\xff_q\xf0n+Q\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\\_oܸ\x11\u007f\xf7\xa7\x18\xb8\x0fi\x01\xef\xfa\x82>\xb4\xf0[\xce\xf1\xa1ƥ\xb9 v\xfdR\xf4\x81+\xcdZCȅu\xe0d\x81 \xea\x01\xe1YX\xe6a\xab\r\xb8L\xdayL\x88H\x8f[\xcf·a\xb5g(\x15\x0ekv\xc6dy\xa0\xfc=\x9a\xef\x1eq\x9c\x98\xffy\xf7֫h\x92a!\xaeꖺD\xf5\xee\xd3\xedß\xefz\xd50Ѓz\x96 -\bx\xe0\xf5\x03\xa6^\xe9\xe02\xe1\xc0 I\r\x95\xa3\x16\xa5\xc1U@&mH\x02h\x03%\x1a\xa9S\x99\x04D\xb9\xb3\xcdt\x95\xa7\xb0A\x02w\xddt(\x8d.\xd18\x19V\xa8/\x1d\x8bԩ\x1dp\xfc\x86&\xe5[y-BˊS\xaf;Lk\x1c\xbcnK\xdb\xf2\xcf\x00\xf7\b\x035\x12\n\xf4\xe6gL\xdc\x1a\xee\xd0\x10\x99\xc0u\xa2\xd5\x0e\r!\x90\xe8G%\xff\xd3ж\xa4\xb1\x8e\x15\xc9am6\xda\xc2\xeb\\\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xa3@\xa5:\xf4\xb8\x89]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05K\x9c袨\x94t\xfbK6\xaarS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+f]\xb15^\x17\xe9\x1f\x82D\xed\x9b\x1e\xaf\ak\xc5\x17\xb6\x97G$@\x86\xd3+\x8c\xef\xeag\xd1\x02MU\x84\xce盻\xfb\xae2I;D\x9fq\xefhX+\x02\x02L\xaa-֫qkt\xc14Q\xa5\xa5\x96\xca\xf1\x1fI.Q\r\xe1\xb7զ\x90\x8e\xe4\xfe\xef\n\xad#Y\xadᚷ'\xd2ê\xa4Փ\xae\xe1V\xc1\xb5(0\xbf\x16\x16\xbf\xb9\x00\bi\xbb\"`\xe3D\xd0\xddY\x87\x8d=j\x9d\x1f\xc2.8!\xaf\xb0\xc6\xefJLzK\x86\xfaɭLxa\xb0\xe5kL\xc0\xc0\xfa\xf92\xbej\xa16=\xd4|X\u007f\xd4\xda\xc4\xee\t\a4\xa161\xeb\x83_&\xd0䟰(i\xb9ΰx_7#\x16\t\xa3\xb4\xf1v\xc2f\x19̛\xae\xad\x1a\x1c\x18\x15\x1e.C\xc2k'\xd3\xdaj\x1c\xa0y\x1cQ\xcf\xd8VT\xb9{ \x97\x01\xed\xbd\xfe\x8c\xd6\xc9d\xac\xe5`\x12\xefG;\x06y\xa3%\x84]\x86\x86\x16'\xff\xc0\xf6n\x94.\U0001ac58\xb2\xc9\x13O\xb4en<\x02d;\xf3\x1cJ\x9d\xc2Ώ\x04\x9b}`\xfaP6\xad|6Z\xe7(\xc6P\xc3/I^\xa5\x986\x1e\xd5(.\x83\xd9\xde\x1ctb\xdfSHEZF\x9e\x1e\xb1\xaa\x9a_'\xe6\xc9\xfb\x950\bd(\xa4\xf24Az\xb7d3\xa1pT\xa4\xc3b\x82ϣ\x1a\xe9\v\xf9\xb8b\x93\xe3\x158S\x8d\xe9z\xa0!\x8c\x11\xfb#\x98\x05\xff|\tdM\x9fڜ\xe72a\x0f\xb41ڌ\x1aC31\xbf\xffC\xc02\xad\x9fb@\xfa\x1b\xb5k7'H\xf8\x18\x04\x1b\xcc\xc4Njc\x87\x1e\x0e~\xc1\xa4r8\xb5\x8e\x84\x83Tn\xb7h\x88\x16;ԍ\xff}\f\xac\xe3&\x82\x8a9.\xf8\x83y\xb5B'\xe11\x1aSSaSt{^\x80\xdc6\x02K/`+s\x87\xecK\xcd1\xdaqtf%\xf7\x92\x00\xc5\xee\xbdT\n\xe1\x92\xec\xa69\xd2F\xf4\x18`5$\xe0\xfd\xf2p\x86a\x19D\x90\x84Ʃ\xe0(\x884X\xf8\xe8\xca=\xaf\x8f\xb6\x86=\xc0w\x1f\xdfc:\a\x19\xc4k\xea\xc1\xa4\xde\r<\x9d.\v<\xc1(\x92\x9dI\xb1\x9b֜\xf1|\f\xed\x02\x04<\xe1\xde{V\xa3\x87˱B\xa2\x15\rI\x83\x1c\xd0c3\xf2\x84{&UG\xe8\xa2\xe8-Q\x15_\x9ep\x1f\xdbt\x00*\xf1W\xc7(<\xbaT\xc1\xb3\x88YJmi@\xad\xd7\x0e8\x1d7YXf\x94B\t\x88\x9f8\xedF`\xbd\xb0\xf4\x13\xee\xdfX/>Z5\x99,\x17 @\x06\x1b,\xf2\n\v\xf1\xd8\a\x91˴\x19\x8c\xd7\xc9\x02\x8a\xb7\xea\x02>jG\xff\xdc|\x91\x96XT)\xbc\xd7h?j\xc75\xdf\x14b?\x89\x13\x01\xf6\x9dyY*\xbf-\x10.\x8b\xc6oy\xe0-\x94T\xb4\x11\x9b\xb4p\xab\xe8|\xe6\xf1Y\"\xa6\f\x03s\x9e\xad\xa2\xb2\x1c\xd1UZ\xadx\x9b\x0e\xa3- \xda\xe5\xab\x16\x956=I],\xa48\xcab\xcd\xde=\xedV\xfe\x97\x83X\xf8\xb1b\xb0\xccE\x82)\xa4\x15\xc7\xdb9\xf0.\x1c>\xca\x04\n4\x8f\b%\xed\x1b\xf1J\xb5\xc0\x92\xfbr\x82\x16ƻ\x16\xa1\xd4\xdbB\x1a\xc7؊V}d\xcb \xe6\xa8\xe6\x13Q\xf6\xe3\xcd\xe3f\xc9\xdb;\xfbCQ\xe8wo\x8f\x97\xed,\v\xe5u\xe8\x83x&\xbd\xfbQ\b\x0e\xf6\xfeB\xdb+\xab\xf7\xafq\xbb\xa1\x90Ʈ\xe1\x1dߝ\xe7\xd8\xed\x1f\xa2\x84\x9d\xa1\xa2H\x12'\xd2\x02\xe9\xc9N\xe4\xe4>\x90\xf1V\x80\xb9w&\xf4\xf6\xc0\x83\x8a31ϙ\xb6~\xcf\xdfJ\xcc\xf9v\xeb\xfc\t\xf7\xe7\x17\a\xd6\xeb\xfcV\x9d\xc7\xd1$\x9b\u007f`\xb4\x1a\xafE\xab|\x0f\xe7\xfc\xdb9;fK\x96\xc8\t\xce\xdb\x02\xad\x8en\xca\xd7\xcfK\x8e\x02\x9c\xe1 \xdb\xe3usIK.\xfc\xdc,\xa2u\xba\xd4\xd6-b듶\xce\a\x00{\xee\xf6H\x840\xe6\xf4WG\rAl\x1d\x1a\xb0N\x9bp!Jfw\x10 '\xc9\xdbyٓ\xa8\x9bh\xa4'L\x87\xcc\xf3\xd6Bx\x9b~\xeeoJ\xe9\xff\xf34\x13v\x96\x98vit\x82\xd6ΫR\xe4\xce1\x13\xb0m\x82\xb5\xc2\x1f\u07b6Q\xa69&\x94\x1c\xca2W\x9c\xa0=\xe1`s\xf3\xa5\x13w&3D\u007fǨ\xf2)<\x02\xe7T\x15\x85\x18^\xceG\xb3{\xed{\x87\x05X\x13\xf3\a&\xf3X\xb1QY\xe67\xd7*\xf9[s<\n\xa9ny x\xfb͜\x15\b\xa6\x1cO=\xca\\\x87\xfe\xad@\x9a\x8a\xd8\xf3+\x84\xabf\xcdw5\x06{\x92=\xbcɈ\x97\x14\x903\xad\xb4\xeb\x06k\xea\x91\xdeX\xd8Jc]\xcb\xf0\x02\xaa\xd2\xf2u\xf2\xb7=c\xaa\x1bcN>b\xfe\xe4{w\u008a\x99~\xae\x13#\x96\x1c\xac\x03\xf8\x99\xd8!\xc8-H\a\xa8\x12])\x0ex\x91\xb9\xa0a\x16P\xf4B\xf4\x9bI\xe4\x9e\xd9鬪\"\x1e\x90\x15k\xa7T\xb3ѱn\x97\x1f\x84\x8c\x8bN\xc1ibu\xb2@]\xcdn\xedm\xe9gv\xf8\u07bd\f\x98B|\x91EU\x80(H,K\u038d[\x9f?\x18\xd2e\xbc\xac\x9f\x85tu\x06\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8a\x8d\xfbP\xcb\u007f4\xdfd\xaa\b\xd8\n\x99Wf\x81\x8d^,\x99\xa5\xe7\xb6\xda<\xbd\xfca,\x9e\x91\x15\x83\x19\x19t_\xe04\xcf\xef\x1f\xa5Y\xe62\u007f2\xf8\xf2\xaeii$i\xa9\x9e\xf3Ngi\xb2\xf7\xda\xf7Nk\xe5\x15j?\xe5\x9e\xceReN^\xddӦ\xbc\xba\xa7\xaf\xee\xe9\xab{:(\xaf\xee\xe9\xab{\xfaꞎ\x97W\xf7\xb4S^\xdd\xd3\xe8\xfd#\x86\xc3\x15t><;\x99\xab\xc8\x14\x8c9\xb6gƪ3\x8d\xae\xf3\xca:4K2\xa4o\xc7{\x8e\xe4\xd0'\xbeɊ?\x06\x9cҚ6u\xa5\xdd\xf4\x9a\x94iZ\x92a1\xf9o1\"\xbc\xf0\xe84\xe8\xe9l\xfb\xd8\x04\xba\xb9\xb4\xb9~\xeex\x93\xae\xe6\xff7\x01\x88\xd3a\xf8Zz\xfe\x1b\x9fn\xceU?\xf7\x8d\xcf\x01\x81\xe3\xdfd^ydZ\xdbL2\xdb\xf1D\xfc\xa9\x1d>`9\xb8\\\xe8\x83iz\x89߿i,#\xb2ͦs\xcc\xea[Ktb\xf7v\xdd\xff\xc5\xe9:\xe3lbf\xcf\xd2e\xfe\xfb#:\xba\xaa\xc7nZ{\xd0\xd3\xfaۿ!\xc6\x13\x14\xb5\x01%s/\x80@\xa1\a?\xfcT\xfa#\xf2\xc9\xeb|\xfe\xa0\x16\x9f\x97\xb64\x1b\xad\xc9\x1f\x9a\xdfU\xbe\"\am\xd9\a\x02\xb3\xf9f1LCL\x96\xd9x\xfe\xd8\f\xd5%\xb9e\xb1g\xf0\x88<\xb2\xf8\xec\xb18x\x80\xbf؍\xcd\x19\x8b\xf6\xdab\xf3þMVXd.X'\xc3k\x96\xe4\x89\x19`р\xc5e{E\xe7xu2\xb7\xe6\xd1:\x92\xd95\x9e\xaf5Kr,\x9f+&K+\x8a\xd7\xe8ܬ&\xe3j>\x92\xf8U\x19Y/\x9f\xfb\xfd\x92~\xfe\xf1\xfc\xaa\xa8\xac\xaa\xa8\xb3\xc0<\xcfQySK\xb3\xa5\xa2P]\x9a\x19\xd5d=\x1d\x198*\x1f\xea0\xd7\xe9\xd8Tf\xb3\xa0\xa63\x9c\x8e\x91\x1d\xcb}\x8a\xc8k:B\xb2\x9b\xf1\xb4\xd8\r\x98զ\x99\x06\xe3_Շ2\xbf\xd7\xe6\xff\v\r\xfc\xdaIk\x93\xa2\x99=\x95,a}\x96\xed~\xbcr0\xfe\xe0s\x9d\xf019\xb5\xea\x9ex\xa6\xbc(\xdd|>\x92\xc0\x8fR\xa5^\x93h\xb1t|\x1a~\xa1\x82\x13\xcc\x1a7k:\xe3\xb6\xf5h\a\xa7-\x8b\xa5 \xa3\x9e\xc2f\xef\xa3Bv\r7\"ɚ\x86\x13\x14y\xe4LX:\xd9\x17\xc2\xc1ys\x8c\xbd\f=\xa9\xe6|\r\xf0\x83n\"\b\x9d\xcfC'\xe8ZY\x94\xf9\x1e*\x8bp\xde'\xf4u\xa7\xb0IݱJ\x946\xd3\xe1%\x81\x88\x83\xd8]\xbf\xc7H\xbc$\xbc#\x90\xe4\xbaJ\x9b\x11\x8e\x88[\xa8=|z`O\x8e\xbf\x9eNگ\xcckO-\x9c\xab\x06\x1f\xa1O\x90\x9cz\xb6\xef\xa9\x1a 럖\xf3f\x9c\x81\v\xef\xd0\x1cB7\xb3\xfe\xa7\xd7\xfe\xb8G\xbd\x1a\u007f\xfaeռFs\x16\x81\xac\u007fo,\xe6\x81\x1f\xff\xa8[\"JW\x99z{M*\xc3\x0fN\x10\x11\xf4\xef1\x9c\xf6\xc4O\xfb\xdcٌ,\xdb\a\xd0\xda\b\xc3\xecsk#\xf2k\x1e\v\x9a|=\xc7\xef\xae\xfe9\xb4\x15\xd1?M\x9c\xa3\xeb\x80\x1f蘙\xe9'j\xd3\u070e\xd7@s\xc7\xf0\xb0\xc7\xdd\x14\xeb\xe3ם+\xf8\x88\xcf#\xb57\x8a&qx\x18\xf5w\x9a\x98r\x84b\xecy\xb3\xa3S\xdc5\xbd\xf8By\xc4Z\xf4\xcdܠ\xf9 R-\xf2\xbcC\xd1_\x1e\x8f\x89\xf5\x8fr\xeb\xc3F\t\xcd\xe9O\a-&\r\xd7Q\xa35e\xb0F\x97\xd4A\xa5E\xb3ô\xa3$\xf5\x1eޭ\xa96\a\xef\xac\xd4\v\x13~\xf9\xf5\xac]\xa3\"I\xb0t\xf5\xfdH\xf7\xfd\xc9\xf3s\xfe#9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index c261775693..2de1d49868 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -4,8 +4,14 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null - name: manager-role + name: velero-perms rules: +- apiGroups: + - velero.io + resources: + - backups + verbs: + - create - apiGroups: - velero.io resources: @@ -46,6 +52,26 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - schedules + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - schedules/status + verbs: + - get + - patch + - update - apiGroups: - velero.io resources: diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index e3355be487..d1c3eb0d53 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -49,6 +49,7 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ controller-gen \ crd:crdVersions=v1\ paths=./pkg/apis/velero/v1/... \ + rbac:roleName=velero-perms \ paths=./pkg/controller/... \ output:crd:artifacts:config=config/crd/v1/bases diff --git a/pkg/apis/velero/v1/schedule.go b/pkg/apis/velero/v1/schedule_types.go similarity index 78% rename from pkg/apis/velero/v1/schedule.go rename to pkg/apis/velero/v1/schedule_types.go index 1f2f84d45e..52cac8255a 100644 --- a/pkg/apis/velero/v1/schedule.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -77,8 +77,18 @@ type ScheduleStatus struct { ValidationErrors []string `json:"validationErrors,omitempty"` } +// TODO(2.0) After converting all resources to use the runtime-controller client, the genclient and k8s:deepcopy markers will no longer be needed and should be removed. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Name",type="string",JSONPath=".metadata.name",description="Name of the schedule" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="Status of the schedule" +// +kubebuilder:printcolumn:name="Schedule",type="string",JSONPath=".spec.schedule",description="A Cron expression defining when to run the Backup" +// +kubebuilder:printcolumn:name="LastBackup",type="date",JSONPath=".status.lastBackup",description="The last time a Backup was run for this schedule" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // Schedule is a Velero resource that represents a pre-scheduled or // periodic Backup that should be run. @@ -96,6 +106,7 @@ type Schedule struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root // ScheduleList is a list of Schedules. type ScheduleList struct { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 6e53d0dee3..0a911cae92 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -654,22 +654,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } - scheduleControllerRunInfo := func() controllerRunInfo { - scheduleController := controller.NewScheduleController( - s.namespace, - s.veleroClient.VeleroV1(), - s.veleroClient.VeleroV1(), - s.sharedInformerFactory.Velero().V1().Schedules(), - s.logger, - s.metrics, - ) - - return controllerRunInfo{ - controller: scheduleController, - numWorkers: defaultControllerWorkers, - } - } - gcControllerRunInfo := func() controllerRunInfo { gcController := controller.NewGCController( s.logger, @@ -771,7 +755,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string enabledControllers := map[string]func() controllerRunInfo{ controller.BackupSync: backupSyncControllerRunInfo, controller.Backup: backupControllerRunInfo, - controller.Schedule: scheduleControllerRunInfo, controller.GarbageCollection: gcControllerRunInfo, controller.BackupDeletion: deletionControllerRunInfo, controller.Restore: restoreControllerRunInfo, @@ -843,6 +826,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupStorageLocation) } + if err := controller.NewScheduleReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.metrics).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) + } + if _, ok := enabledRuntimeControllers[controller.ServerStatusRequest]; ok { r := controller.ServerStatusRequestReconciler{ Scheme: s.mgr.GetScheme(), diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index e5f48814f3..42f2c6d608 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -18,27 +18,22 @@ package controller import ( "context" - "encoding/json" "fmt" "time" - jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" "github.com/robfig/cron" "github.com/sirupsen/logrus" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/client-go/tools/cache" + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" - api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/metrics" + "github.com/vmware-tanzu/velero/pkg/util/kube" kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -46,160 +41,100 @@ const ( scheduleSyncPeriod = time.Minute ) -type scheduleController struct { - *genericController - - namespace string - schedulesClient velerov1client.SchedulesGetter - backupsClient velerov1client.BackupsGetter - schedulesLister velerov1listers.ScheduleLister - clock clock.Clock - metrics *metrics.ServerMetrics +type scheduleReconciler struct { + client.Client + namespace string + logger logrus.FieldLogger + clock clock.Clock + metrics *metrics.ServerMetrics } -func NewScheduleController( +func NewScheduleReconciler( namespace string, - schedulesClient velerov1client.SchedulesGetter, - backupsClient velerov1client.BackupsGetter, - schedulesInformer velerov1informers.ScheduleInformer, logger logrus.FieldLogger, + client client.Client, metrics *metrics.ServerMetrics, -) *scheduleController { - c := &scheduleController{ - genericController: newGenericController(Schedule, logger), - namespace: namespace, - schedulesClient: schedulesClient, - backupsClient: backupsClient, - schedulesLister: schedulesInformer.Lister(), - clock: clock.RealClock{}, - metrics: metrics, +) *scheduleReconciler { + return &scheduleReconciler{ + Client: client, + namespace: namespace, + logger: logger, + clock: clock.RealClock{}, + metrics: metrics, } +} - c.syncHandler = c.processSchedule - c.resyncFunc = c.enqueueAllEnabledSchedules - c.resyncPeriod = scheduleSyncPeriod - - schedulesInformer.Informer().AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - schedule := obj.(*api.Schedule) - - switch schedule.Status.Phase { - case "", api.SchedulePhaseNew, api.SchedulePhaseEnabled: - // add to work queue - default: - c.logger.WithFields(logrus.Fields{ - "schedule": kubeutil.NamespaceAndName(schedule), - "phase": schedule.Status.Phase, - }).Debug("Schedule is not new, skipping") - return - } - - key, err := cache.MetaNamespaceKeyFunc(schedule) - if err != nil { - c.logger.WithError(errors.WithStack(err)).WithField("schedule", schedule).Error("Error creating queue key, item not added to queue") - return - } - c.queue.Add(key) - scheduleName := schedule.GetName() - c.logger.Info("Creating schedule ", scheduleName) - //Init Prometheus metrics to 0 to have them flowing up - metrics.InitSchedule(scheduleName) - }, - }, - ) - - return c +func (c *scheduleReconciler) SetupWithManager(mgr ctrl.Manager) error { + s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1.ScheduleList{}, scheduleSyncPeriod) + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1.Schedule{}). + Watches(s, nil). + Complete(c) } -func (c *scheduleController) enqueueAllEnabledSchedules() { - schedules, err := c.schedulesLister.Schedules(c.namespace).List(labels.NewSelector()) - if err != nil { - c.logger.WithError(errors.WithStack(err)).Error("Error listing Schedules") - return - } +// +kubebuilder:rbac:groups=velero.io,resources=schedules,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=schedules/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=velero.io,resources=backups,verbs=create - for _, schedule := range schedules { - if schedule.Status.Phase != api.SchedulePhaseEnabled { - continue - } +func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := c.logger.WithField("schedule", req.String()) - key, err := cache.MetaNamespaceKeyFunc(schedule) - if err != nil { - c.logger.WithError(errors.WithStack(err)).WithField("schedule", schedule).Error("Error creating queue key, item not added to queue") - continue - } - c.queue.Add(key) + log.Debug("Getting schedule") + schedule := &velerov1.Schedule{} + if err := c.Get(ctx, req.NamespacedName, schedule); err != nil { + log.WithError(err).Error("error getting schedule") + return ctrl.Result{}, nil } -} -func (c *scheduleController) processSchedule(key string) error { - log := c.logger.WithField("key", key) - - log.Debug("Running processSchedule") - ns, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return errors.Wrap(err, "error splitting queue key") + if schedule.Status.Phase != "" && + schedule.Status.Phase != velerov1.SchedulePhaseNew && + schedule.Status.Phase != velerov1.SchedulePhaseEnabled { + log.Debugf("the schedule phase is %s, isn't %s or %s, skip", schedule.Status.Phase, velerov1.SchedulePhaseNew, velerov1.SchedulePhaseEnabled) + return ctrl.Result{}, nil } - log.Debug("Getting Schedule") - schedule, err := c.schedulesLister.Schedules(ns).Get(name) - if err != nil { - // schedule no longer exists - if apierrors.IsNotFound(err) { - log.WithError(err).Debug("Schedule not found") - return nil - } - return errors.Wrap(err, "error getting Schedule") - } + c.metrics.InitSchedule(schedule.Name) - switch schedule.Status.Phase { - case "", api.SchedulePhaseNew, api.SchedulePhaseEnabled: - // valid phase for processing - default: - return nil + patchHelper, err := patch.NewHelper(schedule, c.Client) + if err != nil { + log.WithError(err).Error("error new patch helper") + return ctrl.Result{}, nil } - log.Debug("Cloning schedule") - // store ref to original for creating patch - original := schedule - // don't modify items in the cache - schedule = schedule.DeepCopy() - // validation - even if the item is Enabled, we can't trust it // so re-validate currentPhase := schedule.Status.Phase cronSchedule, errs := parseCronSchedule(schedule, c.logger) if len(errs) > 0 { - schedule.Status.Phase = api.SchedulePhaseFailedValidation + schedule.Status.Phase = velerov1.SchedulePhaseFailedValidation schedule.Status.ValidationErrors = errs } else { - schedule.Status.Phase = api.SchedulePhaseEnabled + schedule.Status.Phase = velerov1.SchedulePhaseEnabled } // update status if it's changed if currentPhase != schedule.Status.Phase { - updatedSchedule, err := patchSchedule(original, schedule, c.schedulesClient) - if err != nil { - return errors.Wrapf(err, "error updating Schedule phase to %s", schedule.Status.Phase) + if err = patchHelper.Patch(ctx, schedule); err != nil { + log.WithError(err).Errorf("error updating schedule phase to %s", schedule.Status.Phase) + return ctrl.Result{}, nil } - schedule = updatedSchedule } - if schedule.Status.Phase != api.SchedulePhaseEnabled { - return nil + if schedule.Status.Phase != velerov1.SchedulePhaseEnabled { + log.Debugf("the schedule's phase is %s, isn't %s, skip", schedule.Status.Phase, velerov1.SchedulePhaseEnabled) + return ctrl.Result{}, nil } // check for the schedule being due to run, and submit a Backup if so - if err := c.submitBackupIfDue(schedule, cronSchedule); err != nil { - return err + if err := c.submitBackupIfDue(ctx, schedule, cronSchedule); err != nil { + log.WithError(err).Error("error running submitBackupIfDue") } - return nil + return ctrl.Result{}, nil } -func parseCronSchedule(itm *api.Schedule, logger logrus.FieldLogger) (cron.Schedule, []string) { +func parseCronSchedule(itm *velerov1.Schedule, logger logrus.FieldLogger) (cron.Schedule, []string) { var validationErrors []string var schedule cron.Schedule @@ -239,7 +174,7 @@ func parseCronSchedule(itm *api.Schedule, logger logrus.FieldLogger) (cron.Sched return schedule, nil } -func (c *scheduleController) submitBackupIfDue(item *api.Schedule, cronSchedule cron.Schedule) error { +func (c *scheduleReconciler) submitBackupIfDue(ctx context.Context, item *velerov1.Schedule, cronSchedule cron.Schedule) error { var ( now = c.clock.Now() isDue, nextRunTime = getNextRunTime(item, cronSchedule, now) @@ -259,23 +194,24 @@ func (c *scheduleController) submitBackupIfDue(item *api.Schedule, cronSchedule // lead to performance issues). log.WithField("nextRunTime", nextRunTime).Info("Schedule is due, submitting Backup") backup := getBackup(item, now) - if _, err := c.backupsClient.Backups(backup.Namespace).Create(context.TODO(), backup, metav1.CreateOptions{}); err != nil { + if err := c.Create(ctx, backup); err != nil { return errors.Wrap(err, "error creating Backup") } - original := item - schedule := item.DeepCopy() - - schedule.Status.LastBackup = &metav1.Time{Time: now} + patchHelper, err := patch.NewHelper(item, c.Client) + if err != nil { + return errors.Wrap(err, "error creating patch helper") + } + item.Status.LastBackup = &metav1.Time{Time: now} - if _, err := patchSchedule(original, schedule, c.schedulesClient); err != nil { - return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", schedule.Status.LastBackup) + if err := patchHelper.Patch(ctx, item); err != nil { + return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", item.Status.LastBackup) } return nil } -func getNextRunTime(schedule *api.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) { +func getNextRunTime(schedule *velerov1.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) { var lastBackupTime time.Time if schedule.Status.LastBackup != nil { lastBackupTime = schedule.Status.LastBackup.Time @@ -288,7 +224,7 @@ func getNextRunTime(schedule *api.Schedule, cronSchedule cron.Schedule, asOf tim return asOf.After(nextRunTime), nextRunTime } -func getBackup(item *api.Schedule, timestamp time.Time) *api.Backup { +func getBackup(item *velerov1.Schedule, timestamp time.Time) *velerov1.Backup { name := item.TimestampedName(timestamp) backup := builder. ForBackup(item.Namespace, name). @@ -297,27 +233,3 @@ func getBackup(item *api.Schedule, timestamp time.Time) *api.Backup { return backup } - -func patchSchedule(original, updated *api.Schedule, client velerov1client.SchedulesGetter) (*api.Schedule, error) { - origBytes, err := json.Marshal(original) - if err != nil { - return nil, errors.Wrap(err, "error marshalling original schedule") - } - - updatedBytes, err := json.Marshal(updated) - if err != nil { - return nil, errors.Wrap(err, "error marshalling updated schedule") - } - - patchBytes, err := jsonpatch.CreateMergePatch(origBytes, updatedBytes) - if err != nil { - return nil, errors.Wrap(err, "error creating json merge patch for schedule") - } - - res, err := client.Schedules(original.Namespace).Patch(context.TODO(), original.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) - if err != nil { - return nil, errors.Wrap(err, "error patching schedule") - } - - return res, nil -} diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go index 5900e673e9..990bb248e9 100644 --- a/pkg/controller/schedule_controller_test.go +++ b/pkg/controller/schedule_controller_test.go @@ -17,7 +17,6 @@ limitations under the License. package controller import ( - "encoding/json" "testing" "time" @@ -25,21 +24,22 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" - core "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" + "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/metrics" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestProcessSchedule(t *testing.T) { +func TestReconcileOfSchedule(t *testing.T) { + require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) + newScheduleBuilder := func(phase velerov1api.SchedulePhase) *builder.ScheduleBuilder { return builder.ForSchedule("ns", "name").Phase(phase) } @@ -49,45 +49,34 @@ func TestProcessSchedule(t *testing.T) { scheduleKey string schedule *velerov1api.Schedule fakeClockTime string - expectedErr bool expectedPhase string expectedValidationErrors []string expectedBackupCreate *velerov1api.Backup expectedLastBackup string }{ { - name: "invalid key returns error", - scheduleKey: "invalid/key/value", - expectedErr: true, - }, - { - name: "missing schedule returns early without an error", + name: "missing schedule triggers no backup", scheduleKey: "foo/bar", - expectedErr: false, }, { - name: "schedule with phase FailedValidation does not get processed", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseFailedValidation).Result(), - expectedErr: false, + name: "schedule with phase FailedValidation triggers no backup", + schedule: newScheduleBuilder(velerov1api.SchedulePhaseFailedValidation).Result(), }, { name: "schedule with phase New gets validated and failed if invalid", schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).Result(), - expectedErr: false, expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase gets validated and failed if invalid", schedule: newScheduleBuilder(velerov1api.SchedulePhase("")).Result(), - expectedErr: false, expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase Enabled gets re-validated and failed if invalid", schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).Result(), - expectedErr: false, expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, @@ -95,7 +84,6 @@ func TestProcessSchedule(t *testing.T) { name: "schedule with phase New gets validated and triggers a backup", schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedErr: false, expectedPhase: string(velerov1api.SchedulePhaseEnabled), expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", @@ -104,7 +92,7 @@ func TestProcessSchedule(t *testing.T) { name: "schedule with phase Enabled gets re-validated and triggers a backup if valid", schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedErr: false, + expectedPhase: string(velerov1api.SchedulePhaseEnabled), expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, @@ -112,7 +100,6 @@ func TestProcessSchedule(t *testing.T) { name: "schedule that's already run gets LastBackup updated", schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedErr: false, expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, @@ -121,134 +108,48 @@ func TestProcessSchedule(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { var ( - client = fake.NewSimpleClientset() - sharedInformers = informers.NewSharedInformerFactory(client, 0) - logger = velerotest.NewLogger() - ) - - c := NewScheduleController( - "namespace", - client.VeleroV1(), - client.VeleroV1(), - sharedInformers.Velero().V1().Schedules(), - logger, - metrics.NewServerMetrics(), - ) - - var ( + client = (&fake.ClientBuilder{}).Build() + logger = velerotest.NewLogger() testTime time.Time err error ) + + reconciler := NewScheduleReconciler("namespace", logger, client, metrics.NewServerMetrics()) + if test.fakeClockTime != "" { testTime, err = time.Parse("2006-01-02 15:04:05", test.fakeClockTime) require.NoError(t, err, "unable to parse test.fakeClockTime: %v", err) } - c.clock = clock.NewFakeClock(testTime) + reconciler.clock = clock.NewFakeClock(testTime) if test.schedule != nil { - sharedInformers.Velero().V1().Schedules().Informer().GetStore().Add(test.schedule) - - // this is necessary so the Patch() call returns the appropriate object - client.PrependReactor("patch", "schedules", func(action core.Action) (bool, runtime.Object, error) { - var ( - patch = action.(core.PatchAction).GetPatch() - patchMap = make(map[string]interface{}) - res = test.schedule.DeepCopy() - ) - - if err := json.Unmarshal(patch, &patchMap); err != nil { - t.Logf("error unmarshalling patch: %s\n", err) - return false, nil, err - } - - // these are the fields that may be updated by the controller - phase, found, err := unstructured.NestedString(patchMap, "status", "phase") - if err == nil && found { - res.Status.Phase = velerov1api.SchedulePhase(phase) - } - - lastBackupStr, found, err := unstructured.NestedString(patchMap, "status", "lastBackup") - if err == nil && found { - parsed, err := time.Parse(time.RFC3339, lastBackupStr) - if err != nil { - t.Logf("error parsing status.lastBackup: %s\n", err) - return false, nil, err - } - res.Status.LastBackup = &metav1.Time{Time: parsed} - } - - return true, res, nil - }) - } - - key := test.scheduleKey - if key == "" && test.schedule != nil { - key, err = cache.MetaNamespaceKeyFunc(test.schedule) - require.NoError(t, err, "error getting key from test.schedule: %v", err) + require.Nil(t, client.Create(ctx, test.schedule)) } - err = c.processSchedule(key) - - assert.Equal(t, test.expectedErr, err != nil, "got error %v", err) + _, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "ns", Name: "name"}}) + require.Nil(t, err) - actions := client.Actions() - index := 0 - - type PatchStatus struct { - ValidationErrors []string `json:"validationErrors"` - Phase velerov1api.SchedulePhase `json:"phase"` - LastBackup time.Time `json:"lastBackup"` + schedule := &velerov1api.Schedule{} + err = client.Get(ctx, types.NamespacedName{"ns", "name"}, schedule) + if len(test.expectedPhase) > 0 { + require.Nil(t, err) + assert.Equal(t, test.expectedPhase, string(schedule.Status.Phase)) } - - type Patch struct { - Status PatchStatus `json:"status"` + if len(test.expectedValidationErrors) > 0 { + require.Nil(t, err) + assert.EqualValues(t, test.expectedValidationErrors, schedule.Status.ValidationErrors) } - - decode := func(decoder *json.Decoder) (interface{}, error) { - actual := new(Patch) - err := decoder.Decode(actual) - - return *actual, err - } - - if test.expectedPhase != "" { - require.True(t, len(actions) > index, "len(actions) is too small") - - expected := Patch{ - Status: PatchStatus{ - ValidationErrors: test.expectedValidationErrors, - Phase: velerov1api.SchedulePhase(test.expectedPhase), - }, - } - - velerotest.ValidatePatch(t, actions[index], expected, decode) - - index++ + if len(test.expectedLastBackup) > 0 { + require.Nil(t, err) + assert.Equal(t, parseTime(test.expectedLastBackup).Unix(), schedule.Status.LastBackup.Unix()) } - if created := test.expectedBackupCreate; created != nil { - require.True(t, len(actions) > index, "len(actions) is too small") - - action := core.NewCreateAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - created.Namespace, - created) - - assert.Equal(t, action, actions[index]) - - index++ - } - - if test.expectedLastBackup != "" { - require.True(t, len(actions) > index, "len(actions) is too small") - - expected := Patch{ - Status: PatchStatus{ - LastBackup: parseTime(test.expectedLastBackup), - }, - } - - velerotest.ValidatePatch(t, actions[index], expected, decode) + backups := &velerov1api.BackupList{} + require.Nil(t, client.List(ctx, backups)) + if test.expectedBackupCreate == nil { + assert.Equal(t, 0, len(backups.Items)) + } else { + assert.Equal(t, 1, len(backups.Items)) } }) } diff --git a/pkg/util/kube/periodical_enqueue_source.go b/pkg/util/kube/periodical_enqueue_source.go new file mode 100644 index 0000000000..bb89295b47 --- /dev/null +++ b/pkg/util/kube/periodical_enqueue_source.go @@ -0,0 +1,88 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kube + +import ( + "context" + "reflect" + "time" + + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/workqueue" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, objList client.ObjectList, period time.Duration) *PeriodicalEnqueueSource { + return &PeriodicalEnqueueSource{ + logger: logger.WithField("resource", reflect.TypeOf(objList).String()), + Client: client, + objList: objList, + period: period, + } +} + +// PeriodicalEnqueueSource is an implementation of interface sigs.k8s.io/controller-runtime/pkg/source/Source +// It reads the specific resources from Kubernetes/cache and enqueues them into the queue to trigger +// the reconcile logic periodically +type PeriodicalEnqueueSource struct { + client.Client + logger logrus.FieldLogger + objList client.ObjectList + period time.Duration +} + +func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHandler, q workqueue.RateLimitingInterface, pre ...predicate.Predicate) error { + go wait.Until(func() { + p.logger.Debug("enqueueing resources ...") + if err := p.List(ctx, p.objList); err != nil { + p.logger.WithError(err).Error("error listing resources") + return + } + if meta.LenList(p.objList) == 0 { + p.logger.Debug("no resources, skip") + return + } + if err := meta.EachListItem(p.objList, func(object runtime.Object) error { + obj, ok := object.(metav1.Object) + if !ok { + p.logger.Error("%s's type isn't metav1.Object", object.GetObjectKind().GroupVersionKind().String()) + return nil + } + q.Add(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + }, + }) + p.logger.Debugf("resource %s/%s enqueued", obj.GetNamespace(), obj.GetName()) + return nil + }); err != nil { + p.logger.WithError(err).Error("error enqueueing resources") + return + } + }, p.period, ctx.Done()) + + return nil +} diff --git a/pkg/util/kube/periodical_enqueue_source_test.go b/pkg/util/kube/periodical_enqueue_source_test.go new file mode 100644 index 0000000000..2264729f7f --- /dev/null +++ b/pkg/util/kube/periodical_enqueue_source_test.go @@ -0,0 +1,64 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kube + +import ( + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestStart(t *testing.T) { + require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) + + ctx, cancelFunc := context.WithCancel(context.TODO()) + client := (&fake.ClientBuilder{}).Build() + queue := workqueue.NewRateLimitingQueue(workqueue.DefaultItemBasedRateLimiter()) + source := NewPeriodicalEnqueueSource(logrus.WithContext(ctx), client, &velerov1.ScheduleList{}, 1*time.Second) + + require.Nil(t, source.Start(ctx, nil, queue)) + + // no resources + time.Sleep(1 * time.Second) + require.Equal(t, queue.Len(), 0) + + // contain one resource + require.Nil(t, client.Create(ctx, &velerov1.Schedule{ + ObjectMeta: metav1.ObjectMeta{ + Name: "schedule", + }, + })) + time.Sleep(2 * time.Second) + require.Equal(t, queue.Len(), 1) + + // context canceled, the enqueue source shouldn't run anymore + item, _ := queue.Get() + queue.Forget(item) + require.Equal(t, queue.Len(), 0) + cancelFunc() + time.Sleep(2 * time.Second) + require.Equal(t, queue.Len(), 0) +} From 5c74aa22e6272062ffb9b2bcd0de4fcbe2e81bdd Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 31 Mar 2022 16:58:00 +0800 Subject: [PATCH 092/366] Avoid duplicated snapshotting, when CSI is enabled Fix #4758 Do not take snapshot for PV to avoid duplicated snapshotting, when CSI feature is enabled. Signed-off-by: Xun Jiang --- changelogs/unreleased/4797-jxun | 1 + pkg/backup/item_backupper.go | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 changelogs/unreleased/4797-jxun diff --git a/changelogs/unreleased/4797-jxun b/changelogs/unreleased/4797-jxun new file mode 100644 index 0000000000..795842678b --- /dev/null +++ b/changelogs/unreleased/4797-jxun @@ -0,0 +1 @@ +Do not take snapshot for PV to avoid duplicated snapshotting, when CSI feature is enabled. \ No newline at end of file diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 32a9f55044..fd4b16028b 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -39,6 +39,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/discovery" + "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/restic" @@ -415,6 +416,12 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie } } + // #4758 Do not take snapshot for CSI PV to avoid duplicated snapshotting, when CSI feature is enabled. + if features.IsEnabled(velerov1api.CSIFeatureFlag) && pv.Spec.CSI != nil { + log.Infof("Skipping snapshot of persistent volume %s, because it's handled by CSI plugin.", pv.Name) + return nil + } + // TODO: -- once failure-domain.beta.kubernetes.io/zone is no longer // supported in any velero-supported version of Kubernetes, remove fallback checking of it pvFailureDomainZone, labelFound := pv.Labels[zoneLabel] From 368a1ddf3c3c8f0e27fd9424024b55937dc8313e Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 00:57:28 +0800 Subject: [PATCH 093/366] Add CSI VolumeSnapshot related metrics. Signed-off-by: Xun Jiang --- pkg/apis/velero/v1/backup.go | 16 ++++ pkg/backup/request.go | 2 + pkg/controller/backup_controller.go | 21 +++++ pkg/controller/backup_deletion_controller.go | 21 +++++ pkg/metrics/metrics.go | 85 +++++++++++++++++++- 5 files changed, 143 insertions(+), 2 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 51ad422ec8..6148f5306b 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -310,6 +311,21 @@ type BackupStatus struct { // +optional // +nullable Progress *BackupProgress `json:"progress,omitempty"` + + // CsiVolumeSnapshotsAttempted is the total number of attempted + // CSI VolumeSnapshots for this backup. + // +optional + CsiVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` + + // CsiVolumeSnapshotsCompleted is the total number of successfully + // completed CSI VolumeSnapshots for this backup. + // +optional + CsiVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` + + // CsiVolumeSnapshotsStorageTotal is the total storage size of created + // snapshots for this backup. + // +optional + CsiVolumeSnapshotsStorageTotal resource.Quantity `json:"csiVolumeSnapshotsStorageTotal,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index bc483ef971..b5bf8ae246 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -20,6 +20,7 @@ import ( "fmt" "sort" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" @@ -48,6 +49,7 @@ type Request struct { VolumeSnapshots []*volume.Snapshot PodVolumeBackups []*velerov1api.PodVolumeBackup BackedUpItems map[itemKey]struct{} + CsiSnapshots []*snapshotv1api.VolumeSnapshot } // BackupResourceList returns the list of backed up resources grouped by the API diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index db8a8b40dc..a2bcfc0d4d 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -636,6 +636,14 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } + backup.Status.CsiVolumeSnapshotsAttempted = len(backup.CsiSnapshots) + for _, vs := range backup.CsiSnapshots { + if *vs.Status.ReadyToUse { + backup.Status.CsiVolumeSnapshotsCompleted++ + backup.Status.CsiVolumeSnapshotsStorageTotal.Add(*vs.Status.RestoreSize) + } + } + backup.Status.Warnings = logCounter.GetCount(logrus.WarnLevel) backup.Status.Errors = logCounter.GetCount(logrus.ErrorLevel) @@ -694,6 +702,19 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterVolumeSnapshotAttempts(backupScheduleName, backup.Status.VolumeSnapshotsAttempted) serverMetrics.RegisterVolumeSnapshotSuccesses(backupScheduleName, backup.Status.VolumeSnapshotsCompleted) serverMetrics.RegisterVolumeSnapshotFailures(backupScheduleName, backup.Status.VolumeSnapshotsAttempted-backup.Status.VolumeSnapshotsCompleted) + + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) + serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) + serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) + storageSize, ret := backup.Status.CsiVolumeSnapshotsStorageTotal.AsInt64() + if !ret { + log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, storageSize) + } + if backup.Status.Progress != nil { serverMetrics.RegisterBackupItemsTotalGauge(backupScheduleName, backup.Status.Progress.TotalItems) } diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 90707be704..d4c58553f4 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -28,6 +28,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -39,6 +40,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/discovery" + "github.com/vmware-tanzu/velero/pkg/features" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -407,6 +409,25 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR c.metrics.RegisterBackupDeletionFailed(backupScheduleName) } + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + vss, err := backupStore.GetCSIVolumeSnapshots(backup.Name) + if err != nil { + errs = append(errs, err.Error()) + } + + var restoreSizeTotal resource.Quantity + for _, vs := range vss { + restoreSizeTotal.Add(*vs.Status.RestoreSize) + } + + storageSize, ret := restoreSizeTotal.AsInt64() + if !ret { + log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + c.metrics.RegisterCsiStorageSizeSub(backupScheduleName, backup.Name, storageSize) + } + // Update status to processed and record errors req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 072db9134a..0a9383456b 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -54,6 +54,10 @@ const ( volumeSnapshotAttemptTotal = "volume_snapshot_attempt_total" volumeSnapshotSuccessTotal = "volume_snapshot_success_total" volumeSnapshotFailureTotal = "volume_snapshot_failure_total" + csiSnapshotAttemptTotal = "csi_snapshot_attempt_total" + csiSnapshotSuccessTotal = "csi_snapshot_success_total" + csiSnapshotFailureTotal = "csi_snapshot_failure_total" + csiSnapshotStorageTotal = "csi_snapshot_storage_total" // Restic metrics podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" @@ -67,8 +71,6 @@ const ( pvbNameLabel = "pod_volume_backup" scheduleLabel = "schedule" backupNameLabel = "backupName" - - secondsInMinute = 60.0 ) // NewServerMetrics returns new ServerMetrics @@ -268,6 +270,38 @@ func NewServerMetrics() *ServerMetrics { }, []string{scheduleLabel}, ), + csiSnapshotAttemptTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotAttemptTotal, + Help: "Total number of CSI attempted volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotSuccessTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotSuccessTotal, + Help: "Total number of CSI successful volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotFailureTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotFailureTotal, + Help: "Total number of CSI failed volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotStorageTotal: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: metricNamespace, + Name: csiSnapshotStorageTotal, + Help: "Total size of CSI volume snapshots storage size", + }, + []string{scheduleLabel, backupNameLabel}, + ), }, } } @@ -385,6 +419,18 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[volumeSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } + if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } } // InitSchedule initializes counter metrics for a node. @@ -593,3 +639,38 @@ func (m *ServerMetrics) RegisterVolumeSnapshotFailures(backupSchedule string, vo c.WithLabelValues(backupSchedule).Add(float64(volumeSnapshotsFailed)) } } + +// RegisterCsiSnapshotAttempts records an attempt to snapshot a volume by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { + if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsAttempted)) + } +} + +// RegisterCsiSnapshotSuccesses records a completed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { + if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotCompleted)) + } +} + +// RegisterCsiSnapshotFailures records a failed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { + if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsFailed)) + } +} + +// RegisterCsiStorageSizeAdd records volume snapshot's storage size increase created by CSI plugin. +func (m *ServerMetrics) RegisterCsiStorageSizeAdd(backupSchedule, backupName string, csiStorageSize int64) { + if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + g.WithLabelValues(backupSchedule, backupName).Add(float64(csiStorageSize)) + } +} + +// RegisterCsiStorageSizeSub records volume snapshot's storage size decrease created by CSI plugin. +func (m *ServerMetrics) RegisterCsiStorageSizeSub(backupSchedule, backupName string, csiStorageSize int64) { + if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + g.WithLabelValues(backupSchedule, backupName).Sub(float64(csiStorageSize)) + } +} From 4daeec7ab9529b2fb6501792bec5728d51797856 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 04:45:06 +0000 Subject: [PATCH 094/366] Update CRD and GRPC. Signed-off-by: Xun Jiang --- changelogs/unreleased/4818-jxun | 1 + config/crd/v1/bases/velero.io_backups.yaml | 13 +++++++++++++ config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup.go | 3 +-- pkg/backup/request.go | 1 + pkg/controller/backup_controller.go | 14 +++++++------- pkg/metrics/metrics.go | 12 ------------ 7 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/4818-jxun diff --git a/changelogs/unreleased/4818-jxun b/changelogs/unreleased/4818-jxun new file mode 100644 index 0000000000..f3e478c706 --- /dev/null +++ b/changelogs/unreleased/4818-jxun @@ -0,0 +1 @@ +Add CSI VolumeSnapshot related metrics. diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index fec76fbeee..5cbdd76d37 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -354,6 +354,19 @@ spec: format: date-time nullable: true type: string + csiVolumeSnapshotsAttempted: + description: CsiVolumeSnapshotsAttempted is the total number of attempted + CSI VolumeSnapshots for this backup. + type: integer + csiVolumeSnapshotsCompleted: + description: CsiVolumeSnapshotsCompleted is the total number of successfully + completed CSI VolumeSnapshots for this backup. + type: integer + csiVolumeSnapshotsStorageTotal: + description: CsiVolumeSnapshotsStorageTotal is the total storage size + of created snapshots for this backup. + format: int64 + type: integer errors: description: Errors is a count of all error messages that were generated during execution of the backup. The actual errors are in the backup's diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index cb45808fcb..da0f366b21 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd6\xe3E\x84\xdd\xcc\x19;\x8e\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe03MQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>n\x9e\xff\xf1K\xef3@\x8e63\xb2rL\x14\x8f\x18H\v\x02\x9eyY`\x02\xf9\xc1\x1d\x84\x03\x83\x95A\x8b\xcaYp\a\x84LT\xae6\bz\a?\xd7[4\n\x1d\xda\x064@V\xd4֡\x01\xeb\x84C\x10\x0e\x04TZ*\aR\x81\x93%\xc2\x1f>>n@o\xff\x82\x99\xb3 T\x0e\xc2Z\x9dI\xe10\x87\xa3.\xea\x12\xfd\xd8?\xae\x1b\xa8\x95\xd1\x15\x1a'#\x9d}\xebHU\xe7\xeb`y\x1f\x88\x02\xbe\x17\xe4$N\xe8\x97\x11\xa8\x88y \x1a\xad\xc7\x1d\xa4m\x97\xcb\x12\xd2\x03\f\xd4I\xa8\x80\xfc\x1a\xbe\xa0!0`\x0f\xba.r\x92\xc2#\x1a\"X\xa6\xf7J\xfew\x03ۂ\xd3\xa3\xc1`u\x0fg\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:\xf7\x80\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŷ\xc0;p\xa6\xc6\t\xca\bc\xc4i\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc9F\xf0{&\xcaA\xeb\x97%B\xfc\v\xf5i\xf5\x1ed\xec%\xc1\x16\x0f\xe2(\xb5\tK\x0ffh\x8b\x80_1\xab\x1d\x8eɿp\x90\xcb\xdd\x0e\r\xc1\xa9\x0e¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xd9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ʼ\x16\x05He\x9dP\x99_\x8fh\xf0:_\x0f\xcc1\xf9\fg\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cw\x1dZ\xb0\xb6M-{+H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x99\x04\xddp\xc4\xfb\x12\x85\xd8b\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xebAf\ao\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5ij\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\xff'a\xa3\xda\u007f\x83\xd0nΆ\xbe\xaf\xd0\x12I%\xb9\xf3\x9b\x1d`Y\xb9\xd3\rH\x17\xbf.A$g\xa5\x9d\xff\xef\x981\x97K\xfcf8\xf2]%~\x96+K\x10\x89+\xcd\xf4\u007f\x87Lac\xf1%؊d\x86\xfc\xd2\x1du\x03r\xd70$\xbf\x81\x9d,\x1c\x9a\x01g~\xd3~y\x0fb\xa4\xd8;j\xa5p\xd9\xe1\xe1+y^\xb6M8%\xd2e8\xd8\xfb\xafџ\xef\x1b\xe6\x05\xb8\xc0\x01\xaa4X\xfa\xc0\xf7\x89\xa9\xd9~a\x8f\xea\xe3\xe7O\x98ϑ\a\xd2$\xefl!\x1f\a\xc8v\xa7\x0eNy\xea2\x82\xeb\xd3\xc47>\xa5q\x03\x02^\xf0\xe4=\x16\xa1\x80\x98#h\xa2\x89H\xe7\x9c8\x9c[a!{\xc1\x13\x83\tɒ\xc5ѩ\xa2\xe0\xdb\v\x9eR\xba\r\bH8I\x1b\x92@DI\xfa\xc0\x84\xe0\xd8:\x9dx\xc0\x89\xaf\xa8\x8b\x96\x17\a\xe9\x8a$\xb6H\xfb7,\xb3a['iȌ\xfd`=\x8bh\x17\x1cd\x95\xb8P2s`\x91wKL}=\x8bB\xe6\xcdD^\xee7j\xda\x1b\xee\xb7\xcf\xdam\xd4\r<|\x956d\x1f?i\xb4\x9f\xb5\xe3/߄\x9c\x1e\xf17\x10\xd3\x0f\xe4\xed\xa5\xbc\xda&:tsh\t\xc2\xed\xdb\xc6Gx\r{\xa4\x85\x8d\xa2\xb8%Ѓ3\xa2~\xbay\xfb\xd0oem9I\xa6\xb4Z\xb1\xa9\\\x8f\xcd䉝\bR\x9b\x1eG\xceQk&\xf5\x13&\x82}\"K\xe2\xc7\xfb\x1co!2\xcc!\xaf\x99\x98\x9c\x99\x14\x0e\xf72\x83\x12\xcd~\xcept[E\xfa=\r\x85D\xad\xebۅ\x12\x96f\xdac\v\xaa;_FfE;7\xa1Wd\xf6b\u05c9\x84\xe4t\xd7\xe5\x15\xb1\x89e\xffc\x91\xba\"\xcf\xf9,I\x14\x8f\x17h\xfc\vxqn\xfb=b\xdeB\x96\x82\x93\x8c\xffCf\x8e\x05\xfa\u007f\xa1\x12\xd2$\xec\xe1\x8f|4T`ol\xc8bu\xa7\xa1\x19\xa4\x05\xe2\xefQ\x14\xe7\xa9\xee\x91\xc5i\xd2-XxC\xaewg\x1e\xcb\r\xbc\x1e\xb4\xf56u'q4\xa5\xdao\xd2\xc2\xf5\v\x9e\xaeo\xce\xf4\xc0\xf5F]{\x03\u007f\xb1\xbai\xbc\x05\xad\x8a\x13\\\xf3\xd8\xeb\xdf\xe2\x04%JbR7>\x82Ku\x95)\x96\x8c\x9e\x00\rlΝ\xc8͝\xc3:I\x0e+m]2*\x8f\xda:\x9fY칥\x97d\xb1\xc0\xcbP\xc8^\x81\xd8\xf9\x93?m\xe2\x99\x0e\xa9\xbdA\u0095\xb8f\xe75,\xb1\xb1Ɉy\xa0\x14X]\xb7;\xd8\xeb\xd3k\u007f\xd0Ó\x88\x8c\x9d\x8bE\xb8\x95\xd1\x19Z;/\"\t\xdaz!I\xd8$\b\x85\x0f`\xfc\x81\xc9|R2\xb6t\x87\x94\x88t\xa1+\xff\U000354fd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9cԃ1o\n\x9c\xfe\xecGv\x12Y\a\xfd\x1a\xcfW'\x0f3\xc7\x1a\x1f\n!\xc8\x1dH\a\xa82]+N\xbf\xd0V\xe7)<\v\xbc\x82N&Y\x9a\x82\xa0\x86\xaa.\xd3\b\xb0b\xa9\x93j6O\xd3\xed\xfe\x93\x90ŷ`\x9b\x93%\xeaz\xd6p\xb6\xadǶ'?\xb2wP^\x8a\xaf\xb2\xacK\x10%\x91>5\xec\xd9\xf9\xe2\x98\x1e\xc7\xe1UHǖ\x83\xe0\xb2\x19q\x9a6UU\xa0Kݑ[\xdci\xc3\xfb\xd9\xca\x1c\x1b\xc3\x1c\xa4@+\x10\xb0\x13\xb2\xa8M\xa2\x86\xbc\x88\xb6\x97\xc4\x1aAY\xbc_\x10\x916\xf9\x8aI\x91\x90\x88Mt\x16\xe7\xb5ue\xd2]\xc5G\x83i\xee\xd9RR:\xbag\x95\x91$K\xfa\xbd=\xb4 bB\x9d\xbe\xbbhg\xed\xbb\x8b\xb6о\xbbh\x93\xed\xbb\x8b\xb6ܾ\xbbh\xa1}w\xd1b\xfb\xee\xa2}w\xd1\xe6\xba\xcdi\xeb%\x8c|\xc5\xfdď\x8bX$\x1cOϡ8\x03?TS\xdc\xfb\xea\xfb\xd4\n\xcb\xcd\xf8\xa8\x91\xba\xdaPֿ\xe2\x1b\tc\x12\xd0\x16]\xb4\xa6\xa4)\xb9\xa4\r\x12\xc5\xdb\x17\x10/\x14a&\x95S\x8eWߦ\x14\xfc,\x95\xf9\xf4\xebL\x9b2\x9bXh\xaa\xe3$#t\x887\x1b\xc8\xed\xed\u0590\xf4\xebu\xd8ύ\x98\xfe\xcdkP\x13Jq\x16\np\xe6\vs\xe7\xe85\b=\xfa\x043\xbd\x82\xd1\xdf\r\xbd\x16\xaad\xa6kc\xc2I\x10:q\xfca\xdd\xff\xc5\xe9P)\x03\xaf\xd2\x1dF\x96\xf2z@\xc5gXj\xdf-{\x8d\xf2\x16\xae\x98\f\xe9\bڀ\x92\x05\x93sFZ{\xe4\x85?W>\x84\xbbx_·\x1fi\xb54o\xae\xa0\xe9W\xc8L\xa8\xe8K\x8f\x8c\xd2\v\x85\xd3kd\xe6\x8bZ.\xa9\x8c\x19ֽL\x02]\xae\x87I\x89\x1c\x17j_\xdeP\xf1\x92X\xed\xf8\x9b\x0f\xc6RjZ\xdeTɲX\x10\x98X\xbfүL\x99\ayA\xd5J\x12q\x96+T.\xaeK\tu \xb3\xebH\xaeF\x19\xa93\x99\x05\xf0\xb4\xaaY\xac\x06Y\xf4\x91\xe7\xf1[\xac\xf7\xb8\xa4\xcac\x91bo\xac\xe8h*6&潴\x8e\xa3_\xa71\x014\xa5zc\xa2:c\x02\xe2l\xcdFjM\xc6\x04\xec\x05\xb3;+%3?\x8e_\x84\x84E\xfbV\xfc\xb5$\xea\xad\v\xd3&G3롧\xa29\x8bb?\xe35\x98sPf\x1f/NR\xaf\xae\xd7?\xc6rݔ\x84g\xf0\xb3T\xb9\x97\x13\x12\xf4\x8e\x9f\xc0\x17\x85\xb9(\xa6qWZ\u007fo\x1c\xe8 ҰX\t\xc37ɷ'\x9f\xad\xb0kx\x10١\xdf\x11\x0e\xc2RLZ\x8e\xbaa\xd7M\x98v\x1bGї\xeb5\xc0O\xba\x89\x84\xbb\u05ec\xac,\xab\xe2\x04\xb5E\xb8\xee\x0fy[\xcc1*\x01V\x89\xca\x1et\xbc\x03\xbb\x10v|\xe9\xf7\x1e\x89\xe8\xe3\rج\xd0u\xde@\x9f`\x9eP'x|f߇\xef\x0ef\xed=\xca\xe0\xdf\xc4Hbx\xcd\xf2\xc7\xf7\x8f\xf0\xad\xd3F\xec\xf1\x17\xed/#/Q\xa2\u07fbw\x13=谘q\x8b\x05Yb\x84\b\xe1Z\xf4\x00X\x9bH\x0f\xbb\xa1M~\x10\x96c\xeamf\xff9W,,\xe6\xe9\xe9\x17\xbf\x00'K\\\u007f\xaa}6eU\tc\x91\xa8\x19\x17\xe6\am\xe9\xbf\a\xfd:\xa6\xf0tX\xf3\x8fC\xbc\rr\xb2\x9e\x936\x17a\xef\xafMG\xc1\x8b$Z\x12\xd4\xe7\xf1Q\x9d@\xaf\xc3$\xbf\xcb\xf5ع\xc4\x14\x9c\xce\xeb\x12\x14X\xfbb\xbb\xf7\xbd\xfb;\xe5\xb1Lݿw\xc2\xd5v\xf9\x06>w\x8b\xefm\x84#\x9f\xda\xf0\xc5]\x0f\xc2_t}\xd3%\xfc\x90\xa1\uef412ϧ\xfb\xf3\x11\xfc҅\xc9=j\x9c\x1bonӿ\n\xdbd\xc1GM|\vΏd\x0f\x9a\xa0a\x0exD\x05Zqқ\xaf\xc4\xfa\xd7X\x86cƒI\x1d(!\xab^W\x85\x16y\xdc\xe1\xd1f\x85\x17<\x9eX\u007f\x99#\x9a\x0fv\x06&\xbf\x18\xb0\xd3f\x8c\b\xe7\n\xd3\x1b\x96;ȅ\xc3\xd5(\xd0$\xdd7*l|>\xb4xo\x9e;\xf9\x1dćK\xf1q\x03\u007f\xbaT\xa2\xb5b\x1f/̿\x92\x02ۣB\xb6\x9b#\xeb\x0f\xf1L{\x1aѿ.\xeeS*\"s\xb5\b\x13\xc4lR\xa7ׇ1\xb3R\xe8=\xecd\xc1]\xc3\xd3\x1eA\xb3O\xa9\x1d\xa9\x1c\xeeq\xe8\xae\xe2\xd7J\x9a\x14K\xf0\xd0t$\xdap>\x8d\xb5A\xfb\x04\x0e\x16r/I\x8d\x12\xb3\xf7\xc2l\xc5\x1eW\x99.\n\xe4҅s\xbc\xbe%\xaf=\xec\xd1'nΖ\xf6S\xb7o\xf4\xa7\x82\xb0{8\xf1ś\x9b`\xa1\xc7\x1d\xd4R\xfcE\x9b\x1b(\xa5\xa2\u007f\xc8\r\xe3\xc84\x0e\xbe\xc8\x1e\xf0\xeb\x02\vx?R\x9f\xe6@\xbb\xa3\xdd0\x8aٔ\xff0~\x88\xb9\x82\xcfxn\xee\xfc\xb9$\xe6\x9c{\x19{ׇ\xbalԣ\xd1{\n\x9dF~\xbc\x8f\xbal\xe4\xb7Ga\x9c\x14Eq\xf2\x93L\xce>\xf2\xc3'$m2iR\xc6\xc9\x1a\xb0\\\xa2l\xe8\xd6\xc6iRyI\xe0\x13ŭ\xae]o\x83\xb6\x1b|D,\xe2\x9ck\xf8\xac\x1d\xc64\x9e\xec\xc3$\xbb\x8a֭p\xb7\xd3\xc6\xf9\xb0p\xb5\x02\xb9\v&j\x04.ixN]\xfbgv@\xba6}\xd2J/{\x9f\x06\x85e\xe9u\xfc\xd8\x0f\x9f.\x89,#\x0f\bo\xad\x13ň\xd6\xf8M\xd9j\xf6\x05H\xfa0\xff\xb7\x11\xe3xF\xf0M\xb7\u007fS\xe2_\x97[\x1f\x1d18O9>\xfd\xf7\x1a\xb3\x98ʣl\x11\x15\xbc\x1a\xe9\x1ci\xa9nn\x1f\x1c饢\x00\xaba'&\x1et\x98ӗ\xfc\xbbv\xa2\xd8L\xe7\x94\xfaNg\xd39.\x8b\x87\x9f/N\x13[\xb6L\x82\x89e\xf9\xea2i\xe3Xbev\x10jOBet\xbd?D\xb9\x9c\xb07S\x99\xf8\x9a\x90\x82\xaa\xa8\xf7$\xea!7\xeej\xa3:qqȖ\xe7\x1dtE\xf62\x89i\xc8\x0eƧ\xden\xc3\x13\r\xab\x9d\xd1\xe5*\xf0\x82\xf3\x067!^5R\x93SF\xd1\xd5\x04\xd0\xf6.4\x8bAU\xa1\x02a\x03>\t\xa5o\xf3l\x9d\v\x1e\x9d0.\xd5\xd5\xfb\xd2\xeb\xbc\xe0\xe51\xe4q|\xbf\x84hܗ\x00\xde\x0f\x1fݣ\xb8Y\xc5W\xe6|\xb4\xefE\xc1\x92\xf3g\x90\x03\xa8\xd1\xf3\x8a3\xb7\xad\xe7\xa4\xf5\xd1\xff\xeb\xfag\xc7\xc6\xc2<\xa4xjσ\xee\x83sT\xda\xe5-\xc4\xe0]\x8d\xd0\xe3\x0fr\xe7\x0fR2\xc2\xfa\x8f\u007f\xf3\xf3\xd1c\x92\xcf\xf2a\xd6]aO\xa4\xf1;\xe0\x13V\x063ڽc\xcbx,\x90\xfc\b\x8b\xd8\xf7\x84>\\\xe4H\xf6\x03X\xfb\xd19,\xab\xd1\x19g\"\xd8vؔ\xb2\x14\xb1\xc3\xc8B\xe2\x03\x88\x11X(-\x9a\tY/XP\xe3\xc4\\\xb6\xa0f\xd8Ԃl\x9d\x91\xd2\xda\xd5\xe3欉\x03\xdfyu\xaf\xc2(\xa9\xf6K{\xec\xdfC\xb7\x91x(@\x18\x89\x88F\x96\xd1\xc4H\x8b\x11Q' \x8a8N\xbcK6\b\x92\xde)$\x1a\xb5\x03g\x1fY\x81杽\x1df\n_\xda,\x85\xc82$q\xfd<|\xe8\xf4\xfa\x9a\xff\x88o\x99\xf2\x9f\x99V\xde\xdc\xda;\xf8\x8f\xff\xbc\x82\x90\x06{\x8e\x8f\x96\xd2\xc7\xff\v\x00\x00\xff\xffy\x8fd\x10\x14V\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x82$\xf0۬Nj\x18\xbb\x993֎\xf3\x10\xe4\x81\xea.I\xa3\xb1R\xab\x1b\x10\x95\xc4/\x0e\x15\xfde\xd7/\xffj\xd7R_\x1f\xbe\xbbx\x91*\xbf\x81\xdb\xda:]\xfe\x8cV\xd7&\xc3O\xb8\x95J:\xa9\xd5E\x89N\xe4\u0089\x9b\v\x00\xa1\x94v\x82>[\xfa\x13 \xd3\xca\x19]\x14hV;T\xeb\x97z\x83\x9bZ\x169\x1a\x06\x1e\xa7>\xfci\xfd/\xeb?]\x00d\x06y\xf8\x93,\xd1:QV7\xa0ꢸ\x00P\xa2\xc4\x1b؈쥮\xec\xfa\x80\x05\x1a\xbd\x96\xfa\xc2V\x98\xd1\\;\xa3\xeb\xea\x06\xda\x1f\xfc\x90\x80\x87_\xc3\xf7<\x9a?\x14Һ\x1f;\x1f\u007f\x92\xd6\xf1\x0fUQ\x1bQ43\xf17+ծ.\x84\x89_/\x00l\xa6+\xbc\x81\xcf4E%2\xcc/\x00\xc2rx\xcaU@\xf8𝇐\xed\xb1\x14\x1e\x17\x00]\xa1\xfa\xf8p\xff\xfc\x8f\x8f\xbd\xcf\x009\xda\xcc\xc8\xca1Q\xa1\xc1`uw'\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:w\x8f\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŧ\xc0\x1bp\xa6\xc6\t\xca\bc\xc4q\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc)F\xf0[&\xca^\xeb\x97%B\xfc\x1b\xf5i\xf5\x1ed\xec%\xc1\x06\xf7\xe2 \xb5\tK\x0ffh\x83\x80_0\xab\x1d\x8eɿp\x90\xcb\xed\x16\r\xc1\xa9\xf6¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xc9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf0:]\x0f\xcc1\xf9\x04g\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cv\x1dZ\xb0\xb6M-{#H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x9a\x04\xddp\xc4\xfb\x12\x85\xd8`\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xeb^f{o\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5qj\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\uf4f0Q\xed\xbfAh\xefO\x86\xbe\xaf\xd0\x12I%\xb9\xf3\xf7[\xc0\xb2r\xc7+\x90.~]\x82H\xceJ;\xff\xef\x981\xe7K\xfc\xfdp\xe4\xbbJ\xfc,W\x96 \x12W\x9a\xe9\u007f\x87Lac\xf1\x18lE2C~ꎺ\x02\xb9m\x18\x92_\xc1V\x16\x0è3\xbfh\xbf\xbc\a1R\xec\x1d\xb5R\xb8l\u007f\xf7\x85~\xd2h?k\xc7_\xbe\n9=\xe2o \xa6\x1f\xc8\xdbKy\xb5Mt\xe8\xe6\xd0\x12\x84۷{\x1f\xe15\xec\x91\x16\xee\x15\xc5-\x81\x1e\x9c\x11\xf5\xd3\xcdۇ~+k\xcbI2\xa5ՊM\xe5zl&O\xecD\x90\xda\xf48r\x8aZ3\xa9\x9f0\x11\xec\x13Y\x12?\xde\xe7x\v\x91a\x0ey\xcd\xc4\xe4̤p\xb8\x93\x19\x94hvs\x86\xa3\xdb*\xd2\xefi($j]\xdfΔ\xb04\xd3\x1e[P\xdd\xf922+ڹ\t\xbd\"\xb3\x17\xbbN$$\xa7\xbb.\xaf\x88M,\xfb\x1f\x8b\xd4\x15y\xcegI\xa2x8C\xe3\x9f\xc1\x8bS\xdb\xef\x11\xf3\x16\xb2\x14\x9cd\xfc_2s,\xd0\xff\a\x95\x90&a\x0f\u007f䣡\x02{cC\x16\xab;\r\xcd -\x10\u007f\x0f\xa28Mu\x8f,N\x93n\xc1\xc2\x1br\xbd=\xf1X\xae\xe0u\xaf\xad\xb7\xa9[\x89\xa3)\xd5~\x93\x16._\xf0xyu\xa2\a.\xefե7\xf0g\xab\x9b\xc6[Ъ8\xc2%\x8f\xbd\xfc%NP\xa2$&u\xe3#\xb8TW\x99b\xc9\xe8\t\xd0\xc0\xe6܉\xdc\xdc9\xac\x93\xe4\xb0\xd2\xd6%\xa3\xf2\xa0\xad\xf3\x99Ş[zN\x16\v\xbc\f\x85\xec\x15\x88\xad?\xf9\xd3&\x9e\xe9\x90\xda\x1b$\\\x89kv^\xc3\x12\x1b\x9b\x8c\x98\aJ\x81\xd5e\xbb\x83\xbd>\xbd\xf4\a=<\x89\xc8عX\x84[\x19\x9d\xa1\xb5\xf3\"\x92\xa0\xad\x17\x92\x84M\x82P\xf8\x00\xc6\x1f\x98\xcc'%cKwH\x89Hg\xba\xf2w_:\xd9K\xda\xfc\xf4\xf7\x92\xf0\x9d\x8b\x17\xf0\x9e-K1<\x19LB\xf1֏\x8c\xdb$\x00\xf2\xa1\x81\xd9ռ\xd5\xd3=\xc8 H\xbf\x053]Ju\xcf\x13\xc0w\xefn\xd6\x1b%\x89oq\xdco\xe3ؖ\xe8\xcd\a\u07bd\xa9\x1e\x91\xe6̽\xc1\x1e\xe7N\xf3\xdc\xe4(&\x82T\xdau\xd3\t\x04\xb7\xd2\xf9\a\v[i\xac\xeb\"\x9a*\x14\xf5\xc2\xeeo۹\x91\x93\xba3\xe6M\x81ӟ\xfd\xc8N\"k\xaf_\xe3\xf9\xea\xe4a\xe6X\xe3C!\x04\xb9\x05\xe9\x00U\xa6k\xc5\xe9\x17\xda\xea<\x85g\x81W\xd0\xc9$KS\x10\xd4P\xd5e\x1a\x01V,uR\xcd\xe6i\xba\xdd\u007f\x10\xb2\xf8\x1als\xb2D]\xcf\x1aζ\xf5\xd8\xf6\xe4G\xf6\x0e\xcaK\xf1E\x96u\t\xa2$ҧ\x86=[_\x1c\xd3\xe38\xbc\n\xe9\xd8r\x10\\6#NӦ\xaa\nt\xa9;r\x83[mx?[\x99cc\x98\x83\x14h\x05\x02\xb6B\x16\xb5IԐg\xd1\xf6\x9cX#(\x8b\xf7\v\"\xd2&_1)\x12\x12\xb1\x89\xce⼶\xaeL\xba\xab\xf8`0\xcd=[JJG\xf7\xac2\x92dI\xbf\xb7\x87\x16DL\xa8\xe37\x17\xed\xa4}s\xd1\x16\xda7\x17m\xb2}sі\xdb7\x17-\xb4o.Zl\xdf\\\xb4o.\xda\\\xb79m\xbd\x84\x91\xaf\xb8\x9f\xf8q\x11\x8b\x84\xe3\xe99\x14g\xe0\x87j\x8a[_}\x9fZay?>j\xa4\xae6\x94\xf5\xaf\xf8F\u0098\x04\xb4E\x17\xad)iJ.i\x83D\xf1\xf6\x05\xc4\vE\x98I\xe5\x94\xe3շ)\x05?Ke>\xfd:Ӧ\xcc&\x16\x9a\xea8\xc9\b\x1d\xe2\xcd\x06r{\xbb5$\xfdz\x1d\xf6s#\xa6\u007f\xf3\x1aԄR\x9c\x85\x02\x9c\xf9\xc2\xdc9z\rB\x8f>\xc1L\xaf`\xf47C\xaf\x85*\x99\xe9ژp\x12\x84N\x1c\xbe[\xf7\u007fq:T\xca\xc0\xabt\xfb\x91\xa5\xbc\xeeQ\xf1\x19\x96\xdau\xcb^\xa3\xbc\x85+&C:\x826\xa0d\xc1䜑\xd6\x1ey\xe1ϕ\x0f\xe1\xceޗ\xf3\xe1GZ-͛+h\xfa\x152\x13*\xfa\xdc#\xa3\xf4B\xe1\xf4\x1a\x99\xf9\xa2\x96s*c\x86u/\x93@\x97\xebaR\"Džڗ7T\xbc$V;\xfe\u20f1\x94\x9a\x967U\xb2,\x16\x04&֯\xf4+S\xe6A\x9eQ\xb5\x92D\x9c\xe5\n\x95\xb3\xebRB\x1d\xc8\xec:\x92\xabQF\xeaLf\x01O֠\xccU\x97,d\xa5N+O\xd2kJfAs\xbd\xc9r%\xc9\xfbՋ\xbe\x87\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=Ω\xf2X\xa4\xd8\x1b+:\x9a\x8a\x8d\x89yϭ\xe3\xe8\xd7iL\x00M\xa9ޘ\xa8Θ\x808[\xb3\x91Z\x931\x01{\xc1\xec\xceJ\xc9̏\xe3\x17!aѾ\x15\xbf\x96D\xbdua\xda\xe4hf=\xf4T4gQ\xecg\xbc\x06s\x0e\xca\xec\xe3\xc5I\xea\xd5\xf5\xfa\xc7X\xae\x9b\x92\xf0\f~\x94*\xf7rB\x82\xde\xf1\x13\xf8\xa20\x17\xc54\xeeJ\xeb\xef\x8d\x03\x1dD\x1a\x16+a\xf8&\xf9\xe6\xe8\xb3\x15v\rw\"\xdb\xf7;\xc2^X\x8aI\xcbQ7\xec\xb2\tӮ\xe3(\xfar\xb9\x06\xf8A7\x91p\xf7\x9a\x95\x95eU\x1c\xa1\xb6\b\x97\xfd!o\x8b9F%\xc0*Qٽ\x8ew`\x17\u008e\xc7~\uf448>ހ\xcd\n]\xe7\r\xf4\t\xe6\tu\x84\x87g\xf6}\xf8\xee`\xd6ޣ\f\xfeM\x8c$\x86\xd7,\xbf\u007f\xff\b\xdf:m\xc4\x0e\u007f\xd2\xfe2\xf2\x12%\xfa\xbd{7у\x0e\x8b\x19\xb7X\x90%F\x88\x10\xaeE\x0f\x80\xb5\x89\xf4\xb0\x1b\xda\xe4\aa9\xa6\xdef\xf6\x9fs\xc5\xc2b\x9e\x9e~\xf2\vp\xb2\xc4\xf5\xa7\xdagSV\x950\x16\x89\x9aqa~І\xfe\xbbׯc\nO\x875\u007f?\xc4\xdb '\xeb9is\x16\xf6\xfe\xdat\x14\xbcH\xa2%A}\x1e\x1f\xd5\t\xf4:L\xf2\xbb\\\x8f\x9dKL\xc1\xe9\xbc.A\x81\xb5/\xb6{\u07fb\xbfS\x1e\xcb\xd4\xfd{'\\m\x97o\xe0s\xb7\xf8\xdeF8\xf2\xa9\r_\xdc\xf5 \xfcE\xd77]\xc2\x0f\x19\xea\xde\x1b(\xf3|\xba=\x1d\xc1/]\x98ܣƹ\xf1\xe66\xfd\xab\xb0M\x16|\xd4ķ\xe0\xfcH\xf6\xa0\t\x1a\xe6\x80\aT\xa0\x15'\xbd\xf9J\xac\u007f\x8de8f,\x99ԁ\x12\xb2\xeauUh\x91\xc7\x1d\x1emVx\xc1\xe3\x89\xf5\x979\xa0\xf9`g`\xf2\x8b\x01[mƈp\xaa0\xbda\xb9\x81\\8\\\x8d\x02M\xd2}\xa3\u0096Y\xd9\x17t\xfb\xd19\x8aGƼ\xf5>\xff\xa6GF\xfb\xeb\xb4\x13\x05\xa8\xba\xdcx\x83.b\x871\xfe=\xde\x0f\xb6\x9c\r\xc7 3\xdb\xcb/L*\x87;\x1c:\x9d\xa7+\xbb\x8d\xf2s\xf6ʚ\x91S+\xb3u\x96\xa1\xb5ۺ(Ƃ\x8cFr\u007f\x8de\x06\x93\xf2D(\x9e\xbd\xd2\xee\xe0\xfeb\x83\x91\x04+\xff:\x9a\xa6\xde\xfaW\x90\xb0c\xfa\x97W\x16E[*\xf7\xcf\xfftֺ\xf9`s\xf1\xc1\a\xee\xe4U?\x9f\x8a\xc6W9\xfc\xb1h\x89֊]|\xe9\xe1\x95,\xef\x0e\x15\xb2\xc37\xb2\xc4\x10\x88\xb7\xc7h\xfdw\x0e|.Pd\xae\x16a\x82\x98\x06\xed\xf4\xfa0\xe6\x0f\x15z\a[Yp\xd7\xf0&M\xa0\xf6y\xb2\x80_*iR\\\x98\xbb\xa6#ц\x13\xc1̦\xf6\xed&,\xe4N\x92\xfd'\x16\xee\x84و\x1d\xae2]\x14\xc857Ӝ\xfc\x1aJ\xca\xc3\x1e}\x9b\xe9di?t\xfbF\t\x0eZ\xdaÉO5]\x05\xd7r<\xb2*\xc5_\xb4\xb9\x82R*\xfa\x87\xe2\aN\xa9\xc4\xc1g92\xfc,\xc6\x02\xde\x0fԧ\xa9\xc4\xe8\x98e\x8cb6\xe5\xf8\x8e\x9f\xbe\xaf\xe03\x9e\xfai\xfe@\x1dsN\x1a\x8e=HE]\xeeՃ\xd1;\x8a\xf9G~lT\xe1\xc8o\x0f\xc28)\x8a\xe2\xe8'\x99\x9c}\xe4\x87OHfp\xd2\x17\x1a'k\xc0r\x89\xb2\xa1[\x9b`\x90\xcaK\x02\x1f\x85ot\xedz\x1b\xb4\xdd\xe0#b\x11\xe7\\\xc3g\xed0\xe6\x9fe\x1f&)<\xb4n\x85ۭ6\xce\xe73V+\x90\xdb\xe0[\x8d\xc0%ׄ\xcf\\\xfc\xfbP ]\x9b\xf7k\xa5\x97\xc3&\x83²\xf4:~\xa5\x8a\x8fEE\x96\x91\xeb\x8e\xd7։bDk\xfc\xa2c\x16vbI\xfa0\xff\x8f\x11\xaf\xee\x84\xe0\xf7\xdd\xfe\xcdݔ\xc6V28O9.[\xf1\x1as\xd4n\x02\x173\xa0\x82W#\x9d#-\xd5=\x94\x02Gz\xa9(\xc0j؊\x89\x97H\xe6\xf4%\xffN\xc6\xed~:\x19ڏ\x96\x9a\xceS\x8e@X\x9c&\xb6l\x98\x04\x13\xcb\xf2e\x91\xd2Ʊ\xc4\xcal/Ԏ\x84\xca\xe8z\xb7\x8fr9ao&\xe0\xe65!\x05UQ\xefH\xd4á\x8e\xab\x8d\xea$t\xc21O\xdeAWd/\x93\x98\x86\xb4v|\xa3\xf0:\xbc-\xb2\xda\x1a]\xae\x02/8\xe1u\x15\x12-Fj\x8a&\xdc~\x94\xe4\xd4\xdaK\xfc,\x06U\x85\n\x84\r\xf8$\xd4lγu.\xeb\xe1\x84q\xa91\xcac\xaf\xf3Bx\u0090\xc7\xf1}\fi$_\xbbz;|-\xf2\n\xacT\xf1yD\x9f\xa6\xf2\xa2`)j1ȑ\xff\xe8A\xdbI\xbcы.\xfa\xe8\xff\xba\x81š\xb10w)\x9e\xda\xf3\xa0\xfb\xa0\x00\x80vy\v1xW#\xf4\xf8\x83\xdc\xfa\x13\xc0\x8c\xb0\xfe\xe3\xdf\xfc`\xff\x90\xe4\xb3|\x98uW\xd8\x13i\xfc\x0e\xf8\x84\x95\xc1L\x8c\x861\x00\x0f\x05\x92\x1fa\x11\xfb\x9eЇ\xb3\x1c\xc9\xc3\xdbB\xc2\xf7\x8c\a\xe3˝\xef\x13%\x1d\xde\x16\t~\xb50\xf0}W\xf7*\x8c\x92j\xb7\xb4\xc7\xfe3t\x1b\x89\x87\x02\x84\x91\x88hd\x19M\x8c\xb4\x18\x11u\x02\xa2\x88\xe3ăz\x83 \xe9\x9dB\xa2Q;p\xf2\x91\x15h\xde\xd9\xdba\xa6\xf0\xa5M\xaf\x89,C\x12\xd7\xcf\xc3\x17z//\xf9\x8f\xf8\b/\xff\x99i\xe5ͭ\xbd\x81\xff\xfa\xef\v\b\xf9\xdb\xe7\xf8\xda.}\xfc\xff\x00\x00\x00\xff\xff\xf3|\x1d\xde\xcdX\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 6148f5306b..184963305a 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -325,7 +324,7 @@ type BackupStatus struct { // CsiVolumeSnapshotsStorageTotal is the total storage size of created // snapshots for this backup. // +optional - CsiVolumeSnapshotsStorageTotal resource.Quantity `json:"csiVolumeSnapshotsStorageTotal,omitempty"` + CsiVolumeSnapshotsStorageTotal int64 `json:"csiVolumeSnapshotsStorageTotal,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index b5bf8ae246..1ca4c2c40a 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -21,6 +21,7 @@ import ( "sort" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index a2bcfc0d4d..f20e9f3c84 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -640,7 +640,12 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { for _, vs := range backup.CsiSnapshots { if *vs.Status.ReadyToUse { backup.Status.CsiVolumeSnapshotsCompleted++ - backup.Status.CsiVolumeSnapshotsStorageTotal.Add(*vs.Status.RestoreSize) + storageSize, ret := vs.Status.RestoreSize.AsInt64() + if !ret { + backupLog.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + backup.Status.CsiVolumeSnapshotsStorageTotal += storageSize } } @@ -707,12 +712,7 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) - storageSize, ret := backup.Status.CsiVolumeSnapshotsStorageTotal.AsInt64() - if !ret { - log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, storageSize) + serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsStorageTotal) } if backup.Status.Progress != nil { diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 0a9383456b..c2d85a105c 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -419,18 +419,6 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[volumeSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } - if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } } // InitSchedule initializes counter metrics for a node. From 4d0005640d6597d93853a59379a6d8306483c3b3 Mon Sep 17 00:00:00 2001 From: yuvalman Date: Mon, 11 Apr 2022 16:43:16 +0300 Subject: [PATCH 095/366] fix: use the right kind in getRestartableProcess logger Signed-off-by: Yuval Manor --- pkg/plugin/clientmgmt/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index b1a952ac5c..9dc2ff3274 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -107,7 +107,7 @@ func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) defer m.lock.Unlock() logger := m.logger.WithFields(logrus.Fields{ - "kind": framework.PluginKindObjectStore.String(), + "kind": kind.String(), "name": name, }) logger.Debug("looking for plugin in registry") From 74db20993bfd0ab055bf7719340145c0248fe778 Mon Sep 17 00:00:00 2001 From: I538157 Date: Fri, 8 Apr 2022 10:05:17 +0300 Subject: [PATCH 096/366] fix: delete empty backups Signed-off-by: Yuval Manor --- changelogs/unreleased/4817-yuvalman | 1 + internal/delete/delete_item_action_handler.go | 7 +++-- .../delete/delete_item_action_handler_test.go | 13 ++++++++- pkg/archive/parser.go | 20 +++++-------- pkg/archive/parser_test.go | 16 +++++------ pkg/restore/restore_test.go | 28 +++++++++++++++++-- 6 files changed, 57 insertions(+), 28 deletions(-) create mode 100644 changelogs/unreleased/4817-yuvalman diff --git a/changelogs/unreleased/4817-yuvalman b/changelogs/unreleased/4817-yuvalman new file mode 100644 index 0000000000..fcacccaa06 --- /dev/null +++ b/changelogs/unreleased/4817-yuvalman @@ -0,0 +1 @@ +fix: delete empty backups diff --git a/internal/delete/delete_item_action_handler.go b/internal/delete/delete_item_action_handler.go index 609f759141..b08c8e024e 100644 --- a/internal/delete/delete_item_action_handler.go +++ b/internal/delete/delete_item_action_handler.go @@ -62,16 +62,17 @@ func InvokeDeleteActions(ctx *Context) error { dir, err := archive.NewExtractor(ctx.Log, ctx.Filesystem).UnzipAndExtractBackup(ctx.BackupReader) if err != nil { return errors.Wrapf(err, "error extracting backup") - } defer ctx.Filesystem.RemoveAll(dir) ctx.Log.Debugf("Downloaded and extracted the backup file to: %s", dir) backupResources, err := archive.NewParser(ctx.Log, ctx.Filesystem).Parse(dir) - if err != nil { + if existErr := errors.Is(err, archive.ErrNotExist); existErr { + ctx.Log.Debug("ignore invoking delete item actions: ", err) + return nil + } else if err != nil { return errors.Wrapf(err, "error parsing backup %q", dir) } - processdResources := sets.NewString() for resource := range backupResources { diff --git a/internal/delete/delete_item_action_handler_test.go b/internal/delete/delete_item_action_handler_test.go index d7e5d153d7..8bd1206020 100644 --- a/internal/delete/delete_item_action_handler_test.go +++ b/internal/delete/delete_item_action_handler_test.go @@ -138,6 +138,17 @@ func TestInvokeDeleteItemActionsRunForCorrectItems(t *testing.T) { new(recordResourcesAction).ForLabelSelector("app=app1"): {"ns-1/pod-1", "ns-2/pvc-2"}, }, }, + { + name: "success if resources dir does not exist", + backup: builder.ForBackup("velero", "velero").Result(), + tarball: test.NewTarWriter(t). + Done(), + apiResources: []*test.APIResource{test.Pods(), test.PVCs()}, + actions: map[*recordResourcesAction][]string{ + new(recordResourcesAction).ForNamespace("ns-1").ForResource("persistentvolumeclaims"): nil, + new(recordResourcesAction).ForNamespace("ns-2").ForResource("pods"): nil, + }, + }, } for _, tc := range tests { @@ -149,7 +160,7 @@ func TestInvokeDeleteItemActionsRunForCorrectItems(t *testing.T) { } // Get the plugins out of the map in order to use them. - actions := []velero.DeleteItemAction{} + var actions []velero.DeleteItemAction for action := range tc.actions { actions = append(actions, action) } diff --git a/pkg/archive/parser.go b/pkg/archive/parser.go index beb0754f1f..166e03114d 100644 --- a/pkg/archive/parser.go +++ b/pkg/archive/parser.go @@ -29,6 +29,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) +var ErrNotExist = errors.New("does not exist") + // Parser traverses an extracted archive on disk to validate // it and provide a helpful representation of it to consumers. type Parser struct { @@ -66,17 +68,9 @@ func (p *Parser) Parse(dir string) (map[string]*ResourceItems, error) { // ensure top-level "resources" directory exists, and read subdirectories // of it, where each one is expected to correspond to a resource. resourcesDir := filepath.Join(dir, velerov1api.ResourcesDir) - exists, err := p.fs.DirExists(resourcesDir) + resourceDirs, err := p.checkAndReadDir(resourcesDir) if err != nil { - return nil, errors.Wrapf(err, "error checking for existence of directory %q", strings.TrimPrefix(resourcesDir, dir+"/")) - } - if !exists { - return nil, errors.Errorf("directory %q does not exist", strings.TrimPrefix(resourcesDir, dir+"/")) - } - - resourceDirs, err := p.fs.ReadDir(resourcesDir) - if err != nil { - return nil, errors.Wrapf(err, "error reading contents of directory %q", strings.TrimPrefix(resourcesDir, dir+"/")) + return nil, err } // loop through each subdirectory (one per resource) and assemble @@ -173,15 +167,15 @@ func (p *Parser) getResourceItemsForScope(dir, archiveRootDir string) ([]string, func (p *Parser) checkAndReadDir(dir string) ([]os.FileInfo, error) { exists, err := p.fs.DirExists(dir) if err != nil { - return []os.FileInfo{}, errors.Wrapf(err, "finding %q", dir) + return nil, errors.Wrapf(err, "error checking for existence of directory %q", filepath.ToSlash(dir)) } if !exists { - return []os.FileInfo{}, errors.Errorf("%q not found", dir) + return nil, errors.Wrapf(ErrNotExist, "directory %q", filepath.ToSlash(dir)) } contents, err := p.fs.ReadDir(dir) if err != nil { - return []os.FileInfo{}, errors.Wrapf(err, "reading contents of %q", dir) + return nil, errors.Wrapf(err, "reading contents of %q", filepath.ToSlash(dir)) } return contents, nil diff --git a/pkg/archive/parser_test.go b/pkg/archive/parser_test.go index 49a2c327df..4a4049804b 100644 --- a/pkg/archive/parser_test.go +++ b/pkg/archive/parser_test.go @@ -32,13 +32,13 @@ func TestParse(t *testing.T) { name string files []string dir string - wantErrMsg string + wantErrMsg error want map[string]*ResourceItems }{ { name: "when there is no top-level resources directory, an error is returned", dir: "root-dir", - wantErrMsg: "directory \"resources\" does not exist", + wantErrMsg: ErrNotExist, }, { name: "when there are no directories under the resources directory, an empty map is returned", @@ -109,8 +109,8 @@ func TestParse(t *testing.T) { } res, err := p.Parse(tc.dir) - if tc.wantErrMsg != "" { - assert.EqualError(t, err, tc.wantErrMsg) + if tc.wantErrMsg != nil { + assert.ErrorIs(t, err, tc.wantErrMsg, "Error should be: %v, got: %v", tc.wantErrMsg, err) } else { assert.Nil(t, err) assert.Equal(t, tc.want, res) @@ -124,13 +124,13 @@ func TestParseGroupVersions(t *testing.T) { name string files []string backupDir string - wantErrMsg string + wantErrMsg error want map[string]metav1.APIGroup }{ { name: "when there is no top-level resources directory, an error is returned", backupDir: "/var/folders", - wantErrMsg: "\"/var/folders/resources\" not found", + wantErrMsg: ErrNotExist, }, { name: "when there are no directories under the resources directory, an empty map is returned", @@ -223,8 +223,8 @@ func TestParseGroupVersions(t *testing.T) { } res, err := p.ParseGroupVersions(tc.backupDir) - if tc.wantErrMsg != "" { - assert.EqualError(t, err, tc.wantErrMsg) + if tc.wantErrMsg != nil { + assert.ErrorIs(t, err, tc.wantErrMsg, "Error should be: %v, got: %v", tc.wantErrMsg, err) } else { assert.Nil(t, err) assert.Equal(t, tc.want, res) diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index a81676085c..b31c87f7ca 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -740,7 +740,7 @@ func TestInvalidTarballContents(t *testing.T) { tarball: test.NewTarWriter(t). Done(), wantErrs: Result{ - Velero: []string{"error parsing backup contents: directory \"resources\" does not exist"}, + Velero: []string{archive.ErrNotExist.Error()}, }, }, { @@ -761,7 +761,7 @@ func TestInvalidTarballContents(t *testing.T) { }, wantErrs: Result{ Namespaces: map[string][]string{ - "ns-1": {"error decoding \"resources/pods/namespaces/ns-1/pod-1.json\": invalid character 'i' looking for beginning of value"}, + "ns-1": {"error decoding"}, }, }, }, @@ -792,12 +792,34 @@ func TestInvalidTarballContents(t *testing.T) { ) assertEmptyResults(t, warnings) - assert.Equal(t, tc.wantErrs, errs) + assertWantErrs(t, tc.wantErrs, errs) assertAPIContents(t, h, tc.want) }) } } +func assertWantErrs(t *testing.T, wantErrRes Result, errRes Result) { + t.Helper() + if wantErrRes.Velero != nil { + assert.Equal(t, len(wantErrRes.Velero), len(errRes.Velero)) + for i := range errRes.Velero { + assert.Contains(t, errRes.Velero[i], wantErrRes.Velero[i]) + } + } + if wantErrRes.Namespaces != nil { + assert.Equal(t, len(wantErrRes.Namespaces), len(errRes.Namespaces)) + for ns := range errRes.Namespaces { + assert.Equal(t, len(wantErrRes.Namespaces[ns]), len(errRes.Namespaces[ns])) + for i := range errRes.Namespaces[ns] { + assert.Contains(t, errRes.Namespaces[ns][i], wantErrRes.Namespaces[ns][i]) + } + } + } + if wantErrRes.Cluster != nil { + assert.Equal(t, wantErrRes.Cluster, errRes.Cluster) + } +} + // TestRestoreItems runs restores of specific items and validates that they are created // with the expected metadata/spec/status in the API. func TestRestoreItems(t *testing.T) { From c17172f4ef204f1bcaa71612bb11558d1bf48440 Mon Sep 17 00:00:00 2001 From: Ming Date: Thu, 14 Apr 2022 15:06:06 +0800 Subject: [PATCH 097/366] Fix default-backup-ttl not work Signed-off-by: Ming --- changelogs/unreleased/4831-qiuming-best | 1 + pkg/cmd/cli/backup/create.go | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 changelogs/unreleased/4831-qiuming-best diff --git a/changelogs/unreleased/4831-qiuming-best b/changelogs/unreleased/4831-qiuming-best new file mode 100644 index 0000000000..3e941627d7 --- /dev/null +++ b/changelogs/unreleased/4831-qiuming-best @@ -0,0 +1 @@ +Fix default-backup-ttl not work diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index f2d165df5e..6190d7f9a1 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -41,8 +41,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/collections" ) -const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour - func NewCreateCommand(f client.Factory, use string) *cobra.Command { o := NewCreateOptions() @@ -106,7 +104,6 @@ type CreateOptions struct { func NewCreateOptions() *CreateOptions { return &CreateOptions{ - TTL: DefaultBackupTTL, IncludeNamespaces: flag.NewStringArray("*"), Labels: flag.NewMap(), SnapshotVolumes: flag.NewOptionalBool(nil), From 9aa4e9e8602d96bf21d3d637b3052d6290e09574 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 04:45:06 +0000 Subject: [PATCH 098/366] Update according to comments. Remove csiVolumeSnapshotsStorageTotal related code. Signed-off-by: Xun Jiang --- config/crd/v1/bases/velero.io_backups.yaml | 9 ++--- config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup.go | 13 +++----- pkg/backup/request.go | 2 +- pkg/controller/backup_controller.go | 19 ++++------- pkg/controller/backup_deletion_controller.go | 21 ------------ pkg/metrics/metrics.go | 35 ++++---------------- 7 files changed, 20 insertions(+), 81 deletions(-) diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index 5cbdd76d37..259e3cd4fe 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -355,18 +355,13 @@ spec: nullable: true type: string csiVolumeSnapshotsAttempted: - description: CsiVolumeSnapshotsAttempted is the total number of attempted + description: CSIVolumeSnapshotsAttempted is the total number of attempted CSI VolumeSnapshots for this backup. type: integer csiVolumeSnapshotsCompleted: - description: CsiVolumeSnapshotsCompleted is the total number of successfully + description: CSIVolumeSnapshotsCompleted is the total number of successfully completed CSI VolumeSnapshots for this backup. type: integer - csiVolumeSnapshotsStorageTotal: - description: CsiVolumeSnapshotsStorageTotal is the total storage size - of created snapshots for this backup. - format: int64 - type: integer errors: description: Errors is a count of all error messages that were generated during execution of the backup. The actual errors are in the backup's diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index da0f366b21..3819b66a8f 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x82$\xf0۬Nj\x18\xbb\x993֎\xf3\x10\xe4\x81\xea.I\xa3\xb1R\xab\x1b\x10\x95\xc4/\x0e\x15\xfde\xd7/\xffj\xd7R_\x1f\xbe\xbbx\x91*\xbf\x81\xdb\xda:]\xfe\x8cV\xd7&\xc3O\xb8\x95J:\xa9\xd5E\x89N\xe4\u0089\x9b\v\x00\xa1\x94v\x82>[\xfa\x13 \xd3\xca\x19]\x14hV;T\xeb\x97z\x83\x9bZ\x169\x1a\x06\x1e\xa7>\xfci\xfd/\xeb?]\x00d\x06y\xf8\x93,\xd1:QV7\xa0ꢸ\x00P\xa2\xc4\x1b؈쥮\xec\xfa\x80\x05\x1a\xbd\x96\xfa\xc2V\x98\xd1\\;\xa3\xeb\xea\x06\xda\x1f\xfc\x90\x80\x87_\xc3\xf7<\x9a?\x14Һ\x1f;\x1f\u007f\x92\xd6\xf1\x0fUQ\x1bQ43\xf17+ծ.\x84\x89_/\x00l\xa6+\xbc\x81\xcf4E%2\xcc/\x00\xc2rx\xcaU@\xf8𝇐\xed\xb1\x14\x1e\x17\x00]\xa1\xfa\xf8p\xff\xfc\x8f\x8f\xbd\xcf\x009\xda\xcc\xc8\xca1Q\xa1\xc1`uw'\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:w\x8f\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŧ\xc0\x1bp\xa6\xc6\t\xca\bc\xc4q\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc)F\xf0[&\xca^\xeb\x97%B\xfc\x1b\xf5i\xf5\x1ed\xec%\xc1\x06\xf7\xe2 \xb5\tK\x0ffh\x83\x80_0\xab\x1d\x8eɿp\x90\xcb\xed\x16\r\xc1\xa9\xf6¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xc9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf0:]\x0f\xcc1\xf9\x04g\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cv\x1dZ\xb0\xb6M-{#H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x9a\x04\xddp\xc4\xfb\x12\x85\xd8`\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xeb^f{o\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5qj\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\uf4f0Q\xed\xbfAh\xefO\x86\xbe\xaf\xd0\x12I%\xb9\xf3\xf7[\xc0\xb2r\xc7+\x90.~]\x82H\xceJ;\xff\xef\x981\xe7K\xfc\xfdp\xe4\xbbJ\xfc,W\x96 \x12W\x9a\xe9\u007f\x87Lac\xf1\x18lE2C~ꎺ\x02\xb9m\x18\x92_\xc1V\x16\x0è3\xbfh\xbf\xbc\a1R\xec\x1d\xb5R\xb8l\u007f\xf7\x85~\xd2h?k\xc7_\xbe\n9=\xe2o \xa6\x1f\xc8\xdbKy\xb5Mt\xe8\xe6\xd0\x12\x84۷{\x1f\xe15\xec\x91\x16\xee\x15\xc5-\x81\x1e\x9c\x11\xf5\xd3\xcdۇ~+k\xcbI2\xa5ՊM\xe5zl&O\xecD\x90\xda\xf48r\x8aZ3\xa9\x9f0\x11\xec\x13Y\x12?\xde\xe7x\v\x91a\x0ey\xcd\xc4\xe4̤p\xb8\x93\x19\x94hvs\x86\xa3\xdb*\xd2\xefi($j]\xdfΔ\xb04\xd3\x1e[P\xdd\xf922+ڹ\t\xbd\"\xb3\x17\xbbN$$\xa7\xbb.\xaf\x88M,\xfb\x1f\x8b\xd4\x15y\xcegI\xa2x8C\xe3\x9f\xc1\x8bS\xdb\xef\x11\xf3\x16\xb2\x14\x9cd\xfc_2s,\xd0\xff\a\x95\x90&a\x0f\u007f䣡\x02{cC\x16\xab;\r\xcd -\x10\u007f\x0f\xa28Mu\x8f,N\x93n\xc1\xc2\x1br\xbd=\xf1X\xae\xe0u\xaf\xad\xb7\xa9[\x89\xa3)\xd5~\x93\x16._\xf0xyu\xa2\a.\xefե7\xf0g\xab\x9b\xc6[Ъ8\xc2%\x8f\xbd\xfc%NP\xa2$&u\xe3#\xb8TW\x99b\xc9\xe8\t\xd0\xc0\xe6܉\xdc\xdc9\xac\x93\xe4\xb0\xd2\xd6%\xa3\xf2\xa0\xad\xf3\x99Ş[zN\x16\v\xbc\f\x85\xec\x15\x88\xad?\xf9\xd3&\x9e\xe9\x90\xda\x1b$\\\x89kv^\xc3\x12\x1b\x9b\x8c\x98\aJ\x81\xd5e\xbb\x83\xbd>\xbd\xf4\a=<\x89\xc8عX\x84[\x19\x9d\xa1\xb5\xf3\"\x92\xa0\xad\x17\x92\x84M\x82P\xf8\x00\xc6\x1f\x98\xcc'%cKwH\x89Hg\xba\xf2w_:\xd9K\xda\xfc\xf4\xf7\x92\xf0\x9d\x8b\x17\xf0\x9e-K1<\x19LB\xf1֏\x8c\xdb$\x00\xf2\xa1\x81\xd9ռ\xd5\xd3=\xc8 H\xbf\x053]Ju\xcf\x13\xc0w\xefn\xd6\x1b%\x89oq\xdco\xe3ؖ\xe8\xcd\a\u07bd\xa9\x1e\x91\xe6̽\xc1\x1e\xe7N\xf3\xdc\xe4(&\x82T\xdau\xd3\t\x04\xb7\xd2\xf9\a\v[i\xac\xeb\"\x9a*\x14\xf5\xc2\xeeo۹\x91\x93\xba3\xe6M\x81ӟ\xfd\xc8N\"k\xaf_\xe3\xf9\xea\xe4a\xe6X\xe3C!\x04\xb9\x05\xe9\x00U\xa6k\xc5\xe9\x17\xda\xea<\x85g\x81W\xd0\xc9$KS\x10\xd4P\xd5e\x1a\x01V,uR\xcd\xe6i\xba\xdd\u007f\x10\xb2\xf8\x1als\xb2D]\xcf\x1aζ\xf5\xd8\xf6\xe4G\xf6\x0e\xcaK\xf1E\x96u\t\xa2$ҧ\x86=[_\x1c\xd3\xe38\xbc\n\xe9\xd8r\x10\\6#NӦ\xaa\nt\xa9;r\x83[mx?[\x99cc\x98\x83\x14h\x05\x02\xb6B\x16\xb5IԐg\xd1\xf6\x9cX#(\x8b\xf7\v\"\xd2&_1)\x12\x12\xb1\x89\xce⼶\xaeL\xba\xab\xf8`0\xcd=[JJG\xf7\xac2\x92dI\xbf\xb7\x87\x16DL\xa8\xe37\x17\xed\xa4}s\xd1\x16\xda7\x17m\xb2}sі\xdb7\x17-\xb4o.Zl\xdf\\\xb4o.\xda\\\xb79m\xbd\x84\x91\xaf\xb8\x9f\xf8q\x11\x8b\x84\xe3\xe99\x14g\xe0\x87j\x8a[_}\x9fZay?>j\xa4\xae6\x94\xf5\xaf\xf8F\u0098\x04\xb4E\x17\xad)iJ.i\x83D\xf1\xf6\x05\xc4\vE\x98I\xe5\x94\xe3շ)\x05?Ke>\xfd:Ӧ\xcc&\x16\x9a\xea8\xc9\b\x1d\xe2\xcd\x06r{\xbb5$\xfdz\x1d\xf6s#\xa6\u007f\xf3\x1aԄR\x9c\x85\x02\x9c\xf9\xc2\xdc9z\rB\x8f>\xc1L\xaf`\xf47C\xaf\x85*\x99\xe9ژp\x12\x84N\x1c\xbe[\xf7\u007fq:T\xca\xc0\xabt\xfb\x91\xa5\xbc\xeeQ\xf1\x19\x96\xdau\xcb^\xa3\xbc\x85+&C:\x826\xa0d\xc1䜑\xd6\x1ey\xe1ϕ\x0f\xe1\xceޗ\xf3\xe1GZ-͛+h\xfa\x152\x13*\xfa\xdc#\xa3\xf4B\xe1\xf4\x1a\x99\xf9\xa2\x96s*c\x86u/\x93@\x97\xebaR\"Džڗ7T\xbc$V;\xfe\u20f1\x94\x9a\x967U\xb2,\x16\x04&֯\xf4+S\xe6A\x9eQ\xb5\x92D\x9c\xe5\n\x95\xb3\xebRB\x1d\xc8\xec:\x92\xabQF\xeaLf\x01O֠\xccU\x97,d\xa5N+O\xd2kJfAs\xbd\xc9r%\xc9\xfbՋ\xbe\x87\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=Ω\xf2X\xa4\xd8\x1b+:\x9a\x8a\x8d\x89yϭ\xe3\xe8\xd7iL\x00M\xa9ޘ\xa8Θ\x808[\xb3\x91Z\x931\x01{\xc1\xec\xceJ\xc9̏\xe3\x17!aѾ\x15\xbf\x96D\xbdua\xda\xe4hf=\xf4T4gQ\xecg\xbc\x06s\x0e\xca\xec\xe3\xc5I\xea\xd5\xf5\xfa\xc7X\xae\x9b\x92\xf0\f~\x94*\xf7rB\x82\xde\xf1\x13\xf8\xa20\x17\xc54\xeeJ\xeb\xef\x8d\x03\x1dD\x1a\x16+a\xf8&\xf9\xe6\xe8\xb3\x15v\rw\"\xdb\xf7;\xc2^X\x8aI\xcbQ7\xec\xb2\tӮ\xe3(\xfar\xb9\x06\xf8A7\x91p\xf7\x9a\x95\x95eU\x1c\xa1\xb6\b\x97\xfd!o\x8b9F%\xc0*Qٽ\x8ew`\x17\u008e\xc7~\uf448>ހ\xcd\n]\xe7\r\xf4\t\xe6\tu\x84\x87g\xf6}\xf8\xee`\xd6ޣ\f\xfeM\x8c$\x86\xd7,\xbf\u007f\xff\b\xdf:m\xc4\x0e\u007f\xd2\xfe2\xf2\x12%\xfa\xbd{7у\x0e\x8b\x19\xb7X\x90%F\x88\x10\xaeE\x0f\x80\xb5\x89\xf4\xb0\x1b\xda\xe4\aa9\xa6\xdef\xf6\x9fs\xc5\xc2b\x9e\x9e~\xf2\vp\xb2\xc4\xf5\xa7\xdagSV\x950\x16\x89\x9aqa~І\xfe\xbbׯc\nO\x875\u007f?\xc4\xdb '\xeb9is\x16\xf6\xfe\xdat\x14\xbcH\xa2%A}\x1e\x1f\xd5\t\xf4:L\xf2\xbb\\\x8f\x9dKL\xc1\xe9\xbc.A\x81\xb5/\xb6{\u07fb\xbfS\x1e\xcb\xd4\xfd{'\\m\x97o\xe0s\xb7\xf8\xdeF8\xf2\xa9\r_\xdc\xf5 \xfcE\xd77]\xc2\x0f\x19\xea\xde\x1b(\xf3|\xba=\x1d\xc1/]\x98ܣƹ\xf1\xe66\xfd\xab\xb0M\x16|\xd4ķ\xe0\xfcH\xf6\xa0\t\x1a\xe6\x80\aT\xa0\x15'\xbd\xf9J\xac\u007f\x8de8f,\x99ԁ\x12\xb2\xeauUh\x91\xc7\x1d\x1emVx\xc1\xe3\x89\xf5\x979\xa0\xf9`g`\xf2\x8b\x01[mƈp\xaa0\xbda\xb9\x81\\8\\\x8d\x02M\xd2}\xa3\u0096Y\xd9\x17t\xfb\xd19\x8aGƼ\xf5>\xff\xa6GF\xfb\xeb\xb4\x13\x05\xa8\xba\xdcx\x83.b\x871\xfe=\xde\x0f\xb6\x9c\r\xc7 3\xdb\xcb/L*\x87;\x1c:\x9d\xa7+\xbb\x8d\xf2s\xf6ʚ\x91S+\xb3u\x96\xa1\xb5ۺ(Ƃ\x8cFr\u007f\x8de\x06\x93\xf2D(\x9e\xbd\xd2\xee\xe0\xfeb\x83\x91\x04+\xff:\x9a\xa6\xde\xfaW\x90\xb0c\xfa\x97W\x16E[*\xf7\xcf\xfftֺ\xf9`s\xf1\xc1\a\xee\xe4U?\x9f\x8a\xc6W9\xfc\xb1h\x89֊]|\xe9\xe1\x95,\xef\x0e\x15\xb2\xc37\xb2\xc4\x10\x88\xb7\xc7h\xfdw\x0e|.Pd\xae\x16a\x82\x98\x06\xed\xf4\xfa0\xe6\x0f\x15z\a[Yp\xd7\xf0&M\xa0\xf6y\xb2\x80_*iR\\\x98\xbb\xa6#ц\x13\xc1̦\xf6\xed&,\xe4N\x92\xfd'\x16\xee\x84و\x1d\xae2]\x14\xc857Ӝ\xfc\x1aJ\xca\xc3\x1e}\x9b\xe9di?t\xfbF\t\x0eZ\xdaÉO5]\x05\xd7r<\xb2*\xc5_\xb4\xb9\x82R*\xfa\x87\xe2\aN\xa9\xc4\xc1g92\xfc,\xc6\x02\xde\x0fԧ\xa9\xc4\xe8\x98e\x8cb6\xe5\xf8\x8e\x9f\xbe\xaf\xe03\x9e\xfai\xfe@\x1dsN\x1a\x8e=HE]\xeeՃ\xd1;\x8a\xf9G~lT\xe1\xc8o\x0f\xc28)\x8a\xe2\xe8'\x99\x9c}\xe4\x87OHfp\xd2\x17\x1a'k\xc0r\x89\xb2\xa1[\x9b`\x90\xcaK\x02\x1f\x85ot\xedz\x1b\xb4\xdd\xe0#b\x11\xe7\\\xc3g\xed0\xe6\x9fe\x1f&)<\xb4n\x85ۭ6\xce\xe73V+\x90\xdb\xe0[\x8d\xc0%ׄ\xcf\\\xfc\xfbP ]\x9b\xf7k\xa5\x97\xc3&\x83²\xf4:~\xa5\x8a\x8fEE\x96\x91\xeb\x8e\xd7։bDk\xfc\xa2c\x16vbI\xfa0\xff\x8f\x11\xaf\xee\x84\xe0\xf7\xdd\xfe\xcdݔ\xc6V28O9.[\xf1\x1as\xd4n\x02\x173\xa0\x82W#\x9d#-\xd5=\x94\x02Gz\xa9(\xc0j؊\x89\x97H\xe6\xf4%\xffN\xc6\xed~:\x19ڏ\x96\x9a\xceS\x8e@X\x9c&\xb6l\x98\x04\x13\xcb\xf2e\x91\xd2Ʊ\xc4\xcal/Ԏ\x84\xca\xe8z\xb7\x8fr9ao&\xe0\xe65!\x05UQ\xefH\xd4á\x8e\xab\x8d\xea$t\xc21O\xdeAWd/\x93\x98\x86\xb4v|\xa3\xf0:\xbc-\xb2\xda\x1a]\xae\x02/8\xe1u\x15\x12-Fj\x8a&\xdc~\x94\xe4\xd4\xdaK\xfc,\x06U\x85\n\x84\r\xf8$\xd4lγu.\xeb\xe1\x84q\xa91\xcac\xaf\xf3Bx\u0090\xc7\xf1}\fi$_\xbbz;|-\xf2\n\xacT\xf1yD\x9f\xa6\xf2\xa2`)j1ȑ\xff\xe8A\xdbI\xbcы.\xfa\xe8\xff\xba\x81š\xb10w)\x9e\xda\xf3\xa0\xfb\xa0\x00\x80vy\v1xW#\xf4\xf8\x83\xdc\xfa\x13\xc0\x8c\xb0\xfe\xe3\xdf\xfc`\xff\x90\xe4\xb3|\x98uW\xd8\x13i\xfc\x0e\xf8\x84\x95\xc1L\x8c\x861\x00\x0f\x05\x92\x1fa\x11\xfb\x9eЇ\xb3\x1c\xc9\xc3\xdbB\xc2\xf7\x8c\a\xe3˝\xef\x13%\x1d\xde\x16\t~\xb50\xf0}W\xf7*\x8c\x92j\xb7\xb4\xc7\xfe3t\x1b\x89\x87\x02\x84\x91\x88hd\x19M\x8c\xb4\x18\x11u\x02\xa2\x88\xe3ăz\x83 \xe9\x9dB\xa2Q;p\xf2\x91\x15h\xde\xd9\xdba\xa6\xf0\xa5M\xaf\x89,C\x12\xd7\xcf\xc3\x17z//\xf9\x8f\xf8\b/\xff\x99i\xe5ͭ\xbd\x81\xff\xfa\xef\v\b\xf9\xdb\xe7\xf8\xda.}\xfc\xff\x00\x00\x00\xff\xff\xf3|\x1d\xde\xcdX\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3zأo\x14\x9d-\xed\xa7n\xdf(\xb6A[y8\xf1ɢ\x9b\xe0b\x8dG\x18\xa5\xf8\x8b67PJE\xff\x90\x1fͩ\x858\xf8\"\x83\xce\xcfC,\xe0\xfd@}\x9a\x8a\x84\x8ey\xc2(fS\x0e\xe0\xf8)\xf4\n\xbe\u0e7f\xe2\x0f\x961\xe7\xe4\xd9\xd8\xc3L\xd4e\xa3\x1e\x8c\xdeS\xec;\xf2c\xa3\x12F~{\x10\xc6IQ\x14'?\xc9\xe4\xec#?|B2\a\x93>\xc18Y\x03\x96K\x94\r\xdd\xda@[*/\t|$\xbcյ\xebm\xd0v\x83\x8f\x88E\x9cs\r_\xb4Ø\x87\x95}\x98\xa4\xd2к\x15\xeev\xda8\x1fׯV w\xc1\xc7\x18\x81K&\x9a\xcf\x1e\xfc;I ]\x9b\xffj\xa5\x97\xc3\a\x83²\xf4:~\xad\x89\x8f\aE\x96\x91\v\x8b\xb7։bDk\xfc\xa6\xe3\x06v\xe6H\xfa0\xff\xb7\x11\xef\xe6\x8c\xe0\x9bn\xff\xe6\x8eFc3\x18\x9c\xa7\x1c\x97ox\x8d9j?\x80\x0f\xf5Q\xc1\xab\x91Α\x96\xea\x1e\u0380#\xbdT\x14`5\xec\xc4ċ\x1cs\xfa\x92\u007f'\x8b\xb6\x99N\n\xf6\xa3\x86\xa6\xf3\x94A\f\x8b\xd3Ė-\x93`bY\xbe\x86t\x8a\xaf\xe1\xbc\x1f\xbe\x9ax\x03V\xaa\xf8L\xa0O\xd7xQ\xb0\xe4\xbd\x1b\xe4\bx\xf4\xc0\xe9\xcc\xef\xeey\xd9}\xf4\xff\xba\x0e\xf6\xb1\xb10\x9fS<\xb5\xe7A\xf7\xc1A8\xed\xf2\x16b\xf0\xaeF\xe8\xf1\a\xb9\xf3'a\x19a\xfdǿ\xf9\x01\xf71\xc9g\xf90뮰'\xd2\xf8\x1d\xf0\t+\x83\x99\x18u\xe7\x01\x1e\n$?\xc2\"\xf6=\xa1\x0f\x179\x92Ƿ\x85F\xef\x19\x17\xc5\x17,\xdf'Z8\xbe-\"\xfaf\xe1\xd0\xfb\xae\xeeU\x18%\xd5~i\x8f\xfd{\xe86\x12\x0f\x05\b#\x11\xd1\xc82\x9a\x18i1\"\xea\x04D\x11lj\x87\xe5\x06A\xd2;\x85D\xa3v\xe0\xec#+м\xb3\xb7\xc3L\xe1K\x9bf\x12Y\x86$\xae_\x86/\xd5^_\xf3\x1f\xf11Z\xfe3\xd3ʛ[{\a\xff\xf1\x9fW\x10\xf2\x98\xcf\xf1\xd5Y\xfa\xf8\u007f\x01\x00\x00\xff\xff\x01\xf3\x19\x8b\xd5W\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 184963305a..15c9eddfc7 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -311,20 +311,15 @@ type BackupStatus struct { // +nullable Progress *BackupProgress `json:"progress,omitempty"` - // CsiVolumeSnapshotsAttempted is the total number of attempted + // CSIVolumeSnapshotsAttempted is the total number of attempted // CSI VolumeSnapshots for this backup. // +optional - CsiVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` + CSIVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` - // CsiVolumeSnapshotsCompleted is the total number of successfully + // CSIVolumeSnapshotsCompleted is the total number of successfully // completed CSI VolumeSnapshots for this backup. // +optional - CsiVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` - - // CsiVolumeSnapshotsStorageTotal is the total storage size of created - // snapshots for this backup. - // +optional - CsiVolumeSnapshotsStorageTotal int64 `json:"csiVolumeSnapshotsStorageTotal,omitempty"` + CSIVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index 1ca4c2c40a..38cd499177 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -50,7 +50,7 @@ type Request struct { VolumeSnapshots []*volume.Snapshot PodVolumeBackups []*velerov1api.PodVolumeBackup BackedUpItems map[itemKey]struct{} - CsiSnapshots []*snapshotv1api.VolumeSnapshot + CSISnapshots []*snapshotv1api.VolumeSnapshot } // BackupResourceList returns the list of backed up resources grouped by the API diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index f20e9f3c84..98982cec8c 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -636,16 +636,10 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } - backup.Status.CsiVolumeSnapshotsAttempted = len(backup.CsiSnapshots) - for _, vs := range backup.CsiSnapshots { + backup.Status.CSIVolumeSnapshotsAttempted = len(backup.CSISnapshots) + for _, vs := range backup.CSISnapshots { if *vs.Status.ReadyToUse { - backup.Status.CsiVolumeSnapshotsCompleted++ - storageSize, ret := vs.Status.RestoreSize.AsInt64() - if !ret { - backupLog.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - backup.Status.CsiVolumeSnapshotsStorageTotal += storageSize + backup.Status.CSIVolumeSnapshotsCompleted++ } } @@ -709,10 +703,9 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterVolumeSnapshotFailures(backupScheduleName, backup.Status.VolumeSnapshotsAttempted-backup.Status.VolumeSnapshotsCompleted) if features.IsEnabled(velerov1api.CSIFeatureFlag) { - serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) - serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) - serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) - serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsStorageTotal) + serverMetrics.RegisterCSISnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsAttempted) + serverMetrics.RegisterCSISnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsCompleted) + serverMetrics.RegisterCSISnapshotFailures(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsAttempted-backup.Status.CSIVolumeSnapshotsCompleted) } if backup.Status.Progress != nil { diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index d4c58553f4..90707be704 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -28,7 +28,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -40,7 +39,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/discovery" - "github.com/vmware-tanzu/velero/pkg/features" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -409,25 +407,6 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR c.metrics.RegisterBackupDeletionFailed(backupScheduleName) } - if features.IsEnabled(velerov1api.CSIFeatureFlag) { - vss, err := backupStore.GetCSIVolumeSnapshots(backup.Name) - if err != nil { - errs = append(errs, err.Error()) - } - - var restoreSizeTotal resource.Quantity - for _, vs := range vss { - restoreSizeTotal.Add(*vs.Status.RestoreSize) - } - - storageSize, ret := restoreSizeTotal.AsInt64() - if !ret { - log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - c.metrics.RegisterCsiStorageSizeSub(backupScheduleName, backup.Name, storageSize) - } - // Update status to processed and record errors req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index c2d85a105c..2e593a0469 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -57,7 +57,6 @@ const ( csiSnapshotAttemptTotal = "csi_snapshot_attempt_total" csiSnapshotSuccessTotal = "csi_snapshot_success_total" csiSnapshotFailureTotal = "csi_snapshot_failure_total" - csiSnapshotStorageTotal = "csi_snapshot_storage_total" // Restic metrics podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" @@ -294,14 +293,6 @@ func NewServerMetrics() *ServerMetrics { }, []string{scheduleLabel, backupNameLabel}, ), - csiSnapshotStorageTotal: prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: metricNamespace, - Name: csiSnapshotStorageTotal, - Help: "Total size of CSI volume snapshots storage size", - }, - []string{scheduleLabel, backupNameLabel}, - ), }, } } @@ -628,37 +619,23 @@ func (m *ServerMetrics) RegisterVolumeSnapshotFailures(backupSchedule string, vo } } -// RegisterCsiSnapshotAttempts records an attempt to snapshot a volume by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { +// RegisterCSISnapshotAttempts records an attempt to snapshot a volume by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsAttempted)) } } -// RegisterCsiSnapshotSuccesses records a completed volume snapshot by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { +// RegisterCSISnapshotSuccesses records a completed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotCompleted)) } } -// RegisterCsiSnapshotFailures records a failed volume snapshot by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { +// RegisterCSISnapshotFailures records a failed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsFailed)) } } - -// RegisterCsiStorageSizeAdd records volume snapshot's storage size increase created by CSI plugin. -func (m *ServerMetrics) RegisterCsiStorageSizeAdd(backupSchedule, backupName string, csiStorageSize int64) { - if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - g.WithLabelValues(backupSchedule, backupName).Add(float64(csiStorageSize)) - } -} - -// RegisterCsiStorageSizeSub records volume snapshot's storage size decrease created by CSI plugin. -func (m *ServerMetrics) RegisterCsiStorageSizeSub(backupSchedule, backupName string, csiStorageSize int64) { - if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - g.WithLabelValues(backupSchedule, backupName).Sub(float64(csiStorageSize)) - } -} From 28bc8b7eef86487996f46ff9e16b179322caa39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 18 Apr 2022 18:06:16 +0800 Subject: [PATCH 099/366] Use controller-gen to generate the deep copy methods for objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are refactoring controllers with kubebuilder, use the controller-gen rather than code-generator to generate the deep copy methods for objects Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4838-ywk253100 | 1 + config/rbac/role.yaml | 14 ++-- hack/update-generated-crd-code.sh | 4 +- hack/verify-generated-crd-code.sh | 2 +- pkg/apis/velero/v1/schedule_types.go | 5 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 81 +-------------------- 6 files changed, 17 insertions(+), 90 deletions(-) create mode 100644 changelogs/unreleased/4838-ywk253100 diff --git a/changelogs/unreleased/4838-ywk253100 b/changelogs/unreleased/4838-ywk253100 new file mode 100644 index 0000000000..70befd62ac --- /dev/null +++ b/changelogs/unreleased/4838-ywk253100 @@ -0,0 +1 @@ +Use controller-gen to generate the deep copy methods for objects \ No newline at end of file diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 1f6a9f12a3..0e95ec2f6a 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -55,7 +55,7 @@ rules: - apiGroups: - velero.io resources: - - schedules + - podvolumebackups verbs: - create - delete @@ -67,7 +67,7 @@ rules: - apiGroups: - velero.io resources: - - schedules/status + - podvolumebackups/status verbs: - get - patch @@ -75,7 +75,7 @@ rules: - apiGroups: - velero.io resources: - - serverstatusrequests + - schedules verbs: - create - delete @@ -87,7 +87,7 @@ rules: - apiGroups: - velero.io resources: - - serverstatusrequests/status + - schedules/status verbs: - get - patch @@ -95,7 +95,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackup + - serverstatusrequests verbs: - create - delete @@ -107,8 +107,8 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackup/status + - serverstatusrequests/status verbs: - get - patch - - update \ No newline at end of file + - update diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index d1c3eb0d53..70b9a942bd 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -51,7 +51,9 @@ controller-gen \ paths=./pkg/apis/velero/v1/... \ rbac:roleName=velero-perms \ paths=./pkg/controller/... \ - output:crd:artifacts:config=config/crd/v1/bases + output:crd:artifacts:config=config/crd/v1/bases \ + object \ + paths=./pkg/apis/velero/v1/... # this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395 # which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked diff --git a/hack/verify-generated-crd-code.sh b/hack/verify-generated-crd-code.sh index ef7490c838..d4c097c799 100755 --- a/hack/verify-generated-crd-code.sh +++ b/hack/verify-generated-crd-code.sh @@ -16,7 +16,7 @@ HACK_DIR=$(dirname "${BASH_SOURCE}") -${HACK_DIR}/update-generated-crd-code.sh --verify-only +${HACK_DIR}/update-generated-crd-code.sh # ensure no changes to generated CRDs if ! git diff --exit-code config/crd/v1/crds/crds.go >/dev/null; then diff --git a/pkg/apis/velero/v1/schedule_types.go b/pkg/apis/velero/v1/schedule_types.go index 52cac8255a..a3987180da 100644 --- a/pkg/apis/velero/v1/schedule_types.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -80,8 +80,8 @@ type ScheduleStatus struct { // TODO(2.0) After converting all resources to use the runtime-controller client, the genclient and k8s:deepcopy markers will no longer be needed and should be removed. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root // +kubebuilder:object:generate=true +// +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Name",type="string",JSONPath=".metadata.name",description="Name of the schedule" @@ -106,7 +106,8 @@ type Schedule struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true // ScheduleList is a list of Schedules. type ScheduleList struct { diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 27e6873367..4c905d4f7e 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1,30 +1,14 @@ //go:build !ignore_autogenerated // +build !ignore_autogenerated -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1 import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -34,7 +18,6 @@ func (in *Backup) DeepCopyInto(out *Backup) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. @@ -65,7 +48,6 @@ func (in *BackupHooks) DeepCopyInto(out *BackupHooks) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupHooks. @@ -90,7 +72,6 @@ func (in *BackupList) DeepCopyInto(out *BackupList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupList. @@ -114,7 +95,6 @@ func (in *BackupList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupProgress) DeepCopyInto(out *BackupProgress) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupProgress. @@ -135,7 +115,6 @@ func (in *BackupResourceHook) DeepCopyInto(out *BackupResourceHook) { *out = new(ExecHook) (*in).DeepCopyInto(*out) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupResourceHook. @@ -190,7 +169,6 @@ func (in *BackupResourceHookSpec) DeepCopyInto(out *BackupResourceHookSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupResourceHookSpec. @@ -261,7 +239,6 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. @@ -299,7 +276,6 @@ func (in *BackupStatus) DeepCopyInto(out *BackupStatus) { *out = new(BackupProgress) **out = **in } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStatus. @@ -319,7 +295,6 @@ func (in *BackupStorageLocation) DeepCopyInto(out *BackupStorageLocation) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocation. @@ -352,7 +327,6 @@ func (in *BackupStorageLocationList) DeepCopyInto(out *BackupStorageLocationList (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationList. @@ -399,7 +373,6 @@ func (in *BackupStorageLocationSpec) DeepCopyInto(out *BackupStorageLocationSpec *out = new(metav1.Duration) **out = **in } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationSpec. @@ -423,7 +396,6 @@ func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationSt in, out := &in.LastValidationTime, &out.LastValidationTime *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. @@ -443,7 +415,6 @@ func (in *DeleteBackupRequest) DeepCopyInto(out *DeleteBackupRequest) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeleteBackupRequest. @@ -476,7 +447,6 @@ func (in *DeleteBackupRequestList) DeepCopyInto(out *DeleteBackupRequestList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeleteBackupRequestList. @@ -500,7 +470,6 @@ func (in *DeleteBackupRequestList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeleteBackupRequestSpec) DeepCopyInto(out *DeleteBackupRequestSpec) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeleteBackupRequestSpec. @@ -521,7 +490,6 @@ func (in *DeleteBackupRequestStatus) DeepCopyInto(out *DeleteBackupRequestStatus *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeleteBackupRequestStatus. @@ -541,7 +509,6 @@ func (in *DownloadRequest) DeepCopyInto(out *DownloadRequest) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DownloadRequest. @@ -574,7 +541,6 @@ func (in *DownloadRequestList) DeepCopyInto(out *DownloadRequestList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DownloadRequestList. @@ -599,7 +565,6 @@ func (in *DownloadRequestList) DeepCopyObject() runtime.Object { func (in *DownloadRequestSpec) DeepCopyInto(out *DownloadRequestSpec) { *out = *in out.Target = in.Target - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DownloadRequestSpec. @@ -619,7 +584,6 @@ func (in *DownloadRequestStatus) DeepCopyInto(out *DownloadRequestStatus) { in, out := &in.Expiration, &out.Expiration *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DownloadRequestStatus. @@ -635,7 +599,6 @@ func (in *DownloadRequestStatus) DeepCopy() *DownloadRequestStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DownloadTarget) DeepCopyInto(out *DownloadTarget) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DownloadTarget. @@ -657,7 +620,6 @@ func (in *ExecHook) DeepCopyInto(out *ExecHook) { copy(*out, *in) } out.Timeout = in.Timeout - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExecHook. @@ -680,7 +642,6 @@ func (in *ExecRestoreHook) DeepCopyInto(out *ExecRestoreHook) { } out.ExecTimeout = in.ExecTimeout out.WaitTimeout = in.WaitTimeout - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExecRestoreHook. @@ -704,7 +665,6 @@ func (in *InitRestoreHook) DeepCopyInto(out *InitRestoreHook) { } } out.Timeout = in.Timeout - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitRestoreHook. @@ -727,7 +687,6 @@ func (in *Metadata) DeepCopyInto(out *Metadata) { (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metadata. @@ -748,7 +707,6 @@ func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) { *out = make([]byte, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageLocation. @@ -764,7 +722,6 @@ func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PluginInfo) DeepCopyInto(out *PluginInfo) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginInfo. @@ -784,7 +741,6 @@ func (in *PodVolumeBackup) DeepCopyInto(out *PodVolumeBackup) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackup. @@ -817,7 +773,6 @@ func (in *PodVolumeBackupList) DeepCopyInto(out *PodVolumeBackupList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackupList. @@ -849,7 +804,6 @@ func (in *PodVolumeBackupSpec) DeepCopyInto(out *PodVolumeBackupSpec) { (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackupSpec. @@ -874,7 +828,6 @@ func (in *PodVolumeBackupStatus) DeepCopyInto(out *PodVolumeBackupStatus) { *out = (*in).DeepCopy() } out.Progress = in.Progress - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackupStatus. @@ -890,7 +843,6 @@ func (in *PodVolumeBackupStatus) DeepCopy() *PodVolumeBackupStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodVolumeOperationProgress) DeepCopyInto(out *PodVolumeOperationProgress) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeOperationProgress. @@ -910,7 +862,6 @@ func (in *PodVolumeRestore) DeepCopyInto(out *PodVolumeRestore) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeRestore. @@ -943,7 +894,6 @@ func (in *PodVolumeRestoreList) DeepCopyInto(out *PodVolumeRestoreList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeRestoreList. @@ -968,7 +918,6 @@ func (in *PodVolumeRestoreList) DeepCopyObject() runtime.Object { func (in *PodVolumeRestoreSpec) DeepCopyInto(out *PodVolumeRestoreSpec) { *out = *in out.Pod = in.Pod - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeRestoreSpec. @@ -993,7 +942,6 @@ func (in *PodVolumeRestoreStatus) DeepCopyInto(out *PodVolumeRestoreStatus) { *out = (*in).DeepCopy() } out.Progress = in.Progress - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeRestoreStatus. @@ -1013,7 +961,6 @@ func (in *ResticRepository) DeepCopyInto(out *ResticRepository) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepository. @@ -1046,7 +993,6 @@ func (in *ResticRepositoryList) DeepCopyInto(out *ResticRepositoryList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryList. @@ -1071,7 +1017,6 @@ func (in *ResticRepositoryList) DeepCopyObject() runtime.Object { func (in *ResticRepositorySpec) DeepCopyInto(out *ResticRepositorySpec) { *out = *in out.MaintenanceFrequency = in.MaintenanceFrequency - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositorySpec. @@ -1091,7 +1036,6 @@ func (in *ResticRepositoryStatus) DeepCopyInto(out *ResticRepositoryStatus) { in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryStatus. @@ -1111,7 +1055,6 @@ func (in *Restore) DeepCopyInto(out *Restore) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Restore. @@ -1142,7 +1085,6 @@ func (in *RestoreHooks) DeepCopyInto(out *RestoreHooks) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreHooks. @@ -1167,7 +1109,6 @@ func (in *RestoreList) DeepCopyInto(out *RestoreList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreList. @@ -1191,7 +1132,6 @@ func (in *RestoreList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RestoreProgress) DeepCopyInto(out *RestoreProgress) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreProgress. @@ -1217,7 +1157,6 @@ func (in *RestoreResourceHook) DeepCopyInto(out *RestoreResourceHook) { *out = new(InitRestoreHook) (*in).DeepCopyInto(*out) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreResourceHook. @@ -1265,7 +1204,6 @@ func (in *RestoreResourceHookSpec) DeepCopyInto(out *RestoreResourceHookSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreResourceHookSpec. @@ -1329,7 +1267,6 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { **out = **in } in.Hooks.DeepCopyInto(&out.Hooks) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSpec. @@ -1363,7 +1300,6 @@ func (in *RestoreStatus) DeepCopyInto(out *RestoreStatus) { *out = new(RestoreProgress) **out = **in } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreStatus. @@ -1383,7 +1319,6 @@ func (in *Schedule) DeepCopyInto(out *Schedule) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Schedule. @@ -1416,7 +1351,6 @@ func (in *ScheduleList) DeepCopyInto(out *ScheduleList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleList. @@ -1446,7 +1380,6 @@ func (in *ScheduleSpec) DeepCopyInto(out *ScheduleSpec) { *out = new(bool) **out = **in } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleSpec. @@ -1471,7 +1404,6 @@ func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) { *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleStatus. @@ -1491,7 +1423,6 @@ func (in *ServerStatusRequest) DeepCopyInto(out *ServerStatusRequest) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequest. @@ -1524,7 +1455,6 @@ func (in *ServerStatusRequestList) DeepCopyInto(out *ServerStatusRequestList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestList. @@ -1548,7 +1478,6 @@ func (in *ServerStatusRequestList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerStatusRequestSpec) DeepCopyInto(out *ServerStatusRequestSpec) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestSpec. @@ -1573,7 +1502,6 @@ func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus *out = make([]PluginInfo, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestStatus. @@ -1594,7 +1522,6 @@ func (in *StorageType) DeepCopyInto(out *StorageType) { *out = new(ObjectStorageLocation) (*in).DeepCopyInto(*out) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageType. @@ -1614,7 +1541,6 @@ func (in *VolumeSnapshotLocation) DeepCopyInto(out *VolumeSnapshotLocation) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocation. @@ -1647,7 +1573,6 @@ func (in *VolumeSnapshotLocationList) DeepCopyInto(out *VolumeSnapshotLocationLi (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationList. @@ -1678,7 +1603,6 @@ func (in *VolumeSnapshotLocationSpec) DeepCopyInto(out *VolumeSnapshotLocationSp (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationSpec. @@ -1694,7 +1618,6 @@ func (in *VolumeSnapshotLocationSpec) DeepCopy() *VolumeSnapshotLocationSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeSnapshotLocationStatus) DeepCopyInto(out *VolumeSnapshotLocationStatus) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationStatus. From 8064421e83314c564aed4f804d3295f4ab68ee3f Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 11 Apr 2022 20:49:20 +0800 Subject: [PATCH 100/366] Update integrated Restic version and add insecureSkipTLSVerify for Restic CLI 1. Add --insecure-tls for ResticManager's commands. 2. Add --insecure-tls in PodVolumeBackup and PodVolumeRestore controller. 3. Upgrade integrated Restic version to v0.13.1 4. Change --last flag in Restic command to --latest=1 due to Restic version update. Signed-off-by: Xun Jiang --- Makefile | 2 +- changelogs/unreleased/4839-jxun | 1 + .../pod_volume_backup_controller.go | 25 +++- .../pod_volume_restore_controller.go | 8 ++ pkg/restic/command_factory.go | 3 +- pkg/restic/command_factory_test.go | 9 +- pkg/restic/exec_commands.go | 19 ++- pkg/restic/repository_manager.go | 43 ++++++- pkg/restic/repository_manager_test.go | 121 ++++++++++++++++++ 9 files changed, 220 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/4839-jxun create mode 100644 pkg/restic/repository_manager_test.go diff --git a/Makefile b/Makefile index c17e241476..df82b92ef4 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ see: https://velero.io/docs/main/build-from-source/#making-images-and-updating-v endef # The version of restic binary to be downloaded -RESTIC_VERSION ?= 0.12.1 +RESTIC_VERSION ?= 0.13.1 CLI_PLATFORMS ?= linux-amd64 linux-arm linux-arm64 darwin-amd64 darwin-arm64 windows-amd64 linux-ppc64le BUILDX_PLATFORMS ?= $(subst -,/,$(ARCH)) diff --git a/changelogs/unreleased/4839-jxun b/changelogs/unreleased/4839-jxun new file mode 100644 index 0000000000..d3ee746d82 --- /dev/null +++ b/changelogs/unreleased/4839-jxun @@ -0,0 +1 @@ +Update integrated Restic version and add insecureSkipTLSVerify for Restic CLI. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 609a7d7123..31fae9f3df 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -139,8 +139,26 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ } defer os.Remove(resticDetails.credsFile) + backupLocation := &velerov1api.BackupStorageLocation{} + if err := r.Client.Get(context.Background(), client.ObjectKey{ + Namespace: pvb.Namespace, + Name: pvb.Spec.BackupStorageLocation, + }, backupLocation); err != nil { + return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") + } + + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(backupLocation, log) + if len(skipTLSRet) > 0 { + resticCmd.ExtraFlags = append(resticCmd.ExtraFlags, skipTLSRet) + } + + var stdout, stderr string + var emptySnapshot bool - stdout, stderr, err := r.ResticExec.RunBackup(resticCmd, log, r.updateBackupProgressFunc(&pvb, log)) + stdout, stderr, err = r.ResticExec.RunBackup(resticCmd, log, r.updateBackupProgressFunc(&pvb, log)) if err != nil { if strings.Contains(stderr, "snapshot is empty") { emptySnapshot = true @@ -156,6 +174,11 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ cmd.Env = resticDetails.envs cmd.CACertFile = resticDetails.caCertFile + // #4820: also apply the insecureTLS flag to Restic snapshots command + if len(skipTLSRet) > 0 { + cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet) + } + snapshotID, err = r.ResticExec.GetSnapshotID(cmd) if err != nil { return r.updateStatusToFailed(ctx, &pvb, err, "getting snapshot id") diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 841f02019d..299af6fb95 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -385,6 +385,14 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume } resticCmd.Env = env + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(backupLocation, log) + if len(skipTLSRet) > 0 { + resticCmd.ExtraFlags = append(resticCmd.ExtraFlags, skipTLSRet) + } + var stdout, stderr string if stdout, stderr, err = restic.RunRestore(resticCmd, log, c.updateRestoreProgressFunc(req, log)); err != nil { diff --git a/pkg/restic/command_factory.go b/pkg/restic/command_factory.go index 16625cd3f0..98bb6ffef2 100644 --- a/pkg/restic/command_factory.go +++ b/pkg/restic/command_factory.go @@ -64,7 +64,8 @@ func GetSnapshotCommand(repoIdentifier, passwordFile string, tags map[string]str Command: "snapshots", RepoIdentifier: repoIdentifier, PasswordFile: passwordFile, - ExtraFlags: []string{"--json", "--last", getSnapshotTagFlag(tags)}, + // "--last" is replaced by "--latest=1" in restic v0.12.1 + ExtraFlags: []string{"--json", "--latest=1", getSnapshotTagFlag(tags)}, } } diff --git a/pkg/restic/command_factory_test.go b/pkg/restic/command_factory_test.go index e8145a28f6..4a38bc1901 100644 --- a/pkg/restic/command_factory_test.go +++ b/pkg/restic/command_factory_test.go @@ -58,7 +58,7 @@ func TestGetSnapshotCommand(t *testing.T) { assert.Equal(t, "password-file", c.PasswordFile) // set up expected flag names - expectedFlags := []string{"--json", "--last", "--tag"} + expectedFlags := []string{"--json", "--latest=1", "--tag"} // for tracking actual flag names actualFlags := []string{} // for tracking actual --tag values as a map @@ -68,10 +68,11 @@ func TestGetSnapshotCommand(t *testing.T) { for _, flag := range c.ExtraFlags { // split into 2 parts from the first = sign (if any) parts := strings.SplitN(flag, "=", 2) - // parts[0] is the flag name - actualFlags = append(actualFlags, parts[0]) + // convert --tag data to a map if parts[0] == "--tag" { + actualFlags = append(actualFlags, parts[0]) + // split based on , tags := strings.Split(parts[1], ",") // loop through each key-value tag pair @@ -81,6 +82,8 @@ func TestGetSnapshotCommand(t *testing.T) { // record actual key & value actualTags[kvs[0]] = kvs[1] } + } else { + actualFlags = append(actualFlags, flag) } } diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index 648dc68cc7..a0d16d8d58 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "strings" "time" "github.com/pkg/errors" @@ -184,7 +185,15 @@ func getSummaryLine(b []byte) ([]byte, error) { // RunRestore runs a `restic restore` command and monitors the volume size to // provide progress updates to the caller. func RunRestore(restoreCmd *Command, log logrus.FieldLogger, updateFunc func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { - snapshotSize, err := getSnapshotSize(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env) + insecureTLSFlag := "" + + for _, extraFlag := range restoreCmd.ExtraFlags { + if strings.Contains(extraFlag, resticInsecureTLSFlag) { + insecureTLSFlag = extraFlag + } + } + + snapshotSize, err := getSnapshotSize(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env, insecureTLSFlag) if err != nil { return "", "", errors.Wrap(err, "error getting snapshot size") } @@ -230,11 +239,15 @@ func RunRestore(restoreCmd *Command, log logrus.FieldLogger, updateFunc func(vel return stdout, stderr, err } -func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string) (int64, error) { +func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { cmd := StatsCommand(repoIdentifier, passwordFile, snapshotID) cmd.Env = env cmd.CACertFile = caCertFile + if len(insecureTLS) > 0 { + cmd.ExtraFlags = append(cmd.ExtraFlags, insecureTLS) + } + stdout, stderr, err := exec.RunCommand(cmd.Cmd()) if err != nil { return 0, errors.Wrapf(err, "error running command, stderr=%s", stderr) @@ -245,7 +258,7 @@ func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string } if err := json.Unmarshal([]byte(stdout), &snapshotStats); err != nil { - return 0, errors.Wrap(err, "error unmarshalling restic stats result") + return 0, errors.Wrapf(err, "error unmarshalling restic stats result, stdout=%s", stdout) } return snapshotStats.TotalSize, nil diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index b456bfc02a..f3ff735f9d 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "strconv" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -95,6 +96,16 @@ type repositoryManager struct { credentialsFileStore credentials.FileStore } +const ( + // insecureSkipTLSVerifyKey is the flag in BackupStorageLocation's config + // to indicate whether to skip TLS verify to setup insecure HTTPS connection. + insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" + + // resticInsecureTLSFlag is the flag for Restic command line to indicate + // skip TLS verify on https connection. + resticInsecureTLSFlag = "--insecure-tls" +) + // NewRepositoryManager constructs a RepositoryManager. func NewRepositoryManager( ctx context.Context, @@ -184,10 +195,11 @@ func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.ResticRepository) e defer rm.repoLocker.Unlock(repo.Name) snapshotsCmd := SnapshotsCommand(repo.Spec.ResticIdentifier) - // use the '--last' flag to minimize the amount of data fetched since + // use the '--latest=1' flag to minimize the amount of data fetched since // we're just validating that the repo exists and can be authenticated // to. - snapshotsCmd.ExtraFlags = append(snapshotsCmd.ExtraFlags, "--last") + // "--last" is replaced by "--latest=1" in restic v0.12.1 + snapshotsCmd.ExtraFlags = append(snapshotsCmd.ExtraFlags, "--latest=1") return rm.exec(snapshotsCmd, repo.Spec.BackupStorageLocation) } @@ -265,6 +277,14 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { } cmd.Env = env + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := GetInsecureSkipTLSVerifyFromBSL(loc, rm.log) + if len(skipTLSRet) > 0 { + cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet) + } + stdout, stderr, err := veleroexec.RunCommand(cmd.Cmd()) rm.log.WithFields(logrus.Fields{ "repository": cmd.RepoName(), @@ -278,3 +298,22 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { return nil } + +// GetInsecureSkipTLSVerifyFromBSL get insecureSkipTLSVerify flag from BSL configuration, +// Then return --insecure-tls flag with boolean value as result. +func GetInsecureSkipTLSVerifyFromBSL(backupLocation *velerov1api.BackupStorageLocation, logger logrus.FieldLogger) string { + result := "" + + if backupLocation == nil { + logger.Info("bsl is nil. return empty.") + return result + } + + if insecure, _ := strconv.ParseBool(backupLocation.Spec.Config[insecureSkipTLSVerifyKey]); insecure { + logger.Debugf("set --insecure-tls=true for Restic command according to BSL %s config", backupLocation.Name) + result = resticInsecureTLSFlag + "=true" + return result + } + + return result +} diff --git a/pkg/restic/repository_manager_test.go b/pkg/restic/repository_manager_test.go new file mode 100644 index 0000000000..79d326bb80 --- /dev/null +++ b/pkg/restic/repository_manager_test.go @@ -0,0 +1,121 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restic + +import ( + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestGetInsecureSkipTLSVerifyFromBSL(t *testing.T) { + log := logrus.StandardLogger() + tests := []struct { + name string + backupLocation *velerov1api.BackupStorageLocation + logger logrus.FieldLogger + expected string + }{ + { + "Test with nil BSL. Should return empty string.", + nil, + log, + "", + }, + { + "Test BSL with no configuration. Should return empty string.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + log, + "", + }, + { + "Test with AWS BSL's insecureSkipTLSVerify set to false.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Config: map[string]string{ + "insecureSkipTLSVerify": "false", + }, + }, + }, + log, + "", + }, + { + "Test with AWS BSL's insecureSkipTLSVerify set to true.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Config: map[string]string{ + "insecureSkipTLSVerify": "true", + }, + }, + }, + log, + "--insecure-tls=true", + }, + { + "Test with Azure BSL's insecureSkipTLSVerify set to invalid.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{ + "insecureSkipTLSVerify": "invalid", + }, + }, + }, + log, + "", + }, + { + "Test with GCP without insecureSkipTLSVerify.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "gcp", + Config: map[string]string{}, + }, + }, + log, + "", + }, + { + "Test with AWS without config.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + }, + }, + log, + "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res := GetInsecureSkipTLSVerifyFromBSL(test.backupLocation, test.logger) + + assert.Equal(t, test.expected, res) + }) + } +} From 82fab3f275174f84a9bf0ab7d0e0d69992f19ef3 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Mon, 18 Apr 2022 16:02:18 -0400 Subject: [PATCH 101/366] Update site/content/docs/main/api-types/restore.md Co-authored-by: JenTing Hsiao Signed-off-by: Tiger Kaovilai --- site/content/docs/main/api-types/restore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/main/api-types/restore.md b/site/content/docs/main/api-types/restore.md index 80f7586b63..9663125f68 100644 --- a/site/content/docs/main/api-types/restore.md +++ b/site/content/docs/main/api-types/restore.md @@ -88,7 +88,7 @@ spec: # Array of namespaces to which this hook does not apply. Optional. excludedNamespaces: - ns3 - # Array of resources to which this hook applies. If empty, it applies to all supported resources. + # Array of resources to which this hook applies. If unspecified, the hook applies to all resources in the backup. Optional. # The only resource supported at this time is pods. includedResources: - pods From fb48c5a301b72801cb21ca4874cbba41c5d9017b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 19 Apr 2022 09:23:40 +0800 Subject: [PATCH 102/366] Return the error when running reconcile to trigger the handling logic again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return the error when running reconcile to trigger the handling logic again Signed-off-by: Wenkai Yin(尹文开) --- pkg/controller/schedule_controller.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index 42f2c6d608..92207b07a6 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" "github.com/robfig/cron" "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" "sigs.k8s.io/cluster-api/util/patch" @@ -82,8 +83,11 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c log.Debug("Getting schedule") schedule := &velerov1.Schedule{} if err := c.Get(ctx, req.NamespacedName, schedule); err != nil { - log.WithError(err).Error("error getting schedule") - return ctrl.Result{}, nil + if apierrors.IsNotFound(err) { + log.WithError(err).Error("schedule not found") + return ctrl.Result{}, nil + } + return ctrl.Result{}, errors.Wrapf(err, "error getting schedule %s", req.String()) } if schedule.Status.Phase != "" && @@ -97,8 +101,7 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c patchHelper, err := patch.NewHelper(schedule, c.Client) if err != nil { - log.WithError(err).Error("error new patch helper") - return ctrl.Result{}, nil + return ctrl.Result{}, errors.Wrapf(err, "error new patch helper for schedule %s", req.String()) } // validation - even if the item is Enabled, we can't trust it @@ -116,8 +119,7 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // update status if it's changed if currentPhase != schedule.Status.Phase { if err = patchHelper.Patch(ctx, schedule); err != nil { - log.WithError(err).Errorf("error updating schedule phase to %s", schedule.Status.Phase) - return ctrl.Result{}, nil + return ctrl.Result{}, errors.Wrapf(err, "error updating phase of schedule %s to %s", req.String(), schedule.Status.Phase) } } @@ -128,7 +130,7 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // check for the schedule being due to run, and submit a Backup if so if err := c.submitBackupIfDue(ctx, schedule, cronSchedule); err != nil { - log.WithError(err).Error("error running submitBackupIfDue") + return ctrl.Result{}, errors.Wrapf(err, "error running submitBackupIfDue for schedule %s", req.String()) } return ctrl.Result{}, nil From 4f9e4451422b14a8c337d3ccf7f9402e27948e84 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 31 Mar 2022 18:09:35 +0800 Subject: [PATCH 103/366] Make the vsc created by backup sync controller deletable Fixes #4760 This commit make changes in 2 parts: 1) When a volumesnapshotcontent is persisted during backup, velero will reset its `Source` field to remove the VolumeHandle, so that the csi-snapshotter will not try to call `CreateSnapshot` when its synced to another cluster with a backup. 2) Make sure the referenced volumesnapshotclasses are persisted and synced with the backup, so that when the volumesnapshotcontent is deleted the storage snapshot is also removed. Signed-off-by: Daniel Jiang --- changelogs/unreleased/4832-reasonerjt | 1 + pkg/cmd/server/server.go | 9 +++-- pkg/controller/backup_controller.go | 34 +++++++++++++++++-- pkg/controller/backup_sync_controller.go | 16 +++++++++ pkg/persistence/mocks/backup_store.go | 5 +++ pkg/persistence/object_store.go | 24 +++++++++++++- pkg/persistence/object_store_layout.go | 4 +++ pkg/util/csi/reset.go | 42 ++++++++++++++++++++++++ 8 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/4832-reasonerjt create mode 100644 pkg/util/csi/reset.go diff --git a/changelogs/unreleased/4832-reasonerjt b/changelogs/unreleased/4832-reasonerjt new file mode 100644 index 0000000000..07f9a34994 --- /dev/null +++ b/changelogs/unreleased/4832-reasonerjt @@ -0,0 +1 @@ +Make the vsc created by backup sync controller deletable \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 0a911cae92..fd7e11bcee 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -535,10 +535,11 @@ func (s *server) initRestic() error { return nil } -func (s *server) getCSISnapshotListers() (snapshotv1listers.VolumeSnapshotLister, snapshotv1listers.VolumeSnapshotContentLister) { +func (s *server) getCSISnapshotListers() (snapshotv1listers.VolumeSnapshotLister, snapshotv1listers.VolumeSnapshotContentLister, snapshotv1listers.VolumeSnapshotClassLister) { // Make empty listers that will only be populated if CSI is properly enabled. var vsLister snapshotv1listers.VolumeSnapshotLister var vscLister snapshotv1listers.VolumeSnapshotContentLister + var vsClassLister snapshotv1listers.VolumeSnapshotClassLister var err error // If CSI is enabled, check for the CSI groups and generate the listers @@ -556,11 +557,12 @@ func (s *server) getCSISnapshotListers() (snapshotv1listers.VolumeSnapshotLister // Access the wrapped factory directly here since we've already done the feature flag check above to know it's safe. vsLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshots().Lister() vscLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshotContents().Lister() + vsClassLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshotClasses().Lister() case err != nil: cmd.CheckError(err) } } - return vsLister, vscLister + return vsLister, vscLister, vsClassLister } func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string) error { @@ -587,7 +589,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupStoreGetter := persistence.NewObjectBackupStoreGetter(s.credentialFileStore) - csiVSLister, csiVSCLister := s.getCSISnapshotListers() + csiVSLister, csiVSCLister, csiVSClassLister := s.getCSISnapshotListers() backupSyncControllerRunInfo := func() controllerRunInfo { backupSyncContoller := controller.NewBackupSyncController( @@ -645,6 +647,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.config.formatFlag.Parse(), csiVSLister, csiVSCLister, + csiVSClassLister, backupStoreGetter, ) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 56c744fe83..76d5375bef 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -36,8 +36,11 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" + "github.com/vmware-tanzu/velero/pkg/util/csi" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" @@ -85,6 +88,7 @@ type backupController struct { formatFlag logging.Format volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister + volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister } func NewBackupController( @@ -106,6 +110,7 @@ func NewBackupController( formatFlag logging.Format, volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, + volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister, backupStoreGetter persistence.ObjectBackupStoreGetter, ) Interface { c := &backupController{ @@ -128,6 +133,7 @@ func NewBackupController( formatFlag: formatFlag, volumeSnapshotLister: volumeSnapshotLister, volumeSnapshotContentLister: volumeSnapshotContentLister, + volumeSnapshotClassLister: volumesnapshotClassLister, backupStoreGetter: backupStoreGetter, } @@ -604,9 +610,9 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { // This way, we only make the Lister call if the feature flag's on. var volumeSnapshots []*snapshotv1api.VolumeSnapshot var volumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent + var volumeSnapshotClasses []*snapshotv1api.VolumeSnapshotClass if features.IsEnabled(velerov1api.CSIFeatureFlag) { selector := label.NewSelectorForBackup(backup.Name) - // Listers are wrapped in a nil check out of caution, since they may not be populated based on the // EnableCSI feature flag. This is more to guard against programmer error, as they shouldn't be nil // when EnableCSI is on. @@ -623,6 +629,23 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { backupLog.Error(err) } } + vsClassSet := sets.NewString() + for _, vsc := range volumeSnapshotContents { + // persist the volumesnapshotclasses referenced by vsc + if c.volumeSnapshotClassLister != nil && + vsc.Spec.VolumeSnapshotClassName != nil && + !vsClassSet.Has(*vsc.Spec.VolumeSnapshotClassName) { + if vsClass, err := c.volumeSnapshotClassLister.Get(*vsc.Spec.VolumeSnapshotClassName); err != nil { + backupLog.Error(err) + } else { + vsClassSet.Insert(*vsc.Spec.VolumeSnapshotClassName) + volumeSnapshotClasses = append(volumeSnapshotClasses, vsClass) + } + } + if err := csi.ResetVolumeSnapshotContent(vsc); err != nil { + backupLog.Error(err) + } + } } // Mark completion timestamp before serializing and uploading. @@ -666,7 +689,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { return err } - if errs := persistBackup(backup, backupFile, logFile, backupStore, c.logger.WithField(Backup, kubeutil.NamespaceAndName(backup)), volumeSnapshots, volumeSnapshotContents); len(errs) > 0 { + if errs := persistBackup(backup, backupFile, logFile, backupStore, c.logger.WithField(Backup, kubeutil.NamespaceAndName(backup)), volumeSnapshots, volumeSnapshotContents, volumeSnapshotClasses); len(errs) > 0 { fatalErrs = append(fatalErrs, errs...) } @@ -706,6 +729,7 @@ func persistBackup(backup *pkgbackup.Request, log logrus.FieldLogger, csiVolumeSnapshots []*snapshotv1api.VolumeSnapshot, csiVolumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent, + csiVolumesnapshotClasses []*snapshotv1api.VolumeSnapshotClass, ) []error { persistErrs := []error{} backupJSON := new(bytes.Buffer) @@ -734,6 +758,10 @@ func persistBackup(backup *pkgbackup.Request, if errs != nil { persistErrs = append(persistErrs, errs...) } + csiSnapshotClassesJSON, errs := encodeToJSONGzip(csiVolumesnapshotClasses, "csi volume snapshot classes list") + if errs != nil { + persistErrs = append(persistErrs, errs...) + } backupResourceList, errs := encodeToJSONGzip(backup.BackupResourceList(), "backup resources list") if errs != nil { @@ -748,6 +776,7 @@ func persistBackup(backup *pkgbackup.Request, backupResourceList = nil csiSnapshotJSON = nil csiSnapshotContentsJSON = nil + csiSnapshotClassesJSON = nil } backupInfo := persistence.BackupInfo{ @@ -760,6 +789,7 @@ func persistBackup(backup *pkgbackup.Request, BackupResourceList: backupResourceList, CSIVolumeSnapshots: csiSnapshotJSON, CSIVolumeSnapshotContents: csiSnapshotContentsJSON, + CSIVolumeSnapshotClasses: csiSnapshotClassesJSON, } if err := backupStore.PutBackup(backupInfo); err != nil { persistErrs = append(persistErrs, err) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 9107449cce..6a1f7c5622 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -283,6 +283,22 @@ func (c *backupSyncController) run() { if features.IsEnabled(velerov1api.CSIFeatureFlag) { // we are syncing these objects only to ensure that the storage snapshots are cleaned up // on backup deletion or expiry. + log.Info("Syncing CSI volumesnapshotclasses in backup") + vsClasses, err := backupStore.GetCSIVolumeSnapshotClasses(backupName) + if err != nil { + log.WithError(errors.WithStack(err)).Error("Error getting CSI volumesnapclasses for this backup from backup store") + continue + } + for _, vsClass := range vsClasses { + vsClass.ResourceVersion = "" + created, err := c.csiSnapshotClient.SnapshotV1().VolumeSnapshotClasses().Create(context.TODO(), vsClass, metav1.CreateOptions{}) + if err != nil { + log.WithError(errors.WithStack(err)).Errorf("Error syncing volumesnapshotclass %s into cluster", vsClass.Name) + continue + } + log.Infof("Created CSI volumesnapshotclass %s", created.Name) + } + log.Info("Syncing CSI volumesnapshotcontents in backup") snapConts, err := backupStore.GetCSIVolumeSnapshotContents(backupName) if err != nil { diff --git a/pkg/persistence/mocks/backup_store.go b/pkg/persistence/mocks/backup_store.go index 9a6eefacfc..449e86fa7f 100644 --- a/pkg/persistence/mocks/backup_store.go +++ b/pkg/persistence/mocks/backup_store.go @@ -284,6 +284,11 @@ func (_m *BackupStore) GetCSIVolumeSnapshotContents(backup string) ([]*snapshotv return nil, nil } +func (_m *BackupStore) GetCSIVolumeSnapshotClasses(backup string) ([]*snapshotv1api.VolumeSnapshotClass, error) { + panic("Not implemented") + return nil, nil +} + func (_m *BackupStore) GetItemSnapshots(name string) ([]*volume.ItemSnapshot, error) { panic("implement me") } diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index b60b9491b3..20bf9328e2 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -47,7 +47,8 @@ type BackupInfo struct { ItemSnapshots, BackupResourceList, CSIVolumeSnapshots, - CSIVolumeSnapshotContents io.Reader + CSIVolumeSnapshotContents, + CSIVolumeSnapshotClasses io.Reader } // BackupStore defines operations for creating, retrieving, and deleting @@ -65,6 +66,7 @@ type BackupStore interface { GetBackupContents(name string) (io.ReadCloser, error) GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error) GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1api.VolumeSnapshotContent, error) + GetCSIVolumeSnapshotClasses(name string) ([]*snapshotv1api.VolumeSnapshotClass, error) // BackupExists checks if the backup metadata file exists in object storage. BackupExists(bucket, backupName string) (bool, error) @@ -252,6 +254,7 @@ func (s *objectBackupStore) PutBackup(info BackupInfo) error { s.layout.getBackupResourceListKey(info.Name): info.BackupResourceList, s.layout.getCSIVolumeSnapshotKey(info.Name): info.CSIVolumeSnapshots, s.layout.getCSIVolumeSnapshotContentsKey(info.Name): info.CSIVolumeSnapshotContents, + s.layout.getCSIVolumeSnapshotClassesKey(info.Name): info.CSIVolumeSnapshotClasses, } for key, reader := range backupObjs { @@ -371,6 +374,25 @@ func decode(jsongzReader io.Reader, into interface{}) error { return nil } +func (s *objectBackupStore) GetCSIVolumeSnapshotClasses(name string) ([]*snapshotv1api.VolumeSnapshotClass, error) { + res, err := tryGet(s.objectStore, s.bucket, s.layout.getCSIVolumeSnapshotClassesKey(name)) + if err != nil { + return nil, err + } + if res == nil { + // this indicates that the no CSI volumesnapshots were prensent in the backup + return nil, nil + } + defer res.Close() + + var csiVSClasses []*snapshotv1api.VolumeSnapshotClass + if err := decode(res, &csiVSClasses); err != nil { + return nil, err + } + return csiVSClasses, nil + +} + func (s *objectBackupStore) GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error) { res, err := tryGet(s.objectStore, s.bucket, s.layout.getCSIVolumeSnapshotKey(name)) if err != nil { diff --git a/pkg/persistence/object_store_layout.go b/pkg/persistence/object_store_layout.go index 046b99a68b..cad7479e0f 100644 --- a/pkg/persistence/object_store_layout.go +++ b/pkg/persistence/object_store_layout.go @@ -111,3 +111,7 @@ func (l *ObjectStoreLayout) getCSIVolumeSnapshotKey(backup string) string { func (l *ObjectStoreLayout) getCSIVolumeSnapshotContentsKey(backup string) string { return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-csi-volumesnapshotcontents.json.gz", backup)) } + +func (l *ObjectStoreLayout) getCSIVolumeSnapshotClassesKey(backup string) string { + return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-csi-volumesnapshotclasses.json.gz", backup)) +} diff --git a/pkg/util/csi/reset.go b/pkg/util/csi/reset.go new file mode 100644 index 0000000000..efe10ad011 --- /dev/null +++ b/pkg/util/csi/reset.go @@ -0,0 +1,42 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package csi + +import ( + "fmt" + + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + corev1 "k8s.io/api/core/v1" +) + +// ResetVolumeSnapshotContent make changes to the volumesnapshot content before it's persisted +// It will move the snapshot Handle to the source to avoid the snapshot-controller creating a snapshot when it's +// synced by the backup sync controller. +// It will return an error if the snapshot handle is not set, which should not happen when this func is called. +func ResetVolumeSnapshotContent(snapCont *snapshotv1api.VolumeSnapshotContent) error { + if snapCont.Status != nil && snapCont.Status.SnapshotHandle != nil && len(*snapCont.Status.SnapshotHandle) > 0 { + v := *snapCont.Status.SnapshotHandle + snapCont.Spec.Source = snapshotv1api.VolumeSnapshotContentSource{ + SnapshotHandle: &v, + } + } else { + return fmt.Errorf("the volumesnapshotcontent '%s' does not have snapshothandle set", snapCont.Name) + } + + snapCont.Spec.VolumeSnapshotRef = corev1.ObjectReference{} + return nil +} From 8f08d9923a7cc287237b36aa40f3b616d9f3b4b2 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Wed, 20 Apr 2022 14:58:26 -0400 Subject: [PATCH 104/366] Add docs for deleting backups Signed-off-by: Abigail McCarthy --- site/content/docs/main/backup-reference.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/backup-reference.md b/site/content/docs/main/backup-reference.md index 06fdc07b85..7d92d6510e 100644 --- a/site/content/docs/main/backup-reference.md +++ b/site/content/docs/main/backup-reference.md @@ -77,8 +77,16 @@ If there is possibility the schedule will be disable to not create backup anymor ## Kubernetes API Pagination -By default, Velero will paginate the LIST API call for each resource type in the Kubernetes API when collecting items into a backup. The `--client-page-size` flag for the Velero server configures the size of each page. +By default, Velero will paginate the LIST API call for each resource type in the Kubernetes API when collecting items into a backup. The `--client-page-size` flag for the Velero server configures the size of each page. Depending on the cluster's scale, tuning the page size can improve backup performance. You can experiment with higher values, noting their impact on the relevant `apiserver_request_duration_seconds_*` metrics from the Kubernetes apiserver. Pagination can be entirely disabled by setting `--client-page-size` to `0`. This will request all items in a single unpaginated LIST call. + +## Deleting Backups + +Use the following commands to delete Velero backups and data: + +* `kubectl delete backup -n ` will delete the backup custom resource only and will not delete any associated data from object/block storage +* `velero backup delete ` will delete the backup resource including all data in object/block storage +* Creating a `DeleteBackupRequest` will delete the backup resource including all data in object/block storage. You can use kubectl to create a `DeleteBackupRequest`. From 22dd0b2ebb3e76be60974cb08d5536b70a609388 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 20 Apr 2022 23:51:21 +0800 Subject: [PATCH 105/366] Modify CSI VolumeSnapshot metric related code. Signed-off-by: Xun Jiang --- changelogs/unreleased/4854-jxun | 1 + pkg/controller/backup_controller.go | 2 ++ pkg/metrics/metrics.go | 9 +++++++++ 3 files changed, 12 insertions(+) create mode 100644 changelogs/unreleased/4854-jxun diff --git a/changelogs/unreleased/4854-jxun b/changelogs/unreleased/4854-jxun new file mode 100644 index 0000000000..abecfa4e1e --- /dev/null +++ b/changelogs/unreleased/4854-jxun @@ -0,0 +1 @@ +Modify CSI VolumeSnapshot metric related code. \ No newline at end of file diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 7402f7e455..e7075a560f 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -621,6 +621,8 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { if err != nil { backupLog.Error(err) } + + backup.CSISnapshots = volumeSnapshots } if c.volumeSnapshotContentLister != nil { diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 2e593a0469..d486a88bd6 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -410,6 +410,15 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[volumeSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } + if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName, "").Add(0) + } + if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName, "").Add(0) + } + if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName, "").Add(0) + } } // InitSchedule initializes counter metrics for a node. From dca7497d5c4824714b52937823179840175f558f Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 25 Apr 2022 10:53:46 -0400 Subject: [PATCH 106/366] Remove DeletedBackupRequest information Signed-off-by: Abigail McCarthy --- site/content/docs/main/backup-reference.md | 1 - 1 file changed, 1 deletion(-) diff --git a/site/content/docs/main/backup-reference.md b/site/content/docs/main/backup-reference.md index 7d92d6510e..b0eddab293 100644 --- a/site/content/docs/main/backup-reference.md +++ b/site/content/docs/main/backup-reference.md @@ -89,4 +89,3 @@ Use the following commands to delete Velero backups and data: * `kubectl delete backup -n ` will delete the backup custom resource only and will not delete any associated data from object/block storage * `velero backup delete ` will delete the backup resource including all data in object/block storage -* Creating a `DeleteBackupRequest` will delete the backup resource including all data in object/block storage. You can use kubectl to create a `DeleteBackupRequest`. From 7aba37f92b02a42d083889ddc3c30d085408a384 Mon Sep 17 00:00:00 2001 From: JenTing Hsiao Date: Tue, 22 Feb 2022 09:52:02 +0800 Subject: [PATCH 107/366] Document steps to update helm chart for each Velero GA release Signed-off-by: JenTing Hsiao --- site/content/docs/main/release-instructions.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/release-instructions.md b/site/content/docs/main/release-instructions.md index 29dc8e0cea..fa20006948 100644 --- a/site/content/docs/main/release-instructions.md +++ b/site/content/docs/main/release-instructions.md @@ -129,13 +129,22 @@ These are the steps to update the Velero Homebrew version. - Run `export HOMEBREW_GITHUB_API_TOKEN=your_token_here` on your command line to make sure that `brew` can work on GitHub on your behalf. - Run `hack/release-tools/brew-update.sh`. This script will download the necessary files, do the checks, and invoke the brew helper to submit the PR, which will open in your browser. - Update Windows Chocolatey version. From a Windows computer, follow the step-by-step instructions to [create the Windows Chocolatey package for Velero CLI](https://github.com/adamrushuk/velero-choco/blob/main/README.md) -- + ## Plugins To release plugins maintained by the Velero team, follow the [plugin release instructions](plugin-release-instructions.md). After the plugin images are built, be sure to update any [e2e tests][3] that use these plugins. +## Helm Chart (GA only) + +### Steps +- Update the CRDs under helm chart folder `crds` according to the current Velero GA version, and add the labels for the helm chart CRDs. For example: https://github.com/vmware-tanzu/helm-charts/pull/248. +- Bump the Chart version `version` on the `Chart.yaml`. +- Bump the Velero version `appVersion` on the `Chart.yaml` file and `tag` on the `values.yaml` file. +- Bump the plugin version on the `values.yaml` if needed. +- Update the _upgrade_ instruction and related tag on the `README.md` file. + ## How to write and release a blog post What to include in a release blog: * Thank all contributors for their involvement in the release. From dfc86566b8ecc2cbe221074cceae03aab776163e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Thu, 14 Apr 2022 20:05:55 +0800 Subject: [PATCH 108/366] Make in-progress backup/restore as failed when doing the reconcile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make in-progress backup/restore as failed when doing the reconcile to avoid hanging in in-progress status Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4833-ywk253100 | 1 + config/crd/v1/bases/velero.io_backups.yaml | 4 +++ config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup.go | 4 +++ pkg/controller/backup_controller.go | 24 ++++++++++++++--- pkg/controller/backup_controller_test.go | 29 ++++++++++++++++---- pkg/controller/restore_controller.go | 21 ++++++++++++--- pkg/controller/restore_controller_test.go | 31 ++++++++++++++++++---- site/content/docs/main/api-types/backup.md | 2 ++ 9 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/4833-ywk253100 diff --git a/changelogs/unreleased/4833-ywk253100 b/changelogs/unreleased/4833-ywk253100 new file mode 100644 index 0000000000..188528745e --- /dev/null +++ b/changelogs/unreleased/4833-ywk253100 @@ -0,0 +1 @@ +Make in-progress backup/restore as failed when doing the reconcile to avoid hanging in in-progress status \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index 259e3cd4fe..b0d3f0d81c 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -372,6 +372,10 @@ spec: format: date-time nullable: true type: string + failureReason: + description: FailureReason is an error that caused the entire backup + to fail. + type: string formatVersion: description: FormatVersion is the backup format version, including major, minor, and patch version. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 54838c3551..60e9847e21 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3zأo\x14\x9d-\xed\xa7n\xdf(\xb6A[y8\xf1ɢ\x9b\xe0b\x8dG\x18\xa5\xf8\x8b67PJE\xff\x90\x1fͩ\x858\xf8\"\x83\xce\xcfC,\xe0\xfd@}\x9a\x8a\x84\x8ey\xc2(fS\x0e\xe0\xf8)\xf4\n\xbe\u0e7f\xe2\x0f\x961\xe7\xe4\xd9\xd8\xc3L\xd4e\xa3\x1e\x8c\xdeS\xec;\xf2c\xa3\x12F~{\x10\xc6IQ\x14'?\xc9\xe4\xec#?|B2\a\x93>\xc18Y\x03\x96K\x94\r\xdd\xda@[*/\t|$\xbcյ\xebm\xd0v\x83\x8f\x88E\x9cs\r_\xb4Ø\x87\x95}\x98\xa4\xd2к\x15\xeev\xda8\x1fׯV w\xc1\xc7\x18\x81K&\x9a\xcf\x1e\xfc;I ]\x9b\xffj\xa5\x97\xc3\a\x83²\xf4:~\xad\x89\x8f\aE\x96\x91\v\x8b\xb7։bDk\xfc\xa6\xe3\x06v\xe6H\xfa0\xff\xb7\x11\xef\xe6\x8c\xe0\x9bn\xff\xe6\x8eFc3\x18\x9c\xa7\x1c\x97ox\x8d9j?\x80\x0f\xf5Q\xc1\xab\x91Α\x96\xea\x1e\u0380#\xbdT\x14`5\xec\xc4ċ\x1cs\xfa\x92\u007f'\x8b\xb6\x99N\n\xf6\xa3\x86\xa6\xf3\x94A\f\x8b\xd3Ė-\x93`bY\xbe\x86t\x8a\xaf\xe1\xbc\x1f\xbe\x9ax\x03V\xaa\xf8L\xa0O\xd7xQ\xb0\xe4\xbd\x1b\xe4\bx\xf4\xc0\xe9\xcc\xef\xeey\xd9}\xf4\xff\xba\x0e\xf6\xb1\xb10\x9fS<\xb5\xe7A\xf7\xc1A8\xed\xf2\x16b\xf0\xaeF\xe8\xf1\a\xb9\xf3'a\x19a\xfdǿ\xf9\x01\xf71\xc9g\xf90뮰'\xd2\xf8\x1d\xf0\t+\x83\x99\x18u\xe7\x01\x1e\n$?\xc2\"\xf6=\xa1\x0f\x179\x92Ƿ\x85F\xef\x19\x17\xc5\x17,\xdf'Z8\xbe-\"\xfaf\xe1\xd0\xfb\xae\xeeU\x18%\xd5~i\x8f\xfd{\xe86\x12\x0f\x05\b#\x11\xd1\xc82\x9a\x18i1\"\xea\x04D\x11lj\x87\xe5\x06A\xd2;\x85D\xa3v\xe0\xec#+м\xb3\xb7\xc3L\xe1K\x9bf\x12Y\x86$\xae_\x86/\xd5^_\xf3\x1f\xf11Z\xfe3\xd3ʛ[{\a\xff\xf1\x9fW\x10\xf2\x98\xcf\xf1\xd5Y\xfa\xf8\u007f\x01\x00\x00\xff\xff\x01\xf3\x19\x8b\xd5W\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3\x86C\xbb_Q\xd8ť\xfd\xd4\xed\x1br+\x9e\xdb\xfe\xba\x9c\xf0\xaf\x96\xf0\x936N\x1al߈:CH\xf3\xc4\x17\x99nO\x85\xd1ה\xce1\xed\xf6\x8d\x1b,\xe8U\x0f'>\xaet\x13\x9c\xc1\xf1X\xa8\x14\u007f\xd1\xe6\x06J\xa9\xe8\x1f\xf2\xf89\t\x12\a_\x84??d\xb1\x80\xf7\x03\xf5ij':\x86\x14㆘rU\xc7\xcf\xcbW\xf0\x05\xcf=+\u007f\x04\x8e9\xa7\xf9ƞ\x90\xa2.\x1b\xf5`\xf4\x9e\xa2\xf4\x91\x1f\x1b\xe55\xf2ۃ0N\x8a\xa28\xf9I&g\x1f\xf9\xe1\x13\x92\xe1\x9a\xf4^\xc6\xc9\x1a\xb0\\\xa2l\xe8֦\x04\xa4\xf2\x92\xc0\x87\xd7[]\xbb\x9e*iUшX\xc49\xd7\xf0E;\x8c\x19cهI\xca\x17\xad[\xe1n\xa7\x8d\xf3\x19\x88\xd5\n\xe4.xC#piO\xf0)\x89\u007f\xd1\t\xa4k3u\xad\xf4r\xa0cx\x13\xf2\xb5\xefR\x9c\xfcA\xa6\xc82r\xb6\xf1\xd6:Q\x8c\xe8\xb7\xdft0\xc2n'I\x1f\xe6\xff6⇝\x11|\xd3\xed\xdf\xdc&i\xac\x1b\x83\xf3\x94\xe3B\x13\xaf\xdbG-\x1dp\xf9\x01*x5\xd29ҧ\xddc$p\xa4A\x8b\x02,锉\xb7C\xe64;\xffN\xb6w3\x9d\xbe\xec\xc77M\xe7)\xd3\x1d\x16\xa7\x89-[&\xc1IJ|!\xa3\xb4q,\xb12;\b\xb5'\xa12\xba\xde\x1f\xa2\\NX\xc6\t\xb8yMHAU\xd4{\x12\xf5p\f\xe3j\xa3:)\x98p0\x93w\xd0\x15\xd9\xcb$\xa6!\x11\x1d_\x15\xbc\r\xaf\x81\xacvF\x97\xab\xc0\vNQ݄Ԉ\x91\x9a\xfc\u007f\n\xe4'\x80\xb6\xd7\xeeY\f\xaa\n\x15\b\x1b\xf0I\xa8\xb2\x9cg\xeb\\\x9e\xc2\t\xe3R\xa3\x8a\xc7^煀\x82!\x8f\xe3\xfb\x18\x12?\xbe\xda\xf4~\xf8\xbe\xe3\rX\xa9\u20c6>\xb1\xe4E\xc1R\x9ca\x90c\xf5ѣ\xb1\xb3\b\xa1\x17\x0f\xf4\xd1\xff\xeb\x86\x02\xc7\xc6\xc2|N\xf1)\x9f\a\xdd\aG\xf6\xb4\xcb[\x88\xc1\x0f\x1c\xa1\xc7\x1f\xe4Ο\xd9e\x84\xf5\x1f\xff\xe6G\xf1\xc7$\x9f\xe5ì\xbb\u009eH\xe3w\xc0'\xac\ffb4\xf0\x00x(\x90\xfc\b\x8b\xd8\xf7\x84>\\\xe4\xf2\x1e\xdf\x16Ľg\x04\x17\xdf\xda|\x9f\xb8\xe6\xf8\xb6\xd8\xed\x9b\x05nﻺWa\x94T\xfb\xa5=\xf6\xef\xa1\xdbH\xe4\x16 \x8c\xc4n#\xcbh\xa2\xb9\xc5ح\x13\xbaE\x1c'\x9e\xc0\x1b\x84s\xef\x14\xbc\x8dځ\xb3\x8f\xac@\xf3\xce\xde\x0e3\x85/mBLd\x19\x92\xb8~\x19\xbe\xa9{}\xcd\u007f\xc4gs\xf9\xcfL+on\xed\x1d\xfc\xc7\u007f^Aȸ>\xc7\xf7q\xe9\xe3\xff\x05\x00\x00\xff\xffj\x17\xd2\xcb\u007fX\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 15c9eddfc7..51f5f6ea3a 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -292,6 +292,10 @@ type BackupStatus struct { // +optional VolumeSnapshotsCompleted int `json:"volumeSnapshotsCompleted,omitempty"` + // FailureReason is an error that caused the entire backup to fail. + // +optional + FailureReason string `json:"failureReason,omitempty"` + // Warnings is a count of all warning messages that were generated during // execution of the backup. The actual warnings are in the backup's log // file in object storage. diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 7402f7e455..0a4310e116 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -147,13 +147,12 @@ func NewBackupController( backup := obj.(*velerov1api.Backup) switch backup.Status.Phase { - case "", velerov1api.BackupPhaseNew: - // only process new backups + case "", velerov1api.BackupPhaseNew, velerov1api.BackupPhaseInProgress: default: c.logger.WithFields(logrus.Fields{ "backup": kubeutil.NamespaceAndName(backup), "phase": backup.Status.Phase, - }).Debug("Backup is not new, skipping") + }).Debug("Backup is not new or in-progress, skipping") return } @@ -241,7 +240,22 @@ func (c *backupController) processBackup(key string) error { // this key (even though it was a no-op). switch original.Status.Phase { case "", velerov1api.BackupPhaseNew: - // only process new backups + case velerov1api.BackupPhaseInProgress: + // A backup may stay in-progress forever because of + // 1) the controller restarts during the processing of a backup + // 2) the backup with in-progress status isn't updated to completed or failed status successfully + // So we try to mark such Backups as failed to avoid it + updated := original.DeepCopy() + updated.Status.Phase = velerov1api.BackupPhaseFailed + updated.Status.FailureReason = fmt.Sprintf("got a Backup with unexpected status %q, this may be due to a restart of the controller during the backing up, mark it as %q", + velerov1api.BackupPhaseInProgress, updated.Status.Phase) + updated.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} + _, err = patchBackup(original, updated, c.client) + if err != nil { + return errors.Wrapf(err, "error updating Backup status to %s", updated.Status.Phase) + } + log.Warn(updated.Status.FailureReason) + return nil default: return nil } @@ -261,6 +275,7 @@ func (c *backupController) processBackup(key string) error { if err != nil { return errors.Wrapf(err, "error updating Backup status to %s", request.Status.Phase) } + // store ref to just-updated item for creating patch original = updatedBackup request.Backup = updatedBackup.DeepCopy() @@ -287,6 +302,7 @@ func (c *backupController) processBackup(key string) error { // result in the backup being Failed. log.WithError(err).Error("backup failed") request.Status.Phase = velerov1api.BackupPhaseFailed + request.Status.FailureReason = err.Error() } switch request.Status.Phase { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 85c581a81a..fffa831486 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -94,11 +94,6 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { key: "velero/backup-1", backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Result(), }, - { - name: "InProgress backup is not processed", - key: "velero/backup-1", - backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result(), - }, { name: "Completed backup is not processed", key: "velero/backup-1", @@ -140,6 +135,28 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { } } +func TestMarkInProgressBackupAsFailed(t *testing.T) { + backup := defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result() + clientset := fake.NewSimpleClientset(backup) + sharedInformers := informers.NewSharedInformerFactory(clientset, 0) + logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatText) + + c := &backupController{ + genericController: newGenericController("backup-test", logger), + client: clientset.VeleroV1(), + lister: sharedInformers.Velero().V1().Backups().Lister(), + clock: &clock.RealClock{}, + } + require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup)) + + err := c.processBackup(fmt.Sprintf("%s/%s", backup.Namespace, backup.Name)) + require.Nil(t, err) + + res, err := clientset.VeleroV1().Backups(backup.Namespace).Get(context.TODO(), backup.Name, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, velerov1api.BackupPhaseFailed, res.Status.Phase) +} + func TestProcessBackupValidationFailures(t *testing.T) { defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() @@ -729,6 +746,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, + FailureReason: "backup already exists in object storage", Version: 1, FormatVersion: "1.1.0", StartTimestamp: ×tamp, @@ -766,6 +784,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, + FailureReason: "error checking if backup already exists in object storage: Backup already exists in object storage", Version: 1, FormatVersion: "1.1.0", StartTimestamp: ×tamp, diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 6a79ead15c..4d01b36dfa 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -144,13 +144,12 @@ func NewRestoreController( restore := obj.(*api.Restore) switch restore.Status.Phase { - case "", api.RestorePhaseNew: - // only process new restores + case "", api.RestorePhaseNew, api.RestorePhaseInProgress: default: c.logger.WithFields(logrus.Fields{ "restore": kubeutil.NamespaceAndName(restore), "phase": restore.Status.Phase, - }).Debug("Restore is not new, skipping") + }).Debug("Restore is not new or in-progress, skipping") return } @@ -202,7 +201,21 @@ func (c *restoreController) processQueueItem(key string) error { // is ("" | New) switch restore.Status.Phase { case "", api.RestorePhaseNew: - // only process new restores + case api.RestorePhaseInProgress: + // A restore may stay in-progress forever because of + // 1) the controller restarts during the processing of a restore + // 2) the restore with in-progress status isn't updated to completed or failed status successfully + // So we try to mark such restores as failed to avoid it + updated := restore.DeepCopy() + updated.Status.Phase = api.RestorePhaseFailed + updated.Status.FailureReason = fmt.Sprintf("got a Restore with unexpected status %q, this may be due to a restart of the controller during the restore, mark it as %q", + api.RestorePhaseInProgress, updated.Status.Phase) + _, err = patchRestore(restore, updated, c.restoreClient) + if err != nil { + return errors.Wrapf(err, "error updating Restore status to %s", updated.Status.Phase) + } + log.Warn(updated.Status.FailureReason) + return nil default: return nil } diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 1641b4f850..1e37f2f567 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io/ioutil" "testing" "time" @@ -170,11 +171,6 @@ func TestProcessQueueItemSkips(t *testing.T) { restoreKey: "foo/bar", expectError: true, }, - { - name: "restore with phase InProgress does not get processed", - restoreKey: "foo/bar", - restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseInProgress).Result(), - }, { name: "restore with phase Completed does not get processed", restoreKey: "foo/bar", @@ -226,6 +222,31 @@ func TestProcessQueueItemSkips(t *testing.T) { } } +func TestMarkInProgressRestoreAsFailed(t *testing.T) { + var ( + restore = builder.ForRestore("velero", "bar").Phase(velerov1api.RestorePhaseInProgress).Result() + client = fake.NewSimpleClientset(restore) + sharedInformers = informers.NewSharedInformerFactory(client, 0) + logger = velerotest.NewLogger() + ) + + c := restoreController{ + genericController: newGenericController("restore-test", logger), + restoreClient: client.VeleroV1(), + restoreLister: sharedInformers.Velero().V1().Restores().Lister(), + } + + err := sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore) + require.Nil(t, err) + + err = c.processQueueItem(fmt.Sprintf("%s/%s", restore.Namespace, restore.Name)) + require.Nil(t, err) + + res, err := c.restoreClient.Restores(restore.Namespace).Get(context.Background(), restore.Name, metav1.GetOptions{}) + require.Nil(t, err) + assert.Equal(t, velerov1api.RestorePhaseFailed, res.Status.Phase) +} + func TestProcessQueueItem(t *testing.T) { defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result() diff --git a/site/content/docs/main/api-types/backup.md b/site/content/docs/main/api-types/backup.md index 5620c9ff69..3955bba8cd 100644 --- a/site/content/docs/main/api-types/backup.md +++ b/site/content/docs/main/api-types/backup.md @@ -144,5 +144,7 @@ status: warnings: 2 # Number of errors that were logged by the backup. errors: 0 + # An error that caused the entire backup to fail. + failureReason: "" ``` From c2c211fefbfa9a02f2ef4aa4f7c57541ae88b1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Thu, 23 Dec 2021 14:42:22 +0800 Subject: [PATCH 109/366] Convert Pod Volume Restore resource/controller to the Kubebuilder framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert Pod Volume Restore resource/controller to the Kubebuilder framework Fixes #4134 Signed-off-by: Wenkai Yin(尹文开) --- Tiltfile | 2 +- changelogs/unreleased/4655-ywk253100 | 1 + .../v1/bases/velero.io_podvolumerestores.yaml | 34 +- config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 38 ++ ..._restore.go => pod_volume_restore_type.go} | 14 + pkg/cmd/cli/restic/server.go | 125 ++---- pkg/cmd/cli/restic/server_test.go | 3 +- .../pod_volume_backup_controller.go | 6 +- .../pod_volume_restore_controller.go | 418 ++++++------------ .../pod_volume_restore_controller_test.go | 353 +++------------ pkg/util/kube/utils.go | 15 +- pkg/util/kube/utils_test.go | 14 +- 13 files changed, 356 insertions(+), 669 deletions(-) create mode 100644 changelogs/unreleased/4655-ywk253100 rename pkg/apis/velero/v1/{pod_volume_restore.go => pod_volume_restore_type.go} (72%) diff --git a/Tiltfile b/Tiltfile index ba36e33466..02da1df56d 100644 --- a/Tiltfile +++ b/Tiltfile @@ -103,7 +103,7 @@ local_resource( local_resource( "restic_binary", - cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild/restic; BIN=velero GOOS=linux GOARCH=amd64 RESTIC_VERSION=0.12.0 OUTPUT_DIR=_tiltbuild/restic ./hack/download-restic.sh', + cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild/restic; BIN=velero GOOS=linux GOARCH=amd64 RESTIC_VERSION=0.13.1 OUTPUT_DIR=_tiltbuild/restic ./hack/download-restic.sh', ) # Note: we need a distro with a bash shell to exec into the Velero container diff --git a/changelogs/unreleased/4655-ywk253100 b/changelogs/unreleased/4655-ywk253100 new file mode 100644 index 0000000000..cfc440fe70 --- /dev/null +++ b/changelogs/unreleased/4655-ywk253100 @@ -0,0 +1 @@ +Convert Pod Volume Restore resource/controller to the Kubebuilder framework \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index 2ca79c5714..c07c1c25f2 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -16,7 +16,37 @@ spec: singular: podvolumerestore scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - description: Namespace of the pod containing the volume to be restored + jsonPath: .spec.pod.namespace + name: Namespace + type: string + - description: Name of the pod containing the volume to be restored + jsonPath: .spec.pod.name + name: Pod + type: string + - description: Name of the volume to be restored + jsonPath: .spec.volume + name: Volume + type: string + - description: Pod Volume Restore status such as New/InProgress + jsonPath: .status.phase + name: Status + type: string + - description: Pod Volume Restore status such as New/InProgress + format: int64 + jsonPath: .status.progress.totalBytes + name: TotalBytes + type: integer + - description: Pod Volume Restore status such as New/InProgress + format: int64 + jsonPath: .status.progress.bytesDone + name: BytesDone + type: integer + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: properties: @@ -136,6 +166,8 @@ spec: type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 54838c3551..c1b8f96eab 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -34,7 +34,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWty\x0f\xceV\r\xa9\xddI*I\xe9\xb6kgSJv=\xae\x913\x97\xa99@DS\xec\x98\x04\x18\xa0)YI忧\x1a \xf4\xa4\x1ev\xd5Lx\xb1\x85G\xe3\xeb\xaf\x1f\xe8&GY\x96\x8dTK\x9f\xd0y\xb2f\x02\xaa%|a4\xf2\xcb\xe7\xcf\u007f\xf69\xd9\xf1\xf2\xc7\xd13\x19=\x81\xbbγm>\xa2\xb7\x9d+\xf0\x1eK2\xc4dͨAVZ\xb1\x9a\x8c\x00\x941\x96\x95\f{\xf9\tPX\xc3\xce\xd65\xbal\x81&\u007f\xee\xe68\xef\xa8\xd6\xe8\x82\xf0t\xf4\xf2\x87\xfcO\xf9\x0f#\x80\xc2a\xd8\xfeD\rzVM;\x01\xd3\xd5\xf5\b\xc0\xa8\x06'\xd0Z\xbd\xb4uנC\xcf֡ϗX\xa3\xb39ّo\xb1\x90S\x17\xcev\xed\x04\xb6\x13qs\x8f(j\xf3h\xf5\xa7 \xe7c\x94\x13\xa6j\xf2\xfc\xf7\xc1\xe9_\xc9sX\xd2֝S\xf5\x00\x8e0\xeb\xc9,\xbaZ\xb9\xe3\xf9\x11\x80/l\x8b\x13x\x10(\xad*P\x8f\x00z\x02\x02\xb4\xacWq\xf9c\x94UTب\x88\x19\xc0\xb6h~z\x9c~\xfa\xfdlo\x18\xa0u\xb6EǔԋώYwF\x014\xfa\xc2Qˁ\xf4[\x11\x18W\x81\x16{\xa2\a\xae0\x81B\xddc\x00[\x02W\xe4\xc1a\xebУ\x89\x16\xde\x13\f\xb2H\x19\xb0\xf3\u007fb\xc19\xccЉ\x18\xf0\x95\xedj-n\xb0D\xc7ర\vC\xff\xde\xc8\xf6\xc06\x1cZ+ƞ\xe3\xedC\x86\xd1\x19U\xc3R\xd5\x1d\xbe\x03e44j\r\x0e\xe5\x14\xe8̎\xbc\xb0\xc4\xe7\xf0\x9bu\bdJ;\x81\x8a\xb9\xf5\x93\xf1xA\x9cܹ\xb0M\xd3\x19\xe2\xf58x&\xcd;\xb6Ώ5.\xb1\x1e{Zd\xca\x15\x151\x16\xdc9\x1c\xab\x96\xb2\x00\xdd\x04\x97\xce\x1b\xfd\x9d\xeb\x03\xc0\xdf\xeea\xe5\xb5\xd8ֳ#\xb3ؙ\b\xcev\xc6\x02\xe2m@\x1eT\xbf5j\xb1%Z\x86\x84\x9d\x8f\u007f\x99=A::\x18\xe3\x90\xfd\xc0\xfbv\xa3ߚ@\b#S\xa2\x8bF,\x9dm\x82L4\xba\xb5d8\xfc(jBsH\xbf\xef\xe6\r\xb1\xd8\xfd_\x1dz\x16[\xe5p\x17b\x1c\xe6\b]\xab\x15\xa3\xceaj\xe0N5X\xdf)\x8f_\xdd\x00´τ\xd8\xebL\xb0\x9b\x9e\x0e\x17G\xd6v&R\n9a\xafô0k\xb1\x10\xf3\t\x83\xb2\x95J*Bl@i\x1d\xa8\xa3\xf5\xf9\x9e\xe8\xe1Еg\xae\x8a箝\xb1uj\x81\xbf\xda(\xf3p\xd1\x01\xb6\x9f\x87\xf6$p\x92Yb\x18c/\x1c|\\y$\x14\xa0N\x9bW\x15:\f{$\x8bQ!\xeee=\xb1uk\x11\x1cT\xd2\xf9\x91\x84\x13\x86\b*[}A\x8dG\xdb\a\x84\xc3\x12\x1d\x1aq\xf7\x98!Z\x1b\xf2\b+2),b\x8a\x05\xb6\x03Z\xcc#\xeaa\x88\xa7\xa9\x873\xd9s\x10\xf0O\x8fӔ1\x13\xc3=t>>\xf7\x02=\U00094135~T\\]q\xf6\xed\xb4\x8c\x87\x85\xdc\xc1\x16\x14\xb4\x84\x05\xee%c \xe3\x19\x95\x06[\x0eJ\x94[\x1b$\xc0\x1c\xf6;\xde\xc5Lѧ\xa4m\n\x17\xeaAI\x8e\"\r\u007f\x9b}x\x18\xffu\x88\xf9\x8d\x16\xa0\x8a\x02\xbd\bR\x8c\r\x1a~\a\xbe+*P^\xd4 \x87z&3y\xa3\f\x95\xe89\xef\xcf@\xe7?\xbf\xff2\xcc\x1e\xc0/\xd6\x01\xbe\xa8\xa6\xad\xf1\x1dPd|\x93\xfe\x92ϐ\x8ftl$\u008a\xb8\xa2\xc3KkÀxW\xaf\xf6*\xa8\xcb\xea\x19\xc1\xf6\xeav\b5=\xe3\x04n$\xcaw`\xfeG\x02\xeb\xbf7'\xa4\xfe.\x06Ѝ,\xba\x89\xe06\xf7\xddnDnAr\xa5\x18\xd8\xd1b\x81.\x14\bCOHޒ\x12\xbf\a\xeb\x84\x01cwD\x04\xc1b\xbd\x98\x8fP\x1f\x81\xfe\xfc\xfe\xcbI\xc4\xfb|\x01\x19\x8d/\xf0\x1e\xc8DnZ\xab\xbf\xcf\xe1)x\xc7ڰz\x91\x93\x8a\xcaz<Ŭ5\xf5Zt\xae\xd4\x12\xc1\xdb\x06a\x85u\x9d\xc5zC\xc3J\xad\x85\x85d8\xf17\x05\xadr|\xd6[S\x95\xf1\xf4\xe1\xfe\xc3$\"\x13\x87Z\x84|'\xb7SIR5H\xb9\x10\xef\xbc\xe0\x8dG\x97fz|\x17݇-\x14\x952\v\x8c\xfa\"\x94\x9d\xdcB\xf9\xed[\xe2\xf8\xf8\xeaO\xcf@\tp\x988\xfeo\x97\xe8\x95ʅJ\xf5\n\xe5\x1ev\xbc\xfc\xacr\xd2\x188\x83\x8cA?m\v/\xaa\x15ز\x1f\xdb%\xba%\xe1j\xbc\xb2\xee\x99\xcc\"\x13\xd7̢\x0f\xf8q(\xed\xc7߅?o\xd6%\x14\xe4\xd7*\x14\x16\u007f\v\xad\xe4\x1c?~\x93R\xa9V\xbc\xfe\x1e\xbb\x9d\xf5\x05\xcc\xe1^\t\x8bUEE\x95\x9a\x80>Ǟ\b&\x92\x8aS\xc7Ԭ\xcc\xfa\xab\xbb\xb2\x10\xda9A\xb4\xce\xfan3SF\xcb\xff\x9e<\xcb\xf8\x9b\x18\xec\xe8\xaa\xf0\xfd\xc7\xf4\xfe\xdb8xGo\x8a\xd5\x13\x85n\xf4\x91\xd6N\xb5PY\x12\xba\vu\xd9ǽũ\xae\x1c\xa8\v7k^U\x18z\xa3Z_Y\x9e\xde_\xc01\xdb,L\x18\xb6\x06\xe8\xcb\xc1$K\x1c\xf7l\x15x\x06O\x14u\x01K\xac\xed\x87j\xec\x1eI\xac9\u0088Ե\x01\xcfp\xb0\xbe\x16\xa1\xb4dR@\xed#̆;\x87\x835\xad\xd5\a#\xfb\x9ep0\xb95\xcd\xc1DT\U000aad8a\x15w\xfe5\x8dUؐ\x98\x8d\xf1ͽ\x98Pܾ\xb9\xb5*\xac\x14\x8e\xfb\xaf\x98\xce[\xf9\xeexGx\x8f\xe1tD\xc7\xd4`\xe8W\x02\x0eX)\x9f\x0e\x19\xb2(\xecȋ[CN\x15q\xa8CY'Ug\xa9\xa8F\r\x9b\x97\\\xf0$\x1dfh\xe8o\x87\xaa\x98$\xa8\xf3\xa8C\xef9\x00\xfax_i]\xa3x\x02\xd2\xc6g\"\xe2h\x85\xe9\xeaZ\xcdk\x9c\x00\xbb\xeex\xfaL\x005\xe8\xbdZ\\\x8a\xa0\xdf\xe2\xaa\xd8\xf1\xf5[@\xcdmǛ\x96\xaf\x0f\xa5\x9e\x8a[\xdf{\xc1\xeb\xda\xceJ\xf9KP\x1ee͐\xc7m\x82\xfa\xbc\xcbɃ\xa6k\x8e\x8f\xc9\xe0\x01W\x03\xa3S\xf3\xe8\xec¡?\xb6L\x96\f8\xd0\x04d\xf0K\xf0\x8eW\x11\xd0\x1ft\x89\x83~\x19T\xb6N\xdemY\xd5`\xbaf\x8eN\x88\x98\xaf\x19}b$\xa5\x86\xa1\x1e:\xd4\xde[&\xb7\x12R\xb6\x8b\xa2\xfan\xa2P&\xbcR\x12\xffe\v\x9a|[\xab\xf5\x80ܤI\xb8^\xc5}%\x8e\xb6\x1e\x93\xa2P\xc2?̽\xb6\xf7\x0f\xa0\xee\xad9Q\r\xa6\x90!\xc3\u007f\xfcÙۘ\f\xe3\xe2 \x95\xf6\xf3B\xe8\xcfr\xca\xd79\xe1̅\xefY9\xbe6\xed\xcd\xf6\x16_\xcaxA\xf4p\xbe\xdbM]ljj\xff\x98o\x99\xa3\x06\x89:\x1a\f\xc8\xf5\x8e\xec\xfe\xbdY?\xb2\xbd\xd9T!\xc5\x1c\xea\x87\xc3O\r77{_\x0e\xc2\xcf\xc2\x1aM\xf13\t|\xfe2\x82\xfe]ڧ\xf49@\x06\xff\x17\x00\x00\xff\xffñ\x1b\xae\xa0\x19\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xc1n\xe36\x10\xbd\xfb+\x06\xdb\xc3^*y\x17=\xb4ЭM[ h\x12,\x9cE.E\x0f\x145\xb2\xa7\xa1H\x96\x1c:u\xbf\xbe\x18J\x8aeY\x897\v\xacn&g\x1e\xdf̛\x19ҫ\xa2(V\xca\xd3\x03\x86H\xceV\xa0<ῌV~\xc5\xf2\xf1\xa7X\x92[\xef?\xae\x1e\xc96\x15\\\xa5Ȯ\xdb`t)h\xfc\x15[\xb2\xc4\xe4\xec\xaaCV\x8dbU\xad\x00\x94\xb5\x8e\x95,G\xf9\t\xa0\x9d\xe5\xe0\x8c\xc1Plі\x8f\xa9\xc6:\x91i0d\xf0\xf1\xe8\xfd\x87\xf2\xc7\xf2\xc3\n@\a\xcc\ue7e9\xc3Ȫ\xf3\x15\xd8d\xcc\n\xc0\xaa\x0e+\b\x18\x99t@\xef\"\xb1\v\x84\xb1ܣ\xc1\xe0Jr\xab\xe8Q˱\xdb\xe0\x92\xaf\xe0\xb8\xd1{\x0f\x94\xfap6\x19h3\x02\x1d\xf2\x96\xa1\xc8\u007f,n\xdfP\xe4l\xe2M\n\xca,\x11\xc9ۑ\xec6\x19\x15\xce\f䀨\x9d\xc7\n\ue10bW\x1a\x9b\x15\xc0\x90\x82̭\x18\x82\xdc\u007f\xec\xb1\xf4\x0e;Փ\x06p\x1e\xedϟ\xae\x1f~\xb8?Y\x06\xf0\xc1y\fLc|\xfd7\x11v\xb2\n\xd0`ԁ<紿\x17\xc0\xde\n\x1aQ\x14#\xf0\x0eGR\xd8\f\x1c\xc0\xb5\xc0;\x8a\x10\xd0\a\x8ch{\x8dO\x80A\x8c\x94\x05W\xff\x8d\x9aK\xb8\xc7 0\x10w.\x99F\na\x8f\x81!\xa0v[K\xff=cG`\x97\x0f5\x8aqH\xf2\xf1#\xcb\x18\xac2\xb0W&\xe1\xf7\xa0l\x03\x9d:@@9\x05\x92\x9d\xe0e\x93X\u00ad\v\bd[W\xc1\x8e\xd9\xc7j\xbd\xde\x12\x8f\x05\xad]\xd7%K|X\xe7ڤ:\xb1\vq\xdd\xe0\x1e\xcd:ҶPA\xef\x88Qs\n\xb8V\x9e\x8aL\xdd\xe6\xa2.\xbb\xe6\xbb0\xb4@|\u007f\u0095\x0f\xa2m\xe4@v;\xd9\xc8\xd5\xf6\x8a\x02Rn@\x11\xd4\xe0\xdaGqL\xb4,Iv6\xbf\xdd\u007f\x86\xf1\xe8,\xc6<\xfb9\xefG\xc7x\x94@\x12F\xb6\xc5Ћ\xd8\x06\xd7eL\xb4\x8dwd9\xffІ\xd0\xce\xd3\x1fS\xdd\x11\x8b\xee\xff$\x8c,Z\x95p\x95\xbb\x1cj\x84\xe4\x1b\xc5ؔpm\xe1Juh\xaeT\xc4o.\x80d:\x16\x92\xd8/\x93`:\xa0\xe6\xc6}\xd6&\x1b\xe3\fyA\xaf\xf9\\\xb8\xf7\xa8E>ɠ\xb8RK:\xf7\x06\xb4.\x80:\xb3/O\xa0\x97[W\xbeZ\xe9\xc7\xe4\xef\xd9\x05\xb5\xc5\x1b\xd7c\u038df\xdc~Y\xf2\x19\xc9\xc9d\xe9\xdb\x18\x97\rϰ\x01x\xa7xҿ\xac\xc8>\x8f\x81\xc5x^\x11!\v\xa1\xa4\x9d\xad\xb2\x1a\u007f\xcf\x15e\xf5\xe1BL\xb7\v.\x12\xd2\xce=\x81k\x19\xed\x14t\xe0\xba\x10I\x8d\x10\x92}\x13\xd9~~_7Rx-a\xb8@t33\x1f\xf3\xde&c\x06\xacB\xbb\xce+\xa6\xda\xe0\xf2\x91\xf2I\xd9P\x8fr\xe8{\xff\xeb\xf3\xbdw&u\xf8|\xdd\\\x88\xe0\xe1\xd4zZ8\xfd\xc2@EB\x81pzq\x9e~C\xadD\xf0\xae\x19H\f\x05\x1d%\xbe7\xc4 \x92S\xc0\xd9\x04-\x96\xdbcf\xb3Tm3\x93\xb9Ƴ\xedY\xfe\xbeh|\xb0\xe2\x14\xdf2@\xb2Øl\x9dB@\xcb\x03L\xbeQ\xbfz\x84\x18\x15y\xd2>\xf2\xa2\xbaP\x017\xe7\x1e#1\x01\x03\x96\x85i\xbf=\xa9\xf9-\x94E[\xea\xb4օNq\x05ra\x14\x02tf!\xef-\xacnP5\xe7}\\\xc0\x9d\xe3\xe5\xad\x17#\\슳\xc5(\xef\x92f\xa2s\xec\x1byX9\xf6\x90\xd2\x1a=cs7\u007f\xbd\xbf{w\xf2\x18\xcf?\xb5\xb3\r\xf5\u007f=\xe0ϿV=*6\x0f\xe3\x03[\x16\xff\x0f\x00\x00\xff\xff\x83\xf9\xd9\xe0\xf4\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85\xb4\xf0[\xce\xf1\xa1ƥ\xb9 v\xfdR\xf4\x81+\xcdZCȅu\xe0d\x81 \xea\x01\xe1YX\xe6a\xab\r\xb8L\xdayL\x88H\x8f[\xcf·a\xb5g(\x15\x0ekv\xc6dy\xa0\xfc=\x9a\xef\x1eq\x9c\x98\xffy\xf7֫h\x92a!\xaeꖺD\xf5\xee\xd3\xedß\xefz\xd50Ѓz\x96 -\bx\xe0\xf5\x03\xa6^\xe9\xe02\xe1\xc0 I\r\x95\xa3\x16\xa5\xc1U@&mH\x02h\x03%\x1a\xa9S\x99\x04D\xb9\xb3\xcdt\x95\xa7\xb0A\x02w\xddt(\x8d.\xd18\x19V\xa8/\x1d\x8bԩ\x1dp\xfc\x86&\xe5[y-BˊS\xaf;Lk\x1c\xbcnK\xdb\xf2\xcf\x00\xf7\b\x035\x12\n\xf4\xe6gL\xdc\x1a\xee\xd0\x10\x99\xc0u\xa2\xd5\x0e\r!\x90\xe8G%\xff\xd3ж\xa4\xb1\x8e\x15\xc9am6\xda\xc2\xeb\\\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xa3@\xa5:\xf4\xb8\x89]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05K\x9c袨\x94t\xfbK6\xaarS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+f]\xb15^\x17\xe9\x1f\x82D\xed\x9b\x1e\xaf\ak\xc5\x17\xb6\x97G$@\x86\xd3+\x8c\xef\xeag\xd1\x02MU\x84\xce盻\xfb\xae2I;D\x9fq\xefhX+\x02\x02L\xaa-֫qkt\xc14Q\xa5\xa5\x96\xca\xf1\x1fI.Q\r\xe1\xb7զ\x90\x8e\xe4\xfe\xef\n\xad#Y\xadᚷ'\xd2ê\xa4Փ\xae\xe1V\xc1\xb5(0\xbf\x16\x16\xbf\xb9\x00\bi\xbb\"`\xe3D\xd0\xddY\x87\x8d=j\x9d\x1f\xc2.8!\xaf\xb0\xc6\xefJLzK\x86\xfaɭLxa\xb0\xe5kL\xc0\xc0\xfa\xf92\xbej\xa16=\xd4|X\u007f\xd4\xda\xc4\xee\t\a4\xa161\xeb\x83_&\xd0䟰(i\xb9ΰx_7#\x16\t\xa3\xb4\xf1v\xc2f\x19̛\xae\xad\x1a\x1c\x18\x15\x1e.C\xc2k'\xd3\xdaj\x1c\xa0y\x1cQ\xcf\xd8VT\xb9{ \x97\x01\xed\xbd\xfe\x8c\xd6\xc9d\xac\xe5`\x12\xefG;\x06y\xa3%\x84]\x86\x86\x16'\xff\xc0\xf6n\x94.\U0001ac58\xb2\xc9\x13O\xb4en<\x02d;\xf3\x1cJ\x9d\xc2Ώ\x04\x9b}`\xfaP6\xad|6Z\xe7(\xc6P\xc3/I^\xa5\x986\x1e\xd5(.\x83\xd9\xde\x1ctb\xdfSHEZF\x9e\x1e\xb1\xaa\x9a_'\xe6\xc9\xfb\x950\bd(\xa4\xf24Az\xb7d3\xa1pT\xa4\xc3b\x82ϣ\x1a\xe9\v\xf9\xb8b\x93\xe3\x158S\x8d\xe9z\xa0!\x8c\x11\xfb#\x98\x05\xff|\tdM\x9fڜ\xe72a\x0f\xb41ڌ\x1aC31\xbf\xffC\xc02\xad\x9fb@\xfa\x1b\xb5k7'H\xf8\x18\x04\x1b\xcc\xc4Njc\x87\x1e\x0e~\xc1\xa4r8\xb5\x8e\x84\x83Tn\xb7h\x88\x16;ԍ\xff}\f\xac\xe3&\x82\x8a9.\xf8\x83y\xb5B'\xe11\x1aSSaSt{^\x80\xdc6\x02K/`+s\x87\xecK\xcd1\xdaqtf%\xf7\x92\x00\xc5\xee\xbdT\n\xe1\x92\xec\xa69\xd2F\xf4\x18`5$\xe0\xfd\xf2p\x86a\x19D\x90\x84Ʃ\xe0(\x884X\xf8\xe8\xca=\xaf\x8f\xb6\x86=\xc0w\x1f\xdfc:\a\x19\xc4k\xea\xc1\xa4\xde\r<\x9d.\v<\xc1(\x92\x9dI\xb1\x9b֜\xf1|\f\xed\x02\x04<\xe1\xde{V\xa3\x87˱B\xa2\x15\rI\x83\x1c\xd0c3\xf2\x84{&UG\xe8\xa2\xe8-Q\x15_\x9ep\x1f\xdbt\x00*\xf1W\xc7(<\xbaT\xc1\xb3\x88YJmi@\xad\xd7\x0e8\x1d7YXf\x94B\t\x88\x9f8\xedF`\xbd\xb0\xf4\x13\xee\xdfX/>Z5\x99,\x17 @\x06\x1b,\xf2\n\v\xf1\xd8\a\x91˴\x19\x8c\xd7\xc9\x02\x8a\xb7\xea\x02>jG\xff\xdc|\x91\x96XT)\xbc\xd7h?j\xc75\xdf\x14b?\x89\x13\x01\xf6\x9dyY*\xbf-\x10.\x8b\xc6oy\xe0-\x94T\xb4\x11\x9b\xb4p\xab\xe8|\xe6\xf1Y\"\xa6\f\x03s\x9e\xad\xa2\xb2\x1c\xd1UZ\xadx\x9b\x0e\xa3- \xda\xe5\xab\x16\x956=I],\xa48\xcab\xcd\xde=\xedV\xfe\x97\x83X\xf8\xb1b\xb0\xccE\x82)\xa4\x15\xc7\xdb9\xf0.\x1c>\xca\x04\n4\x8f\b%\xed\x1b\xf1J\xb5\xc0\x92\xfbr\x82\x16ƻ\x16\xa1\xd4\xdbB\x1a\xc7؊V}d\xcb \xe6\xa8\xe6\x13Q\xf6\xe3\xcd\xe3f\xc9\xdb;\xfbCQ\xe8wo\x8f\x97\xed,\v\xe5u\xe8\x83x&\xbd\xfbQ\b\x0e\xf6\xfeB\xdb+\xab\xf7\xafq\xbb\xa1\x90Ʈ\xe1\x1dߝ\xe7\xd8\xed\x1f\xa2\x84\x9d\xa1\xa2H\x12'\xd2\x02\xe9\xc9N\xe4\xe4>\x90\xf1V\x80\xb9w&\xf4\xf6\xc0\x83\x8a31ϙ\xb6~\xcf\xdfJ\xcc\xf9v\xeb\xfc\t\xf7\xe7\x17\a\xd6\xeb\xfcV\x9d\xc7\xd1$\x9b\u007f`\xb4\x1a\xafE\xab|\x0f\xe7\xfc\xdb9;fK\x96\xc8\t\xce\xdb\x02\xad\x8en\xca\xd7\xcfK\x8e\x02\x9c\xe1 \xdb\xe3usIK.\xfc\xdc,\xa2u\xba\xd4\xd6-b듶\xce\a\x00{\xee\xf6H\x840\xe6\xf4WG\rAl\x1d\x1a\xb0N\x9bp!Jfw\x10 '\xc9\xdbyٓ\xa8\x9bh\xa4'L\x87\xcc\xf3\xd6Bx\x9b~\xeeoJ\xe9\xff\xf34\x13v\x96\x98vit\x82\xd6ΫR\xe4\xce1\x13\xb0m\x82\xb5\xc2\x1f\u07b6Q\xa69&\x94\x1c\xca2W\x9c\xa0=\xe1`s\xf3\xa5\x13w&3D\u007fǨ\xf2)<\x02\xe7T\x15\x85\x18^\xceG\xb3{\xed{\x87\x05X\x13\xf3\a&\xf3X\xb1QY\xe67\xd7*\xf9[s<\n\xa9ny x\xfb͜\x15\b\xa6\x1cO=\xca\\\x87\xfe\xad@\x9a\x8a\xd8\xf3+\x84\xabf\xcdw5\x06{\x92=\xbcɈ\x97\x14\x903\xad\xb4\xeb\x06k\xea\x91\xdeX\xd8Jc]\xcb\xf0\x02\xaa\xd2\xf2u\xf2\xb7=c\xaa\x1bcN>b\xfe\xe4{w\u008a\x99~\xae\x13#\x96\x1c\xac\x03\xf8\x99\xd8!\xc8-H\a\xa8\x12])\x0ex\x91\xb9\xa0a\x16P\xf4B\xf4\x9bI\xe4\x9e\xd9鬪\"\x1e\x90\x15k\xa7T\xb3ѱn\x97\x1f\x84\x8c\x8bN\xc1ibu\xb2@]\xcdn\xedm\xe9gv\xf8\u07bd\f\x98B|\x91EU\x80(H,K\u038d[\x9f?\x18\xd2e\xbc\xac\x9f\x85tu\x06\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8a\x8d\xfbP\xcb\u007f4\xdfd\xaa\b\xd8\n\x99Wf\x81\x8d^,\x99\xa5\xe7\xb6\xda<\xbd\xfca,\x9e\x91\x15\x83\x19\x19t_\xe04\xcf\xef\x1f\xa5Y\xe62\u007f2\xf8\xf2\xaeii$i\xa9\x9e\xf3Ngi\xb2\xf7\xda\xf7Nk\xe5\x15j?\xe5\x9e\xceReN^\xddӦ\xbc\xba\xa7\xaf\xee\xe9\xab{:(\xaf\xee\xe9\xab{\xfaꞎ\x97W\xf7\xb4S^\xdd\xd3\xe8\xfd#\x86\xc3\x15t><;\x99\xab\xc8\x14\x8c9\xb6gƪ3\x8d\xae\xf3\xca:4K2\xa4o\xc7{\x8e\xe4\xd0'\xbeɊ?\x06\x9cҚ6u\xa5\xdd\xf4\x9a\x94iZ\x92a1\xf9o1\"\xbc\xf0\xe84\xe8\xe9l\xfb\xd8\x04\xba\xb9\xb4\xb9~\xeex\x93\xae\xe6\xff7\x01\x88\xd3a\xf8Zz\xfe\x1b\x9fn\xceU?\xf7\x8d\xcf\x01\x81\xe3\xdfd^ydZ\xdbL2\xdb\xf1D\xfc\xa9\x1d>`9\xb8\\\xe8\x83iz\x89߿i,#\xb2ͦs\xcc\xea[Ktb\xf7v\xdd\xff\xc5\xe9:\xe3lbf\xcf\xd2e\xfe\xfb#:\xba\xaa\xc7nZ{\xd0\xd3\xfaۿ!\xc6\x13\x14\xb5\x01%s/\x80@\xa1\a?\xfcT\xfa#\xf2\xc9\xeb|\xfe\xa0\x16\x9f\x97\xb64\x1b\xad\xc9\x1f\x9a\xdfU\xbe\"\am\xd9\a\x02\xb3\xf9f1LCL\x96\xd9x\xfe\xd8\f\xd5%\xb9e\xb1g\xf0\x88<\xb2\xf8\xec\xb18x\x80\xbf؍\xcd\x19\x8b\xf6\xdab\xf3þMVXd.X'\xc3k\x96\xe4\x89\x19`р\xc5e{E\xe7xu2\xb7\xe6\xd1:\x92\xd95\x9e\xaf5Kr,\x9f+&K+\x8a\xd7\xe8ܬ&\xe3j>\x92\xf8U\x19Y/\x9f\xfb\xfd\x92~\xfe\xf1\xfc\xaa\xa8\xac\xaa\xa8\xb3\xc0<\xcfQySK\xb3\xa5\xa2P]\x9a\x19\xd5d=\x1d\x198*\x1f\xea0\xd7\xe9\xd8Tf\xb3\xa0\xa63\x9c\x8e\x91\x1d\xcb}\x8a\xc8k:B\xb2\x9b\xf1\xb4\xd8\r\x98զ\x99\x06\xe3_Շ2\xbf\xd7\xe6\xff\v\r\xfc\xdaIk\x93\xa2\x99=\x95,a}\x96\xed~\xbcr0\xfe\xe0s\x9d\xf019\xb5\xea\x9ex\xa6\xbc(\xdd|>\x92\xc0\x8fR\xa5^\x93h\xb1t|\x1a~\xa1\x82\x13\xcc\x1a7k:\xe3\xb6\xf5h\a\xa7-\x8b\xa5 \xa3\x9e\xc2f\xef\xa3Bv\r7\"ɚ\x86\x13\x14y\xe4LX:\xd9\x17\xc2\xc1ys\x8c\xbd\f=\xa9\xe6|\r\xf0\x83n\"\b\x9d\xcfC'\xe8ZY\x94\xf9\x1e*\x8bp\xde'\xf4u\xa7\xb0IݱJ\x946\xd3\xe1%\x81\x88\x83\xd8]\xbf\xc7H\xbc$\xbc#\x90\xe4\xbaJ\x9b\x11\x8e\x88[\xa8=|z`O\x8e\xbf\x9eNگ\xcckO-\x9c\xab\x06\x1f\xa1O\x90\x9cz\xb6\xef\xa9\x1a 럖\xf3f\x9c\x81\v\xef\xd0\x1cB7\xb3\xfe\xa7\xd7\xfe\xb8G\xbd\x1a\u007f\xfaeռFs\x16\x81\xac\u007fo,\xe6\x81\x1f\xff\xa8[\"JW\x99z{M*\xc3\x0fN\x10\x11\xf4\xef1\x9c\xf6\xc4O\xfb\xdcٌ,\xdb\a\xd0\xda\b\xc3\xecsk#\xf2k\x1e\v\x9a|=\xc7\xef\xae\xfe9\xb4\x15\xd1?M\x9c\xa3\xeb\x80\x1f蘙\xe9'j\xd3\u070e\xd7@s\xc7\xf0\xb0\xc7\xdd\x14\xeb\xe3ם+\xf8\x88\xcf#\xb57\x8a&qx\x18\xf5w\x9a\x98r\x84b\xecy\xb3\xa3S\xdc5\xbd\xf8By\xc4Z\xf4\xcdܠ\xf9 R-\xf2\xbcC\xd1_\x1e\x8f\x89\xf5\x8fr\xeb\xc3F\t\xcd\xe9O\a-&\r\xd7Q\xa35e\xb0F\x97\xd4A\xa5E\xb3ô\xa3$\xf5\x1eޭ\xa96\a\xef\xac\xd4\v\x13~\xf9\xf5\xac]\xa3\"I\xb0t\xf5\xfdH\xf7\xfd\xc9\xf3s\xfe# 0 { - log.Warnf(`Init containers before the %s container may cause issues - if they interfere with volumes being restored: %s index %d`, restic.InitContainer, restic.InitContainer, resticInitContainerIndex) + pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted + pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} + if err = patchHelper.Patch(ctx, pvr); err != nil { + log.WithError(err).Error("Unable to update status to completed") + return ctrl.Result{}, err } + log.Info("Restore completed") + return ctrl.Result{}, nil +} - selector := labels.Set(map[string]string{ - velerov1api.PodUIDLabel: string(pod.UID), - }).AsSelector() +func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logrus.FieldLogger, pvr *velerov1api.PodVolumeRestore) (bool, *corev1api.Pod, error) { + if !isPVRNew(pvr) { + log.Debug("PodVolumeRestore is not new, skip") + return false, nil, nil + } - pvrs, err := c.podVolumeRestoreLister.List(selector) - if err != nil { - log.WithError(err).Error("Unable to list pod volume restores") - return + // we filter the pods during the initialization of cache, if we can get a pod here, the pod must be in the same node with the controller + // so we don't need to compare the node anymore + pod := &corev1api.Pod{} + if err := c.Get(ctx, types.NamespacedName{Namespace: pvr.Spec.Pod.Namespace, Name: pvr.Spec.Pod.Name}, pod); err != nil { + if apierrors.IsNotFound(err) { + log.WithError(err).Debug("Pod not found on this node, skip") + return false, nil, nil + } + log.WithError(err).Error("Unable to get pod") + return false, nil, err } - if len(pvrs) == 0 { - return + if !isResticInitContainerRunning(pod) { + log.Debug("Pod is not running restic-wait init container, skip") + return false, nil, nil } - for _, pvr := range pvrs { - log := loggerForPodVolumeRestore(log, pvr) - if !isPVRNew(pvr) { - log.Debug("Restore is not new, not enqueuing") - continue + return true, pod, nil +} + +func (c *PodVolumeRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { + mgr.GetConfig() + + // The pod may not being scheduled at the point when its PVRs are initially reconciled. + // By watching the pods, we can trigger the PVR reconciliation again once the pod is finally scheduled on the node. + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1api.PodVolumeRestore{}). + Watches(&source.Kind{Type: &corev1api.Pod{}}, handler.EnqueueRequestsFromMapFunc(c.findVolumeRestoresForPod)). + Complete(c) +} + +func (c *PodVolumeRestoreReconciler) findVolumeRestoresForPod(pod client.Object) []reconcile.Request { + list := &velerov1api.PodVolumeRestoreList{} + options := &client.ListOptions{ + LabelSelector: labels.Set(map[string]string{ + velerov1api.PodUIDLabel: string(pod.GetUID()), + }).AsSelector(), + } + if err := c.List(context.TODO(), list, options); err != nil { + c.logger.WithField("pod", fmt.Sprintf("%s/%s", pod.GetNamespace(), pod.GetName())).WithError(err). + Error("unable to list PodVolumeRestores") + return []reconcile.Request{} + } + requests := make([]reconcile.Request, len(list.Items)) + for i, item := range list.Items { + requests[i] = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: item.GetNamespace(), + Name: item.GetName(), + }, } - log.Debug("Enqueuing") - c.enqueue(pvr) } + return requests } func isPVRNew(pvr *velerov1api.PodVolumeRestore) bool { return pvr.Status.Phase == "" || pvr.Status.Phase == velerov1api.PodVolumeRestorePhaseNew } -func isPodOnNode(pod *corev1api.Pod, node string) bool { - return pod.Spec.NodeName == node -} - func isResticInitContainerRunning(pod *corev1api.Pod) bool { // Restic wait container can be anywhere in the list of init containers, but must be running. i := getResticInitContainerIndex(pod) @@ -237,92 +220,6 @@ func getResticInitContainerIndex(pod *corev1api.Pod) int { return -1 } -func (c *podVolumeRestoreController) processQueueItem(key string) error { - log := c.logger.WithField("key", key) - log.Debug("Running processQueueItem") - - ns, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - log.WithError(errors.WithStack(err)).Error("error splitting queue key") - return nil - } - - req, err := c.podVolumeRestoreLister.PodVolumeRestores(ns).Get(name) - if apierrors.IsNotFound(err) { - log.Debug("Unable to find PodVolumeRestore") - return nil - } - if err != nil { - return errors.Wrap(err, "error getting PodVolumeRestore") - } - - // Don't mutate the shared cache - reqCopy := req.DeepCopy() - return c.processRestoreFunc(reqCopy) -} - -func loggerForPodVolumeRestore(baseLogger logrus.FieldLogger, req *velerov1api.PodVolumeRestore) logrus.FieldLogger { - log := baseLogger.WithFields(logrus.Fields{ - "namespace": req.Namespace, - "name": req.Name, - }) - - if len(req.OwnerReferences) == 1 { - log = log.WithField("restore", fmt.Sprintf("%s/%s", req.Namespace, req.OwnerReferences[0].Name)) - } - - return log -} - -func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRestore) error { - log := loggerForPodVolumeRestore(c.logger, req) - - log.Info("Restore starting") - - var err error - - // update status to InProgress - req, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) { - r.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress - r.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()} - }) - if err != nil { - log.WithError(err).Error("Error setting PodVolumeRestore startTimestamp and phase to InProgress") - return errors.WithStack(err) - } - - pod, err := c.podLister.Pods(req.Spec.Pod.Namespace).Get(req.Spec.Pod.Name) - if err != nil { - log.WithError(err).Errorf("Error getting pod %s/%s", req.Spec.Pod.Namespace, req.Spec.Pod.Name) - return c.failRestore(req, errors.Wrap(err, "error getting pod").Error(), log) - } - - volumeDir, err := kube.GetVolumeDirectory(log, pod, req.Spec.Volume, c.pvcLister, c.pvLister, c.kbClient) - if err != nil { - log.WithError(err).Error("Error getting volume directory name") - return c.failRestore(req, errors.Wrap(err, "error getting volume directory name").Error(), log) - } - - // execute the restore process - if err := c.restorePodVolume(req, volumeDir, log); err != nil { - log.WithError(err).Error("Error restoring volume") - return c.failRestore(req, errors.Wrap(err, "error restoring volume").Error(), log) - } - - // update status to Completed - if _, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) { - r.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted - r.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - }); err != nil { - log.WithError(err).Error("Error setting PodVolumeRestore completionTimestamp and phase to Completed") - return err - } - - log.Info("Restore completed") - - return nil -} - func singlePathMatch(path string) (string, error) { matches, err := filepath.Glob(path) if err != nil { @@ -336,7 +233,12 @@ func singlePathMatch(path string) (string, error) { return matches[0], nil } -func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolumeRestore, volumeDir string, log logrus.FieldLogger) error { +func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *velerov1api.PodVolumeRestore, pod *corev1api.Pod, log logrus.FieldLogger) error { + volumeDir, err := kube.GetVolumeDirectory(ctx, log, pod, req.Spec.Volume, c.Client) + if err != nil { + return errors.Wrap(err, "error getting volume directory name") + } + // Get the full path of the new volume's directory as mounted in the daemonset pod, which // will look like: /host_pods//volumes// volumePath, err := singlePathMatch(fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir)) @@ -346,8 +248,7 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume credsFile, err := c.credentialsFileStore.Path(restic.RepoKeySelector()) if err != nil { - log.WithError(err).Error("Error creating temp restic credentials file") - return c.failRestore(req, errors.Wrap(err, "error creating temp restic credentials file").Error(), log) + return errors.Wrap(err, "error creating temp restic credentials file") } // ignore error since there's nothing we can do and it's a temp file. defer os.Remove(credsFile) @@ -360,11 +261,11 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume ) backupLocation := &velerov1api.BackupStorageLocation{} - if err := c.kbClient.Get(context.Background(), client.ObjectKey{ + if err := c.Get(ctx, client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, backupLocation); err != nil { - return c.failRestore(req, errors.Wrap(err, "error getting backup storage location").Error(), log) + return errors.Wrap(err, "error getting backup storage location") } // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic @@ -381,7 +282,7 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume env, err := restic.CmdEnv(backupLocation, c.credentialsFileStore) if err != nil { - return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) + return errors.Wrap(err, "error setting restic cmd env") } resticCmd.Env = env @@ -432,55 +333,18 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume return nil } -func (c *podVolumeRestoreController) patchPodVolumeRestore(req *velerov1api.PodVolumeRestore, mutate func(*velerov1api.PodVolumeRestore)) (*velerov1api.PodVolumeRestore, error) { - // Record original json - oldData, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrap(err, "error marshalling original PodVolumeRestore") - } - - // Mutate - mutate(req) - - // Record new json - newData, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrap(err, "error marshalling updated PodVolumeRestore") - } - - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) - if err != nil { - return nil, errors.Wrap(err, "error creating json merge patch for PodVolumeRestore") - } - - req, err = c.podVolumeRestoreClient.PodVolumeRestores(req.Namespace).Patch(context.TODO(), req.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) - if err != nil { - return nil, errors.Wrap(err, "error patching PodVolumeRestore") - } - - return req, nil -} - -func (c *podVolumeRestoreController) failRestore(req *velerov1api.PodVolumeRestore, msg string, log logrus.FieldLogger) error { - if _, err := c.patchPodVolumeRestore(req, func(pvr *velerov1api.PodVolumeRestore) { - pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed - pvr.Status.Message = msg - pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - }); err != nil { - log.WithError(err).Error("Error setting PodVolumeRestore phase to Failed") - return err - } - return nil -} - // updateRestoreProgressFunc returns a func that takes progress info and patches // the PVR with the new progress -func (c *podVolumeRestoreController) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { +func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { return func(progress velerov1api.PodVolumeOperationProgress) { - if _, err := c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) { - r.Status.Progress = progress - }); err != nil { - log.WithError(err).Error("error updating PodVolumeRestore progress") + helper, err := patch.NewHelper(req, c.Client) + if err != nil { + log.WithError(err).Error("Unable to new patch helper") + return + } + req.Status.Progress = progress + if err = helper.Patch(context.Background(), req); err != nil { + log.WithError(err).Error("Unable to update PodVolumeRestore progress") } } } diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 5503620c42..5af7e41ac0 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -17,64 +17,64 @@ limitations under the License. package controller import ( + "context" "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "k8s.io/apimachinery/pkg/runtime" + + "github.com/sirupsen/logrus" + + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/stretchr/testify/assert" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" - corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerofake "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - veleroinformers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/restic" - velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestPVRHandler(t *testing.T) { +func TestShouldProcess(t *testing.T) { controllerNode := "foo" tests := []struct { - name string - obj *velerov1api.PodVolumeRestore - pod *corev1api.Pod - shouldEnqueue bool + name string + obj *velerov1api.PodVolumeRestore + pod *corev1api.Pod + shouldProcessed bool }{ { - name: "InProgress phase pvr should not be enqueued", + name: "InProgress phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ Status: velerov1api.PodVolumeRestoreStatus{ Phase: velerov1api.PodVolumeRestorePhaseInProgress, }, }, - shouldEnqueue: false, + shouldProcessed: false, }, { - name: "Completed phase pvr should not be enqueued", + name: "Completed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ Status: velerov1api.PodVolumeRestoreStatus{ Phase: velerov1api.PodVolumeRestorePhaseCompleted, }, }, - shouldEnqueue: false, + shouldProcessed: false, }, { - name: "Failed phase pvr should not be enqueued", + name: "Failed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ Status: velerov1api.PodVolumeRestoreStatus{ Phase: velerov1api.PodVolumeRestorePhaseFailed, }, }, - shouldEnqueue: false, + shouldProcessed: false, }, { - name: "Unable to get pvr's pod should not be enqueued", + name: "Unable to get pvr's pod should not be processed", obj: &velerov1api.PodVolumeRestore{ Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ @@ -86,50 +86,10 @@ func TestPVRHandler(t *testing.T) { Phase: "", }, }, - shouldEnqueue: false, + shouldProcessed: false, }, { - name: "Empty phase pvr with pod not on node running init container should not be enqueued", - obj: &velerov1api.PodVolumeRestore{ - Spec: velerov1api.PodVolumeRestoreSpec{ - Pod: corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pod-1", - }, - }, - Status: velerov1api.PodVolumeRestoreStatus{ - Phase: "", - }, - }, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", - }, - Spec: corev1api.PodSpec{ - NodeName: "some-other-node", - InitContainers: []corev1api.Container{ - { - Name: restic.InitContainer, - }, - }, - }, - Status: corev1api.PodStatus{ - InitContainerStatuses: []corev1api.ContainerStatus{ - { - State: corev1api.ContainerState{ - Running: &corev1api.ContainerStateRunning{ - StartedAt: metav1.Time{Time: time.Now()}, - }, - }, - }, - }, - }, - }, - shouldEnqueue: false, - }, - { - name: "Empty phase pvr with pod on node not running init container should not be enqueued", + name: "Empty phase pvr with pod on node not running init container should not be processed", obj: &velerov1api.PodVolumeRestore{ Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ @@ -162,7 +122,7 @@ func TestPVRHandler(t *testing.T) { }, }, }, - shouldEnqueue: false, + shouldProcessed: false, }, { name: "Empty phase pvr with pod on node running init container should be enqueued", @@ -202,220 +162,23 @@ func TestPVRHandler(t *testing.T) { }, }, }, - shouldEnqueue: true, + shouldProcessed: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var ( - podInformer = cache.NewSharedIndexInformer(nil, new(corev1api.Pod), 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - c = &podVolumeRestoreController{ - genericController: newGenericController(PodVolumeRestore, velerotest.NewLogger()), - podLister: corev1listers.NewPodLister(podInformer.GetIndexer()), - nodeName: controllerNode, - } - ) - + builder := fake.NewClientBuilder() if test.pod != nil { - require.NoError(t, podInformer.GetStore().Add(test.pod)) - } - - c.pvrHandler(test.obj) - - if !test.shouldEnqueue { - assert.Equal(t, 0, c.queue.Len()) - return + builder.WithObjects(test.pod) } - - require.Equal(t, 1, c.queue.Len()) - }) - } -} - -func TestPodHandler(t *testing.T) { - controllerNode := "foo" - - tests := []struct { - name string - pod *corev1api.Pod - podVolumeRestores []*velerov1api.PodVolumeRestore - expectedEnqueues sets.String - }{ - { - name: "pod on controller node running restic init container with multiple PVRs has new ones enqueued", - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", - UID: types.UID("uid"), - }, - Spec: corev1api.PodSpec{ - NodeName: controllerNode, - InitContainers: []corev1api.Container{ - { - Name: restic.InitContainer, - }, - }, - }, - Status: corev1api.PodStatus{ - InitContainerStatuses: []corev1api.ContainerStatus{ - { - State: corev1api.ContainerState{ - Running: &corev1api.ContainerStateRunning{StartedAt: metav1.Time{Time: time.Now()}}, - }, - }, - }, - }, - }, - podVolumeRestores: []*velerov1api.PodVolumeRestore{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-1", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "uid", - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-2", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "uid", - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-3", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "uid", - }, - }, - Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseInProgress, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-4", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "some-other-pod", - }, - }, - }, - }, - expectedEnqueues: sets.NewString("ns-1/pvr-1", "ns-1/pvr-2"), - }, - { - name: "pod on controller node not running restic init container doesn't have PVRs enqueued", - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", - UID: types.UID("uid"), - }, - Spec: corev1api.PodSpec{ - NodeName: controllerNode, - InitContainers: []corev1api.Container{ - { - Name: restic.InitContainer, - }, - }, - }, - Status: corev1api.PodStatus{ - InitContainerStatuses: []corev1api.ContainerStatus{ - { - State: corev1api.ContainerState{}, - }, - }, - }, - }, - podVolumeRestores: []*velerov1api.PodVolumeRestore{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-1", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "uid", - }, - }, - }, - }, - }, - { - name: "pod not running on controller node doesn't have PVRs enqueued", - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", - UID: types.UID("uid"), - }, - Spec: corev1api.PodSpec{ - NodeName: "some-other-node", - InitContainers: []corev1api.Container{ - { - Name: restic.InitContainer, - }, - }, - }, - Status: corev1api.PodStatus{ - InitContainerStatuses: []corev1api.ContainerStatus{ - { - State: corev1api.ContainerState{ - Running: &corev1api.ContainerStateRunning{StartedAt: metav1.Time{Time: time.Now()}}, - }, - }, - }, - }, - }, - podVolumeRestores: []*velerov1api.PodVolumeRestore{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pvr-1", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "uid", - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var ( - client = velerofake.NewSimpleClientset() - informers = veleroinformers.NewSharedInformerFactory(client, 0) - pvrInformer = informers.Velero().V1().PodVolumeRestores() - c = &podVolumeRestoreController{ - genericController: newGenericController(PodVolumeRestore, velerotest.NewLogger()), - podVolumeRestoreLister: velerov1listers.NewPodVolumeRestoreLister(pvrInformer.Informer().GetIndexer()), - nodeName: controllerNode, - } - ) - - if len(test.podVolumeRestores) > 0 { - for _, pvr := range test.podVolumeRestores { - require.NoError(t, pvrInformer.Informer().GetStore().Add(pvr)) - } + c := &PodVolumeRestoreReconciler{ + logger: logrus.New(), + Client: builder.Build(), } - c.podHandler(test.pod) - - require.Equal(t, len(test.expectedEnqueues), c.queue.Len()) - - itemCount := c.queue.Len() - - for i := 0; i < itemCount; i++ { - item, _ := c.queue.Get() - assert.True(t, test.expectedEnqueues.Has(item.(string))) - } + shouldProcess, _, _ := c.shouldProcess(context.Background(), c.logger, test.obj) + require.Equal(t, test.shouldProcessed, shouldProcess) }) } } @@ -437,17 +200,6 @@ func TestIsPVRNew(t *testing.T) { } } -func TestIsPodOnNode(t *testing.T) { - pod := &corev1api.Pod{} - assert.False(t, isPodOnNode(pod, "bar")) - - pod.Spec.NodeName = "foo" - assert.False(t, isPodOnNode(pod, "bar")) - - pod.Spec.NodeName = "bar" - assert.True(t, isPodOnNode(pod, "bar")) -} - func TestIsResticContainerRunning(t *testing.T) { tests := []struct { name string @@ -720,3 +472,44 @@ func TestGetResticInitContainerIndex(t *testing.T) { }) } } + +func TestFindVolumeRestoresForPod(t *testing.T) { + pod := &corev1api.Pod{} + pod.UID = "uid" + + scheme := runtime.NewScheme() + scheme.AddKnownTypes(velerov1api.SchemeGroupVersion, &velerov1api.PodVolumeRestore{}, &velerov1api.PodVolumeRestoreList{}) + clientBuilder := fake.NewClientBuilder().WithScheme(scheme) + + // no matching PVR + reconciler := &PodVolumeRestoreReconciler{ + Client: clientBuilder.Build(), + logger: logrus.New(), + } + requests := reconciler.findVolumeRestoresForPod(pod) + assert.Len(t, requests, 0) + + // contain one matching PVR + reconciler.Client = clientBuilder.WithLists(&velerov1api.PodVolumeRestoreList{ + Items: []velerov1api.PodVolumeRestore{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pvr1", + Labels: map[string]string{ + velerov1api.PodUIDLabel: string(pod.GetUID()), + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pvr2", + Labels: map[string]string{ + velerov1api.PodUIDLabel: "non-matching-uid", + }, + }, + }, + }, + }).Build() + requests = reconciler.findVolumeRestoresForPod(pod) + assert.Len(t, requests, 1) +} diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index ccda34ad32..24b2ef6c70 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -21,8 +21,6 @@ import ( "fmt" "time" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/pkg/errors" "github.com/sirupsen/logrus" corev1api "k8s.io/api/core/v1" @@ -35,7 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - corev1listers "k8s.io/client-go/listers/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) // These annotations are taken from the Kubernetes persistent volume/persistent volume claim controller. @@ -117,8 +115,7 @@ func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client core // GetVolumeDirectory gets the name of the directory on the host, under /var/lib/kubelet/pods//volumes/, // where the specified volume lives. // For volumes with a CSIVolumeSource, append "/mount" to the directory name. -func GetVolumeDirectory(log logrus.FieldLogger, pod *corev1api.Pod, volumeName string, pvcLister corev1listers.PersistentVolumeClaimLister, - pvLister corev1listers.PersistentVolumeLister, client client.Client) (string, error) { +func GetVolumeDirectory(ctx context.Context, log logrus.FieldLogger, pod *corev1api.Pod, volumeName string, cli client.Client) (string, error) { var volume *corev1api.Volume for _, item := range pod.Spec.Volumes { @@ -142,18 +139,20 @@ func GetVolumeDirectory(log logrus.FieldLogger, pod *corev1api.Pod, volumeName s } // Most common case is that we have a PVC VolumeSource, and we need to check the PV it points to for a CSI source. - pvc, err := pvcLister.PersistentVolumeClaims(pod.Namespace).Get(volume.VolumeSource.PersistentVolumeClaim.ClaimName) + pvc := &corev1api.PersistentVolumeClaim{} + err := cli.Get(ctx, client.ObjectKey{Namespace: pod.Namespace, Name: volume.VolumeSource.PersistentVolumeClaim.ClaimName}, pvc) if err != nil { return "", errors.WithStack(err) } - pv, err := pvLister.Get(pvc.Spec.VolumeName) + pv := &corev1api.PersistentVolume{} + err = cli.Get(ctx, client.ObjectKey{Name: pvc.Spec.VolumeName}, pv) if err != nil { return "", errors.WithStack(err) } // PV's been created with a CSI source. - isProvisionedByCSI, err := isProvisionedByCSI(log, pv, client) + isProvisionedByCSI, err := isProvisionedByCSI(log, pv, cli) if err != nil { return "", errors.WithStack(err) } diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index eea77b1109..4a6db60695 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -17,6 +17,7 @@ limitations under the License. package kube import ( + "context" "encoding/json" "testing" "time" @@ -33,7 +34,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - kubeinformers "k8s.io/client-go/informers" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/vmware-tanzu/velero/pkg/builder" @@ -202,22 +202,18 @@ func TestGetVolumeDirectorySuccess(t *testing.T) { csiDriver := storagev1api.CSIDriver{ ObjectMeta: metav1.ObjectMeta{Name: "csi.test.com"}, } - kbClient := fake.NewClientBuilder().WithLists(&storagev1api.CSIDriverList{Items: []storagev1api.CSIDriver{csiDriver}}).Build() for _, tc := range tests { - h := newHarness(t) - - pvcInformer := kubeinformers.NewSharedInformerFactoryWithOptions(h.KubeClient, 0, kubeinformers.WithNamespace("ns-1")).Core().V1().PersistentVolumeClaims() - pvInformer := kubeinformers.NewSharedInformerFactory(h.KubeClient, 0).Core().V1().PersistentVolumes() + clientBuilder := fake.NewClientBuilder().WithLists(&storagev1api.CSIDriverList{Items: []storagev1api.CSIDriver{csiDriver}}) if tc.pvc != nil { - require.NoError(t, pvcInformer.Informer().GetStore().Add(tc.pvc)) + clientBuilder = clientBuilder.WithObjects(tc.pvc) } if tc.pv != nil { - require.NoError(t, pvInformer.Informer().GetStore().Add(tc.pv)) + clientBuilder = clientBuilder.WithObjects(tc.pv) } // Function under test - dir, err := GetVolumeDirectory(logrus.StandardLogger(), tc.pod, tc.pod.Spec.Volumes[0].Name, pvcInformer.Lister(), pvInformer.Lister(), kbClient) + dir, err := GetVolumeDirectory(context.Background(), logrus.StandardLogger(), tc.pod, tc.pod.Spec.Volumes[0].Name, clientBuilder.Build()) require.NoError(t, err) assert.Equal(t, tc.want, dir) From f89691c5c11c735a5dae9676f421dc4aba84cb43 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Wed, 27 Apr 2022 15:13:21 +0800 Subject: [PATCH 110/366] Add ClusterClasses to the restore priority list Make sure ClusterClasses are stored before Clusters. Fixes #4767 Signed-off-by: Daniel Jiang --- changelogs/unreleased/4866-reasonerjt | 1 + pkg/cmd/server/server.go | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 changelogs/unreleased/4866-reasonerjt diff --git a/changelogs/unreleased/4866-reasonerjt b/changelogs/unreleased/4866-reasonerjt new file mode 100644 index 0000000000..118106c269 --- /dev/null +++ b/changelogs/unreleased/4866-reasonerjt @@ -0,0 +1 @@ +Add ClusterClasses to the restore priority list \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 00b4ca0faa..70670c98c7 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -476,6 +476,7 @@ func (s *server) veleroResourcesExist() error { // have restic restores run before controllers adopt the pods. // - Replica sets go before deployments/other controllers so they can be explicitly // restored and be adopted by controllers. +// - CAPI ClusterClasses go before Clusters. // - CAPI Clusters come before ClusterResourceSets because failing to do so means the CAPI controller-manager will panic. // Both Clusters and ClusterResourceSets need to come before ClusterResourceSetBinding in order to properly restore workload clusters. // See https://github.com/kubernetes-sigs/cluster-api/issues/4105 @@ -498,6 +499,7 @@ var defaultRestorePriorities = []string{ // to ensure that we prioritize restoring from "apps" too, since this is how they're stored // in the backup. "replicasets.apps", + "clusterclasses.cluster.x-k8s.io", "clusters.cluster.x-k8s.io", "clusterresourcesets.addons.cluster.x-k8s.io", } From 74fd158a1ceae44cfe4a3f000e81d84e36ec4906 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 25 Apr 2022 18:03:08 +0800 Subject: [PATCH 111/366] Refactor backup deletion controller based on kubebuilder Signed-off-by: Daniel Jiang --- changelogs/unreleased/4855-reasonerjt | 1 + .../bases/velero.io_deletebackuprequests.yaml | 13 +- config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 35 +- ...uest.go => delete_backup_request_types.go} | 8 + pkg/cmd/server/server.go | 42 +- pkg/controller/backup_deletion_controller.go | 481 +++----- .../backup_deletion_controller_test.go | 1048 +++++------------ pkg/restic/common.go | 18 +- pkg/restic/common_test.go | 18 +- pkg/test/fake_controller_runtime_client.go | 9 + 11 files changed, 567 insertions(+), 1108 deletions(-) create mode 100644 changelogs/unreleased/4855-reasonerjt rename pkg/apis/velero/v1/{delete_backup_request.go => delete_backup_request_types.go} (81%) diff --git a/changelogs/unreleased/4855-reasonerjt b/changelogs/unreleased/4855-reasonerjt new file mode 100644 index 0000000000..3f05d33551 --- /dev/null +++ b/changelogs/unreleased/4855-reasonerjt @@ -0,0 +1 @@ +Refactor backup deletion controller based on kubebuilder \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml index 761036ddbf..076e0af46c 100644 --- a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml +++ b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml @@ -16,7 +16,16 @@ spec: singular: deletebackuprequest scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - description: The name of the backup to be deleted + jsonPath: .spec.backupName + name: BackupName + type: string + - description: The status of the deletion request + jsonPath: .status.phase + name: Status + type: string + name: v1 schema: openAPIV3Schema: description: DeleteBackupRequest is a request to delete one or more backups. @@ -63,6 +72,8 @@ spec: type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 54838c3551..12ee414f23 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -31,7 +31,7 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3zأo\x14\x9d-\xed\xa7n\xdf(\xb6A[y8\xf1ɢ\x9b\xe0b\x8dG\x18\xa5\xf8\x8b67PJE\xff\x90\x1fͩ\x858\xf8\"\x83\xce\xcfC,\xe0\xfd@}\x9a\x8a\x84\x8ey\xc2(fS\x0e\xe0\xf8)\xf4\n\xbe\u0e7f\xe2\x0f\x961\xe7\xe4\xd9\xd8\xc3L\xd4e\xa3\x1e\x8c\xdeS\xec;\xf2c\xa3\x12F~{\x10\xc6IQ\x14'?\xc9\xe4\xec#?|B2\a\x93>\xc18Y\x03\x96K\x94\r\xdd\xda@[*/\t|$\xbcյ\xebm\xd0v\x83\x8f\x88E\x9cs\r_\xb4Ø\x87\x95}\x98\xa4\xd2к\x15\xeev\xda8\x1fׯV w\xc1\xc7\x18\x81K&\x9a\xcf\x1e\xfc;I ]\x9b\xffj\xa5\x97\xc3\a\x83²\xf4:~\xad\x89\x8f\aE\x96\x91\v\x8b\xb7։bDk\xfc\xa6\xe3\x06v\xe6H\xfa0\xff\xb7\x11\xef\xe6\x8c\xe0\x9bn\xff\xe6\x8eFc3\x18\x9c\xa7\x1c\x97ox\x8d9j?\x80\x0f\xf5Q\xc1\xab\x91Α\x96\xea\x1e\u0380#\xbdT\x14`5\xec\xc4ċ\x1cs\xfa\x92\u007f'\x8b\xb6\x99N\n\xf6\xa3\x86\xa6\xf3\x94A\f\x8b\xd3Ė-\x93`bY\xbe\x86t\x8a\xaf\xe1\xbc\x1f\xbe\x9ax\x03V\xaa\xf8L\xa0O\xd7xQ\xb0\xe4\xbd\x1b\xe4\bx\xf4\xc0\xe9\xcc\xef\xeey\xd9}\xf4\xff\xba\x0e\xf6\xb1\xb10\x9fS<\xb5\xe7A\xf7\xc1A8\xed\xf2\x16b\xf0\xaeF\xe8\xf1\a\xb9\xf3'a\x19a\xfdǿ\xf9\x01\xf71\xc9g\xf90뮰'\xd2\xf8\x1d\xf0\t+\x83\x99\x18u\xe7\x01\x1e\n$?\xc2\"\xf6=\xa1\x0f\x179\x92Ƿ\x85F\xef\x19\x17\xc5\x17,\xdf'Z8\xbe-\"\xfaf\xe1\xd0\xfb\xae\xeeU\x18%\xd5~i\x8f\xfd{\xe86\x12\x0f\x05\b#\x11\xd1\xc82\x9a\x18i1\"\xea\x04D\x11lj\x87\xe5\x06A\xd2;\x85D\xa3v\xe0\xec#+м\xb3\xb7\xc3L\xe1K\x9bf\x12Y\x86$\xae_\x86/\xd5^_\xf3\x1f\xf11Z\xfe3\xd3ʛ[{\a\xff\xf1\x9fW\x10\xf2\x98\xcf\xf1\xd5Y\xfa\xf8\u007f\x01\x00\x00\xff\xff\x01\xf3\x19\x8b\xd5W\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWty\x0f\xceV\r\xa9\xddI*I\xe9\xb6kgSJv=\xae\x913\x97\xa99@DS\xec\x98\x04\x18\xa0)YI忧\x1a \xf4\xa4\x1ev\xd5Lx\xb1\x85G\xe3\xeb\xaf\x1f\xe8&GY\x96\x8dTK\x9f\xd0y\xb2f\x02\xaa%|a4\xf2\xcb\xe7\xcf\u007f\xf69\xd9\xf1\xf2\xc7\xd13\x19=\x81\xbbγm>\xa2\xb7\x9d+\xf0\x1eK2\xc4dͨAVZ\xb1\x9a\x8c\x00\x941\x96\x95\f{\xf9\tPX\xc3\xce\xd65\xbal\x81&\u007f\xee\xe68\xef\xa8\xd6\xe8\x82\xf0t\xf4\xf2\x87\xfcO\xf9\x0f#\x80\xc2a\xd8\xfeD\rzVM;\x01\xd3\xd5\xf5\b\xc0\xa8\x06'\xd0Z\xbd\xb4uנC\xcf֡ϗX\xa3\xb39ّo\xb1\x90S\x17\xcev\xed\x04\xb6\x13qs\x8f(j\xf3h\xf5\xa7 \xe7c\x94\x13\xa6j\xf2\xfc\xf7\xc1\xe9_\xc9sX\xd2֝S\xf5\x00\x8e0\xeb\xc9,\xbaZ\xb9\xe3\xf9\x11\x80/l\x8b\x13x\x10(\xad*P\x8f\x00z\x02\x02\xb4\xacWq\xf9c\x94UTب\x88\x19\xc0\xb6h~z\x9c~\xfa\xfdlo\x18\xa0u\xb6EǔԋώYwF\x014\xfa\xc2Qˁ\xf4[\x11\x18W\x81\x16{\xa2\a\xae0\x81B\xddc\x00[\x02W\xe4\xc1a\xebУ\x89\x16\xde\x13\f\xb2H\x19\xb0\xf3\u007fb\xc19\xccЉ\x18\xf0\x95\xedj-n\xb0D\xc7ర\vC\xff\xde\xc8\xf6\xc06\x1cZ+ƞ\xe3\xedC\x86\xd1\x19U\xc3R\xd5\x1d\xbe\x03e44j\r\x0e\xe5\x14\xe8̎\xbc\xb0\xc4\xe7\xf0\x9bu\bdJ;\x81\x8a\xb9\xf5\x93\xf1xA\x9cܹ\xb0M\xd3\x19\xe2\xf58x&\xcd;\xb6Ώ5.\xb1\x1e{Zd\xca\x15\x151\x16\xdc9\x1c\xab\x96\xb2\x00\xdd\x04\x97\xce\x1b\xfd\x9d\xeb\x03\xc0\xdf\xeea\xe5\xb5\xd8ֳ#\xb3ؙ\b\xcev\xc6\x02\xe2m@\x1eT\xbf5j\xb1%Z\x86\x84\x9d\x8f\u007f\x99=A::\x18\xe3\x90\xfd\xc0\xfbv\xa3ߚ@\b#S\xa2\x8bF,\x9dm\x82L4\xba\xb5d8\xfc(jBsH\xbf\xef\xe6\r\xb1\xd8\xfd_\x1dz\x16[\xe5p\x17b\x1c\xe6\b]\xab\x15\xa3\xceaj\xe0N5X\xdf)\x8f_\xdd\x00´τ\xd8\xebL\xb0\x9b\x9e\x0e\x17G\xd6v&R\n9a\xafô0k\xb1\x10\xf3\t\x83\xb2\x95J*Bl@i\x1d\xa8\xa3\xf5\xf9\x9e\xe8\xe1Еg\xae\x8a箝\xb1uj\x81\xbf\xda(\xf3p\xd1\x01\xb6\x9f\x87\xf6$p\x92Yb\x18c/\x1c|\\y$\x14\xa0N\x9bW\x15:\f{$\x8bQ!\xeee=\xb1uk\x11\x1cT\xd2\xf9\x91\x84\x13\x86\b*[}A\x8dG\xdb\a\x84\xc3\x12\x1d\x1aq\xf7\x98!Z\x1b\xf2\b+2),b\x8a\x05\xb6\x03Z\xcc#\xeaa\x88\xa7\xa9\x873\xd9s\x10\xf0O\x8fӔ1\x13\xc3=t>>\xf7\x02=\U00094135~T\\]q\xf6\xed\xb4\x8c\x87\x85\xdc\xc1\x16\x14\xb4\x84\x05\xee%c \xe3\x19\x95\x06[\x0eJ\x94[\x1b$\xc0\x1c\xf6;\xde\xc5Lѧ\xa4m\n\x17\xeaAI\x8e\"\r\u007f\x9b}x\x18\xffu\x88\xf9\x8d\x16\xa0\x8a\x02\xbd\bR\x8c\r\x1a~\a\xbe+*P^\xd4 \x87z&3y\xa3\f\x95\xe89\xef\xcf@\xe7?\xbf\xff2\xcc\x1e\xc0/\xd6\x01\xbe\xa8\xa6\xad\xf1\x1dPd|\x93\xfe\x92ϐ\x8ftl$\u008a\xb8\xa2\xc3KkÀxW\xaf\xf6*\xa8\xcb\xea\x19\xc1\xf6\xeav\b5=\xe3\x04n$\xcaw`\xfeG\x02\xeb\xbf7'\xa4\xfe.\x06Ѝ,\xba\x89\xe06\xf7\xddnDnAr\xa5\x18\xd8\xd1b\x81.\x14\bCOHޒ\x12\xbf\a\xeb\x84\x01cwD\x04\xc1b\xbd\x98\x8fP\x1f\x81\xfe\xfc\xfe\xcbI\xc4\xfb|\x01\x19\x8d/\xf0\x1e\xc8DnZ\xab\xbf\xcf\xe1)x\xc7ڰz\x91\x93\x8a\xcaz<Ŭ5\xf5Zt\xae\xd4\x12\xc1\xdb\x06a\x85u\x9d\xc5zC\xc3J\xad\x85\x85d8\xf17\x05\xadr|\xd6[S\x95\xf1\xf4\xe1\xfe\xc3$\"\x13\x87Z\x84|'\xb7SIR5H\xb9\x10\xef\xbc\xe0\x8dG\x97fz|\x17݇-\x14\x952\v\x8c\xfa\"\x94\x9d\xdcB\xf9\xed[\xe2\xf8\xf8\xeaO\xcf@\tp\x988\xfeo\x97\xe8\x95ʅJ\xf5\n\xe5\x1ev\xbc\xfc\xacr\xd2\x188\x83\x8cA?m\v/\xaa\x15ز\x1f\xdb%\xba%\xe1j\xbc\xb2\xee\x99\xcc\"\x13\xd7̢\x0f\xf8q(\xed\xc7߅?o\xd6%\x14\xe4\xd7*\x14\x16\u007f\v\xad\xe4\x1c?~\x93R\xa9V\xbc\xfe\x1e\xbb\x9d\xf5\x05\xcc\xe1^\t\x8bUEE\x95\x9a\x80>Ǟ\b&\x92\x8aS\xc7Ԭ\xcc\xfa\xab\xbb\xb2\x10\xda9A\xb4\xce\xfan3SF\xcb\xff\x9e<\xcb\xf8\x9b\x18\xec\xe8\xaa\xf0\xfd\xc7\xf4\xfe\xdb8xGo\x8a\xd5\x13\x85n\xf4\x91\xd6N\xb5PY\x12\xba\vu\xd9ǽũ\xae\x1c\xa8\v7k^U\x18z\xa3Z_Y\x9e\xde_\xc01\xdb,L\x18\xb6\x06\xe8\xcb\xc1$K\x1c\xf7l\x15x\x06O\x14u\x01K\xac\xed\x87j\xec\x1eI\xac9\u0088Ե\x01\xcfp\xb0\xbe\x16\xa1\xb4dR@\xed#̆;\x87\x835\xad\xd5\a#\xfb\x9ep0\xb95\xcd\xc1DT\U000aad8a\x15w\xfe5\x8dUؐ\x98\x8d\xf1ͽ\x98Pܾ\xb9\xb5*\xac\x14\x8e\xfb\xaf\x98\xce[\xf9\xeexGx\x8f\xe1tD\xc7\xd4`\xe8W\x02\x0eX)\x9f\x0e\x19\xb2(\xecȋ[CN\x15q\xa8CY'Ug\xa9\xa8F\r\x9b\x97\\\xf0$\x1dfh\xe8o\x87\xaa\x98$\xa8\xf3\xa8C\xef9\x00\xfax_i]\xa3x\x02\xd2\xc6g\"\xe2h\x85\xe9\xeaZ\xcdk\x9c\x00\xbb\xeex\xfaL\x005\xe8\xbdZ\\\x8a\xa0\xdf\xe2\xaa\xd8\xf1\xf5[@\xcdmǛ\x96\xaf\x0f\xa5\x9e\x8a[\xdf{\xc1\xeb\xda\xceJ\xf9KP\x1ee͐\xc7m\x82\xfa\xbc\xcbɃ\xa6k\x8e\x8f\xc9\xe0\x01W\x03\xa3S\xf3\xe8\xec¡?\xb6L\x96\f8\xd0\x04d\xf0K\xf0\x8eW\x11\xd0\x1ft\x89\x83~\x19T\xb6N\xdemY\xd5`\xbaf\x8eN\x88\x98\xaf\x19}b$\xa5\x86\xa1\x1e:\xd4\xde[&\xb7\x12R\xb6\x8b\xa2\xfan\xa2P&\xbcR\x12\xffe\v\x9a|[\xab\xf5\x80ܤI\xb8^\xc5}%\x8e\xb6\x1e\x93\xa2P\xc2?̽\xb6\xf7\x0f\xa0\xee\xad9Q\r\xa6\x90!\xc3\u007f\xfcÙۘ\f\xe3\xe2 \x95\xf6\xf3B\xe8\xcfr\xca\xd79\xe1̅\xefY9\xbe6\xed\xcd\xf6\x16_\xcaxA\xf4p\xbe\xdbM]ljj\xff\x98o\x99\xa3\x06\x89:\x1a\f\xc8\xf5\x8e\xec\xfe\xbdY?\xb2\xbd\xd9T!\xc5\x1c\xea\x87\xc3O\r77{_\x0e\xc2\xcf\xc2\x1aM\xf13\t|\xfe2\x82\xfe]ڧ\xf49@\x06\xff\x17\x00\x00\xff\xffñ\x1b\xae\xa0\x19\x00\x00"), diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 1f6a9f12a3..fbb442f1e5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -12,6 +12,7 @@ rules: - backups verbs: - create + - delete - apiGroups: - velero.io resources: @@ -32,6 +33,26 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - deletebackuprequests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - deletebackuprequests/status + verbs: + - get + - patch + - update - apiGroups: - velero.io resources: @@ -55,7 +76,7 @@ rules: - apiGroups: - velero.io resources: - - schedules + - podvolumebackups verbs: - create - delete @@ -67,7 +88,7 @@ rules: - apiGroups: - velero.io resources: - - schedules/status + - podvolumebackups/status verbs: - get - patch @@ -75,7 +96,7 @@ rules: - apiGroups: - velero.io resources: - - serverstatusrequests + - schedules verbs: - create - delete @@ -87,7 +108,7 @@ rules: - apiGroups: - velero.io resources: - - serverstatusrequests/status + - schedules/status verbs: - get - patch @@ -95,7 +116,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackup + - serverstatusrequests verbs: - create - delete @@ -107,8 +128,8 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackup/status + - serverstatusrequests/status verbs: - get - patch - - update \ No newline at end of file + - update diff --git a/pkg/apis/velero/v1/delete_backup_request.go b/pkg/apis/velero/v1/delete_backup_request_types.go similarity index 81% rename from pkg/apis/velero/v1/delete_backup_request.go rename to pkg/apis/velero/v1/delete_backup_request_types.go index 6e2eb51725..6e14d28d7e 100644 --- a/pkg/apis/velero/v1/delete_backup_request.go +++ b/pkg/apis/velero/v1/delete_backup_request_types.go @@ -50,8 +50,15 @@ type DeleteBackupRequestStatus struct { Errors []string `json:"errors,omitempty"` } +// TODO(2.0) After converting all resources to use the runtime-controller client, the genclient and k8s:deepcopy markers will no longer be needed and should be removed. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="BackupName",type="string",JSONPath=".spec.backupName",description="The name of the backup to be deleted" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="The status of the deletion request" // DeleteBackupRequest is a request to delete one or more backups. type DeleteBackupRequest struct { @@ -68,6 +75,7 @@ type DeleteBackupRequest struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true // DeleteBackupRequestList is a list of DeleteBackupRequests. type DeleteBackupRequestList struct { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 00b4ca0faa..7c31fa7590 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -669,34 +669,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } - deletionControllerRunInfo := func() controllerRunInfo { - deletionController := controller.NewBackupDeletionController( - s.logger, - s.sharedInformerFactory.Velero().V1().DeleteBackupRequests(), - s.veleroClient.VeleroV1(), // deleteBackupRequestClient - s.veleroClient.VeleroV1(), // backupClient - s.sharedInformerFactory.Velero().V1().Restores().Lister(), - s.veleroClient.VeleroV1(), // restoreClient - backupTracker, - s.resticManager, - s.sharedInformerFactory.Velero().V1().PodVolumeBackups().Lister(), - s.mgr.GetClient(), - s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), - csiVSLister, - csiVSCLister, - s.csiSnapshotClient, - newPluginManager, - backupStoreGetter, - s.metrics, - s.discoveryHelper, - ) - - return controllerRunInfo{ - controller: deletionController, - numWorkers: defaultControllerWorkers, - } - } - restoreControllerRunInfo := func() controllerRunInfo { restorer, err := restore.NewKubernetesRestorer( s.veleroClient.VeleroV1(), @@ -756,7 +728,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string controller.BackupSync: backupSyncControllerRunInfo, controller.Backup: backupControllerRunInfo, controller.GarbageCollection: gcControllerRunInfo, - controller.BackupDeletion: deletionControllerRunInfo, controller.Restore: restoreControllerRunInfo, controller.ResticRepo: resticRepoControllerRunInfo, } @@ -830,6 +801,19 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) } + if err := controller.NewBackupDeletionReconciler( + s.logger, + s.mgr.GetClient(), + backupTracker, + s.resticManager, + s.metrics, + s.discoveryHelper, + newPluginManager, + backupStoreGetter, + ).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupDeletion) + } + if _, ok := enabledRuntimeControllers[controller.ServerStatusRequest]; ok { r := controller.ServerStatusRequestReconciler{ Scheme: s.mgr.GetScheme(), diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 2ca0fda470..67c53dd7b4 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -23,25 +23,19 @@ import ( "time" jsonpatch "github.com/evanphx/json-patch" - snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" - snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" kubeerrs "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/client-go/tools/cache" + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" "github.com/vmware-tanzu/velero/internal/delete" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/discovery" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/persistence" @@ -54,250 +48,202 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const resticTimeout = time.Minute - -type backupDeletionController struct { - *genericController - - deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister - backupClient velerov1client.BackupsGetter - restoreLister velerov1listers.RestoreLister - restoreClient velerov1client.RestoresGetter - backupTracker BackupTracker - resticMgr restic.RepositoryManager - podvolumeBackupLister velerov1listers.PodVolumeBackupLister - kbClient client.Client - snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister - csiSnapshotLister snapshotv1listers.VolumeSnapshotLister - csiSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister - csiSnapshotClient *snapshotterClientSet.Clientset - processRequestFunc func(*velerov1api.DeleteBackupRequest) error - clock clock.Clock - newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - backupStoreGetter persistence.ObjectBackupStoreGetter - metrics *metrics.ServerMetrics - helper discovery.Helper +const ( + resticTimeout = time.Minute + deleteBackupRequestMaxAge = 24 * time.Hour +) + +type backupDeletionReconciler struct { + client.Client + logger logrus.FieldLogger + backupTracker BackupTracker + resticMgr restic.RepositoryManager + metrics *metrics.ServerMetrics + clock clock.Clock + discoveryHelper discovery.Helper + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager + backupStoreGetter persistence.ObjectBackupStoreGetter } -// NewBackupDeletionController creates a new backup deletion controller. -func NewBackupDeletionController( +// NewBackupDeletionReconciler creates a new backup deletion reconciler. +func NewBackupDeletionReconciler( logger logrus.FieldLogger, - deleteBackupRequestInformer velerov1informers.DeleteBackupRequestInformer, - deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - backupClient velerov1client.BackupsGetter, - restoreLister velerov1listers.RestoreLister, - restoreClient velerov1client.RestoresGetter, + client client.Client, backupTracker BackupTracker, resticMgr restic.RepositoryManager, - podvolumeBackupLister velerov1listers.PodVolumeBackupLister, - kbClient client.Client, - snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, - csiSnapshotLister snapshotv1listers.VolumeSnapshotLister, - csiSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, - csiSnapshotClient *snapshotterClientSet.Clientset, - newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, - backupStoreGetter persistence.ObjectBackupStoreGetter, metrics *metrics.ServerMetrics, helper discovery.Helper, -) Interface { - c := &backupDeletionController{ - genericController: newGenericController(BackupDeletion, logger), - deleteBackupRequestClient: deleteBackupRequestClient, - deleteBackupRequestLister: deleteBackupRequestInformer.Lister(), - backupClient: backupClient, - restoreLister: restoreLister, - restoreClient: restoreClient, - backupTracker: backupTracker, - resticMgr: resticMgr, - podvolumeBackupLister: podvolumeBackupLister, - kbClient: kbClient, - snapshotLocationLister: snapshotLocationLister, - csiSnapshotLister: csiSnapshotLister, - csiSnapshotContentLister: csiSnapshotContentLister, - csiSnapshotClient: csiSnapshotClient, - metrics: metrics, - helper: helper, - // use variables to refer to these functions so they can be - // replaced with fakes for testing. + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, + backupStoreGetter persistence.ObjectBackupStoreGetter, +) *backupDeletionReconciler { + return &backupDeletionReconciler{ + Client: client, + logger: logger, + backupTracker: backupTracker, + resticMgr: resticMgr, + metrics: metrics, + clock: clock.RealClock{}, + discoveryHelper: helper, newPluginManager: newPluginManager, backupStoreGetter: backupStoreGetter, - - clock: &clock.RealClock{}, } - - c.syncHandler = c.processQueueItem - c.processRequestFunc = c.processRequest - - deleteBackupRequestInformer.Informer().AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: c.enqueue, - }, - ) - - c.resyncPeriod = time.Hour - c.resyncFunc = c.deleteExpiredRequests - - return c } -func (c *backupDeletionController) processQueueItem(key string) error { - log := c.logger.WithField("key", key) - log.Debug("Running processItem") - - ns, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return errors.Wrap(err, "error splitting queue key") - } - - req, err := c.deleteBackupRequestLister.DeleteBackupRequests(ns).Get(name) - if apierrors.IsNotFound(err) { - log.Debug("Unable to find DeleteBackupRequest") - return nil - } - if err != nil { - return errors.Wrap(err, "error getting DeleteBackupRequest") - } - - switch req.Status.Phase { - case velerov1api.DeleteBackupRequestPhaseProcessed: - // Don't do anything because it's already been processed - default: - // Don't mutate the shared cache - reqCopy := req.DeepCopy() - return c.processRequestFunc(reqCopy) - } - - return nil +func (r *backupDeletionReconciler) SetupWithManager(mgr ctrl.Manager) error { + // Make sure the expired requests can be deleted eventually + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.DeleteBackupRequestList{}, time.Hour) + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1api.DeleteBackupRequest{}). + Watches(s, nil). + Complete(r) } -func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupRequest) error { - log := c.logger.WithFields(logrus.Fields{ - "namespace": req.Namespace, - "name": req.Name, - "backup": req.Spec.BackupName, - }) +// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=velero.io,resources=backups,verbs=delete - var err error +func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.logger.WithFields(logrus.Fields{ + "controller": BackupDeletion, + "deletebackuprequest": req.String(), + }) + log.Debug("Getting deletebackuprequest") + dbr := &velerov1api.DeleteBackupRequest{} + if err := r.Get(ctx, req.NamespacedName, dbr); err != nil { + if apierrors.IsNotFound(err) { + log.Debug("Unable to find the deletebackuprequest") + return ctrl.Result{}, nil + } + log.WithError(err).Error("Error getting deletebackuprequest") + return ctrl.Result{}, err + } + + // Since we use the reconciler along with the PeriodicalEnqueueSource, there may be reconciliation triggered by + // stale requests. + if dbr.Status.Phase == velerov1api.DeleteBackupRequestPhaseProcessed { + age := r.clock.Now().Sub(dbr.CreationTimestamp.Time) + if age >= deleteBackupRequestMaxAge { // delete the expired request + log.Debug("The request is expired, deleting it.") + if err := r.Delete(ctx, dbr); err != nil { + log.WithError(err).Error("Error deleting DeleteBackupRequest") + } + } else { + log.Info("The request has been processed, skip.") + } + return ctrl.Result{}, nil + } // Make sure we have the backup name - if req.Spec.BackupName == "" { - _, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { - r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - r.Status.Errors = []string{"spec.backupName is required"} + if dbr.Spec.BackupName == "" { + _, err := r.patchDeleteBackupRequest(ctx, dbr, func(res *velerov1api.DeleteBackupRequest) { + res.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed + res.Status.Errors = []string{"spec.backupName is required"} }) - return err + return ctrl.Result{}, err } + log = log.WithField("backup", dbr.Spec.BackupName) + // Remove any existing deletion requests for this backup so we only have // one at a time - if errs := c.deleteExistingDeletionRequests(req, log); errs != nil { - return kubeerrs.NewAggregate(errs) + if errs := r.deleteExistingDeletionRequests(ctx, dbr, log); errs != nil { + return ctrl.Result{}, kubeerrs.NewAggregate(errs) } // Don't allow deleting an in-progress backup - if c.backupTracker.Contains(req.Namespace, req.Spec.BackupName) { - _, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + if r.backupTracker.Contains(dbr.Namespace, dbr.Spec.BackupName) { + _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed r.Status.Errors = []string{"backup is still in progress"} }) - - return err + return ctrl.Result{}, err } // Get the backup we're trying to delete - backup, err := c.backupClient.Backups(req.Namespace).Get(context.TODO(), req.Spec.BackupName, metav1.GetOptions{}) - if apierrors.IsNotFound(err) { + backup := &velerov1api.Backup{} + if err := r.Get(ctx, types.NamespacedName{ + Namespace: dbr.Namespace, + Name: dbr.Spec.BackupName, + }, backup); apierrors.IsNotFound(err) { // Couldn't find backup - update status to Processed and record the not-found error - req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + _, err = r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed r.Status.Errors = []string{"backup not found"} }) - - return err - } - if err != nil { - return errors.Wrap(err, "error getting backup") + return ctrl.Result{}, err + } else if err != nil { + return ctrl.Result{}, errors.Wrap(err, "error getting backup") } // Don't allow deleting backups in read-only storage locations location := &velerov1api.BackupStorageLocation{} - if err := c.kbClient.Get(context.Background(), client.ObjectKey{ + if err := r.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { if apierrors.IsNotFound(err) { - _, err := c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("backup storage location %s not found", backup.Spec.StorageLocation)) }) - return err + return ctrl.Result{}, err } - return errors.Wrap(err, "error getting backup storage location") + return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } if location.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { - _, err := c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("cannot delete backup because backup storage location %s is currently in read-only mode", location.Name)) }) - return err + return ctrl.Result{}, err } // if the request object has no labels defined, initialise an empty map since // we will be updating labels - if req.Labels == nil { - req.Labels = map[string]string{} + if dbr.Labels == nil { + dbr.Labels = map[string]string{} } - - // Update status to InProgress and set backup-name label if needed - req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + // Update status to InProgress and set backup-name and backup-uid label if needed + dbr, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseInProgress - if req.Labels[velerov1api.BackupNameLabel] == "" { - req.Labels[velerov1api.BackupNameLabel] = label.GetValidName(req.Spec.BackupName) + if r.Labels[velerov1api.BackupNameLabel] == "" { + r.Labels[velerov1api.BackupNameLabel] = label.GetValidName(dbr.Spec.BackupName) } - }) - if err != nil { - return err - } - // Set backup-uid label if needed - if req.Labels[velerov1api.BackupUIDLabel] == "" { - req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { - req.Labels[velerov1api.BackupUIDLabel] = string(backup.UID) - }) - if err != nil { - return err + if r.Labels[velerov1api.BackupUIDLabel] == "" { + r.Labels[velerov1api.BackupUIDLabel] = string(backup.UID) } + }) + if err != nil { + return ctrl.Result{}, err } // Set backup status to Deleting - backup, err = c.patchBackup(backup, func(b *velerov1api.Backup) { + backup, err = r.patchBackup(ctx, backup, func(b *velerov1api.Backup) { b.Status.Phase = velerov1api.BackupPhaseDeleting }) if err != nil { log.WithError(errors.WithStack(err)).Error("Error setting backup phase to deleting") - return err + return ctrl.Result{}, err } backupScheduleName := backup.GetLabels()[velerov1api.ScheduleNameLabel] - c.metrics.RegisterBackupDeletionAttempt(backupScheduleName) + r.metrics.RegisterBackupDeletionAttempt(backupScheduleName) - var errs []string - - pluginManager := c.newPluginManager(log) + pluginManager := r.newPluginManager(log) defer pluginManager.CleanupClients() - backupStore, err := c.backupStoreGetter.Get(location, pluginManager, log) + backupStore, err := r.backupStoreGetter.Get(location, pluginManager, log) if err != nil { - return errors.Wrap(err, "error getting the backup store") + return ctrl.Result{}, errors.Wrap(err, "error getting the backup store") } actions, err := pluginManager.GetDeleteItemActions() log.Debugf("%d actions before invoking actions", len(actions)) if err != nil { - return errors.Wrap(err, "error getting delete item actions") + return ctrl.Result{}, errors.Wrap(err, "error getting delete item actions") } // don't defer CleanupClients here, since it was already called above. @@ -308,13 +254,13 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR if err != nil { log.WithError(err).Errorf("Unable to download tarball for backup %s, skipping associated DeleteItemAction plugins", backup.Name) } else { - defer closeAndRemoveFile(backupFile, c.logger) + defer closeAndRemoveFile(backupFile, r.logger) ctx := &delete.Context{ Backup: backup, BackupReader: backupFile, Actions: actions, - Log: c.logger, - DiscoveryHelper: c.helper, + Log: r.logger, + DiscoveryHelper: r.discoveryHelper, Filesystem: filesystem.NewFileSystem(), } @@ -322,11 +268,13 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR // but what do we do with the error returned? We can't just swallow it as that may lead to dangling resources. err = delete.InvokeDeleteActions(ctx) if err != nil { - return errors.Wrap(err, "error invoking delete item actions") + return ctrl.Result{}, errors.Wrap(err, "error invoking delete item actions") } } } + var errs []string + if backupStore != nil { log.Info("Removing PV snapshots") @@ -340,7 +288,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR volumeSnapshotter, ok := volumeSnapshotters[snapshot.Spec.Location] if !ok { - if volumeSnapshotter, err = volumeSnapshotterForSnapshotLocation(backup.Namespace, snapshot.Spec.Location, c.snapshotLocationLister, pluginManager); err != nil { + if volumeSnapshotter, err = volumeSnapshottersForVSL(ctx, backup.Namespace, snapshot.Spec.Location, r.Client, pluginManager); err != nil { errs = append(errs, err.Error()) continue } @@ -353,9 +301,8 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } } } - log.Info("Removing restic snapshots") - if deleteErrs := c.deleteResticSnapshots(backup); len(deleteErrs) > 0 { + if deleteErrs := r.deleteResticSnapshots(ctx, backup); len(deleteErrs) > 0 { for _, err := range deleteErrs { errs = append(errs, err.Error()) } @@ -369,15 +316,19 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } log.Info("Removing restores") - if restores, err := c.restoreLister.Restores(backup.Namespace).List(labels.Everything()); err != nil { + restoreList := &velerov1api.RestoreList{} + selector := labels.Everything() + if err := r.List(ctx, restoreList, &client.ListOptions{ + Namespace: backup.Namespace, + LabelSelector: selector, + }); err != nil { log.WithError(errors.WithStack(err)).Error("Error listing restore API objects") } else { - for _, restore := range restores { + for _, restore := range restoreList.Items { if restore.Spec.BackupName != backup.Name { continue } - - restoreLog := log.WithField("restore", kube.NamespaceAndName(restore)) + restoreLog := log.WithField("restore", kube.NamespaceAndName(&restore)) restoreLog.Info("Deleting restore log/results from backup storage") if err := backupStore.DeleteRestore(restore.Name); err != nil { @@ -387,202 +338,160 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } restoreLog.Info("Deleting restore referencing backup") - if err := c.restoreClient.Restores(restore.Namespace).Delete(context.TODO(), restore.Name, metav1.DeleteOptions{}); err != nil { - errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(restore)).Error()) + if err := r.Delete(ctx, &restore); err != nil { + errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(&restore)).Error()) } } } if len(errs) == 0 { // Only try to delete the backup object from kube if everything preceding went smoothly - err = c.backupClient.Backups(backup.Namespace).Delete(context.TODO(), backup.Name, metav1.DeleteOptions{}) - if err != nil { + if err := r.Delete(ctx, backup); err != nil { errs = append(errs, errors.Wrapf(err, "error deleting backup %s", kube.NamespaceAndName(backup)).Error()) } } if len(errs) == 0 { - c.metrics.RegisterBackupDeletionSuccess(backupScheduleName) + r.metrics.RegisterBackupDeletionSuccess(backupScheduleName) } else { - c.metrics.RegisterBackupDeletionFailed(backupScheduleName) + r.metrics.RegisterBackupDeletionFailed(backupScheduleName) } // Update status to processed and record errors - req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { + if _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed r.Status.Errors = errs - }) - if err != nil { - return err + }); err != nil { + return ctrl.Result{}, err } - // Everything deleted correctly, so we can delete all DeleteBackupRequests for this backup if len(errs) == 0 { - listOptions := pkgbackup.NewDeleteBackupRequestListOptions(backup.Name, string(backup.UID)) - err = c.deleteBackupRequestClient.DeleteBackupRequests(req.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, listOptions) + labelSelector, err := labels.Parse(fmt.Sprintf("%s=%s,%s=%s", velerov1api.BackupNameLabel, label.GetValidName(backup.Name), velerov1api.BackupUIDLabel, backup.UID)) + if err != nil { + // Should not be here + r.logger.WithError(err).WithField("backup", kube.NamespaceAndName(backup)).Error("error creating label selector for the backup for deleting DeleteBackupRequests") + return ctrl.Result{}, nil + } + alldbr := &velerov1api.DeleteBackupRequest{} + err = r.DeleteAllOf(ctx, alldbr, client.MatchingLabelsSelector{ + Selector: labelSelector, + }, client.InNamespace(dbr.Namespace)) if err != nil { // If this errors, all we can do is log it. - c.logger.WithField("backup", kube.NamespaceAndName(backup)).Error("error deleting all associated DeleteBackupRequests after successfully deleting the backup") + r.logger.WithError(err).WithField("backup", kube.NamespaceAndName(backup)).Error("error deleting all associated DeleteBackupRequests after successfully deleting the backup") } } + log.Infof("Reconciliation done") - return nil + return ctrl.Result{}, nil } -func volumeSnapshotterForSnapshotLocation( - namespace, snapshotLocationName string, - snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, +func volumeSnapshottersForVSL( + ctx context.Context, + namespace, vslName string, + client client.Client, pluginManager clientmgmt.Manager, ) (velero.VolumeSnapshotter, error) { - snapshotLocation, err := snapshotLocationLister.VolumeSnapshotLocations(namespace).Get(snapshotLocationName) + vsl := &velerov1api.VolumeSnapshotLocation{} + if err := client.Get(ctx, types.NamespacedName{ + Namespace: namespace, + Name: vslName, + }, vsl); err != nil { + return nil, errors.Wrapf(err, "error getting volume snapshot location %s", vslName) + } + volumeSnapshotter, err := pluginManager.GetVolumeSnapshotter(vsl.Spec.Provider) if err != nil { - return nil, errors.Wrapf(err, "error getting volume snapshot location %s", snapshotLocationName) + return nil, errors.Wrapf(err, "error getting volume snapshotter for provider %s", vsl.Spec.Provider) } - volumeSnapshotter, err := pluginManager.GetVolumeSnapshotter(snapshotLocation.Spec.Provider) - if err != nil { - return nil, errors.Wrapf(err, "error getting volume snapshotter for provider %s", snapshotLocation.Spec.Provider) - } - - if err = volumeSnapshotter.Init(snapshotLocation.Spec.Config); err != nil { - return nil, errors.Wrapf(err, "error initializing volume snapshotter for volume snapshot location %s", snapshotLocationName) + if err = volumeSnapshotter.Init(vsl.Spec.Config); err != nil { + return nil, errors.Wrapf(err, "error initializing volume snapshotter for volume snapshot location %s", vslName) } return volumeSnapshotter, nil } -func (c *backupDeletionController) deleteExistingDeletionRequests(req *velerov1api.DeleteBackupRequest, log logrus.FieldLogger) []error { +func (r *backupDeletionReconciler) deleteExistingDeletionRequests(ctx context.Context, req *velerov1api.DeleteBackupRequest, log logrus.FieldLogger) []error { log.Info("Removing existing deletion requests for backup") + dbrList := &velerov1api.DeleteBackupRequestList{} selector := label.NewSelectorForBackup(req.Spec.BackupName) - dbrs, err := c.deleteBackupRequestLister.DeleteBackupRequests(req.Namespace).List(selector) - if err != nil { + if err := r.List(ctx, dbrList, &client.ListOptions{ + Namespace: req.Namespace, + LabelSelector: selector, + }); err != nil { return []error{errors.Wrap(err, "error listing existing DeleteBackupRequests for backup")} } - var errs []error - for _, dbr := range dbrs { + for _, dbr := range dbrList.Items { if dbr.Name == req.Name { continue } - - if err := c.deleteBackupRequestClient.DeleteBackupRequests(req.Namespace).Delete(context.TODO(), dbr.Name, metav1.DeleteOptions{}); err != nil { + if err := r.Delete(ctx, &dbr); err != nil { errs = append(errs, errors.WithStack(err)) + } else { + log.Infof("deletion request '%s' removed.", dbr.Name) } } - return errs } -func (c *backupDeletionController) deleteResticSnapshots(backup *velerov1api.Backup) []error { - if c.resticMgr == nil { +func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, backup *velerov1api.Backup) []error { + if r.resticMgr == nil { return nil } - snapshots, err := restic.GetSnapshotsInBackup(backup, c.podvolumeBackupLister) + snapshots, err := restic.GetSnapshotsInBackup(ctx, backup, r.Client) if err != nil { return []error{err} } - ctx, cancelFunc := context.WithTimeout(context.Background(), resticTimeout) + ctx2, cancelFunc := context.WithTimeout(ctx, resticTimeout) defer cancelFunc() var errs []error for _, snapshot := range snapshots { - if err := c.resticMgr.Forget(ctx, snapshot); err != nil { + if err := r.resticMgr.Forget(ctx2, snapshot); err != nil { errs = append(errs, err) } } - return errs } -const deleteBackupRequestMaxAge = 24 * time.Hour - -func (c *backupDeletionController) deleteExpiredRequests() { - c.logger.Info("Checking for expired DeleteBackupRequests") - defer c.logger.Info("Done checking for expired DeleteBackupRequests") - - // Our shared informer factory filters on a single namespace, so asking for all is ok here. - requests, err := c.deleteBackupRequestLister.List(labels.Everything()) +func (r *backupDeletionReconciler) patchDeleteBackupRequest(ctx context.Context, req *velerov1api.DeleteBackupRequest, mutate func(*velerov1api.DeleteBackupRequest)) (*velerov1api.DeleteBackupRequest, error) { + patchHelper, err := patch.NewHelper(req, r.Client) if err != nil { - c.logger.WithError(err).Error("unable to check for expired DeleteBackupRequests") - return + return nil, errors.Wrap(err, "unable to get the patch helper") } - - now := c.clock.Now() - - for _, req := range requests { - if req.Status.Phase != velerov1api.DeleteBackupRequestPhaseProcessed { - continue - } - - age := now.Sub(req.CreationTimestamp.Time) - if age >= deleteBackupRequestMaxAge { - reqLog := c.logger.WithFields(logrus.Fields{"namespace": req.Namespace, "name": req.Name}) - reqLog.Info("Deleting expired DeleteBackupRequest") - - err = c.deleteBackupRequestClient.DeleteBackupRequests(req.Namespace).Delete(context.TODO(), req.Name, metav1.DeleteOptions{}) - if err != nil { - reqLog.WithError(err).Error("Error deleting DeleteBackupRequest") - } - } - } -} - -func (c *backupDeletionController) patchDeleteBackupRequest(req *velerov1api.DeleteBackupRequest, mutate func(*velerov1api.DeleteBackupRequest)) (*velerov1api.DeleteBackupRequest, error) { - // Record original json - oldData, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrap(err, "error marshalling original DeleteBackupRequest") - } - // Mutate mutate(req) - - // Record new json - newData, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrap(err, "error marshalling updated DeleteBackupRequest") - } - - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) - if err != nil { - return nil, errors.Wrap(err, "error creating json merge patch for DeleteBackupRequest") - } - - req, err = c.deleteBackupRequestClient.DeleteBackupRequests(req.Namespace).Patch(context.TODO(), req.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) - if err != nil { - return nil, errors.Wrap(err, "error patching DeleteBackupRequest") + if err := patchHelper.Patch(ctx, req); err != nil { + return nil, errors.Wrap(err, "error patching the deletebackuprquest") } - return req, nil } -func (c *backupDeletionController) patchBackup(backup *velerov1api.Backup, mutate func(*velerov1api.Backup)) (*velerov1api.Backup, error) { +func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *velerov1api.Backup, mutate func(*velerov1api.Backup)) (*velerov1api.Backup, error) { + //TODO: The patchHelper can't be used here because the `backup/xxx/status` does not exist, until the bakcup resource is refactored + // Record original json oldData, err := json.Marshal(backup) if err != nil { return nil, errors.Wrap(err, "error marshalling original Backup") } - // Mutate - mutate(backup) - - // Record new json - newData, err := json.Marshal(backup) + newBackup := backup.DeepCopy() + mutate(newBackup) + newData, err := json.Marshal(newBackup) if err != nil { return nil, errors.Wrap(err, "error marshalling updated Backup") } - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) if err != nil { return nil, errors.Wrap(err, "error creating json merge patch for Backup") } - backup, err = c.backupClient.Backups(backup.Namespace).Patch(context.TODO(), backup.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) - if err != nil { + if err := r.Client.Patch(ctx, backup, client.RawPatch(types.MergePatchType, patchBytes)); err != nil { return nil, errors.Wrap(err, "error patching Backup") } - return backup, nil } diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index a03e98eb04..816f906276 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -18,33 +18,35 @@ package controller import ( "bytes" - "context" "fmt" - "io/ioutil" - "strings" - "testing" "time" - "github.com/pkg/errors" + "context" + "io/ioutil" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + + "strings" + "testing" + + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/sets" - core "k8s.io/client-go/testing" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" "github.com/vmware-tanzu/velero/pkg/volume" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" + "github.com/vmware-tanzu/velero/pkg/builder" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/metrics" persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" @@ -53,134 +55,49 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestBackupDeletionControllerProcessQueueItem(t *testing.T) { - client := fake.NewSimpleClientset() - sharedInformers := informers.NewSharedInformerFactory(client, 0) - - controller := NewBackupDeletionController( - velerotest.NewLogger(), - sharedInformers.Velero().V1().DeleteBackupRequests(), - client.VeleroV1(), // deleteBackupRequestClient - client.VeleroV1(), // backupClient - sharedInformers.Velero().V1().Restores().Lister(), - client.VeleroV1(), // restoreClient - NewBackupTracker(), - nil, // restic repository manager - sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - nil, - sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), - nil, // csiSnapshotLister - nil, // csiSnapshotContentLister - nil, // csiSnapshotClient - nil, // new plugin manager func - nil, // backupStoreGetter - metrics.NewServerMetrics(), - nil, // discovery helper - ).(*backupDeletionController) - - // Error splitting key - err := controller.processQueueItem("foo/bar/baz") - assert.Error(t, err) - - // Can't find DeleteBackupRequest - err = controller.processQueueItem("foo/bar") - assert.NoError(t, err) - - // Already processed - req := pkgbackup.NewDeleteBackupRequest("foo", "uid") - req.Namespace = "foo" - req.Name = "foo-abcde" - req.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - - err = controller.processQueueItem("foo/bar") - assert.NoError(t, err) - - // Invoke processRequestFunc - for _, phase := range []velerov1api.DeleteBackupRequestPhase{"", velerov1api.DeleteBackupRequestPhaseNew, velerov1api.DeleteBackupRequestPhaseInProgress} { - t.Run(fmt.Sprintf("phase=%s", phase), func(t *testing.T) { - req.Status.Phase = phase - sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(req) - - var errorToReturn error - var actual *velerov1api.DeleteBackupRequest - var called bool - controller.processRequestFunc = func(r *velerov1api.DeleteBackupRequest) error { - called = true - actual = r - return errorToReturn - } - - // No error - err = controller.processQueueItem("foo/foo-abcde") - require.True(t, called, "processRequestFunc wasn't called") - assert.Equal(t, err, errorToReturn) - assert.Equal(t, req, actual) - - // Error - errorToReturn = errors.New("bar") - err = controller.processQueueItem("foo/foo-abcde") - require.True(t, called, "processRequestFunc wasn't called") - assert.Equal(t, err, errorToReturn) - }) - } -} - type backupDeletionControllerTestData struct { - client *fake.Clientset fakeClient client.Client sharedInformers informers.SharedInformerFactory volumeSnapshotter *velerotest.FakeVolumeSnapshotter backupStore *persistencemocks.BackupStore - controller *backupDeletionController - req *velerov1api.DeleteBackupRequest + controller *backupDeletionReconciler + req ctrl.Request } -func setupBackupDeletionControllerTest(t *testing.T, objects ...runtime.Object) *backupDeletionControllerTestData { +func defaultTestDbr() *velerov1api.DeleteBackupRequest { req := pkgbackup.NewDeleteBackupRequest("foo", "uid") - req.Namespace = "velero" + req.Namespace = velerov1api.DefaultNamespace req.Name = "foo-abcde" + return req +} + +func setupBackupDeletionControllerTest(t *testing.T, req *velerov1api.DeleteBackupRequest, objects ...runtime.Object) *backupDeletionControllerTestData { var ( - client = fake.NewSimpleClientset(append(objects, req)...) - fakeClient = velerotest.NewFakeControllerRuntimeClient(t, objects...) - sharedInformers = informers.NewSharedInformerFactory(client, 0) + fakeClient = velerotest.NewFakeControllerRuntimeClient(t, append(objects, req)...) volumeSnapshotter = &velerotest.FakeVolumeSnapshotter{SnapshotsTaken: sets.NewString()} pluginManager = &pluginmocks.Manager{} backupStore = &persistencemocks.BackupStore{} ) data := &backupDeletionControllerTestData{ - client: client, fakeClient: fakeClient, - sharedInformers: sharedInformers, volumeSnapshotter: volumeSnapshotter, backupStore: backupStore, - controller: NewBackupDeletionController( + controller: NewBackupDeletionReconciler( velerotest.NewLogger(), - sharedInformers.Velero().V1().DeleteBackupRequests(), - client.VeleroV1(), // deleteBackupRequestClient - client.VeleroV1(), // backupClient - sharedInformers.Velero().V1().Restores().Lister(), - client.VeleroV1(), // restoreClient + fakeClient, NewBackupTracker(), nil, // restic repository manager - sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - fakeClient, - sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), - nil, // csiSnapshotLister - nil, // csiSnapshotContentLister - nil, // csiSnapshotClient - func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - NewFakeSingleObjectBackupStoreGetter(backupStore), metrics.NewServerMetrics(), nil, // discovery helper - ).(*backupDeletionController), - - req: req, + func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + NewFakeSingleObjectBackupStoreGetter(backupStore), + ), + req: ctrl.Request{NamespacedName: types.NamespacedName{Namespace: req.Namespace, Name: req.Name}}, } pluginManager.On("CleanupClients").Return(nil) - return data } @@ -201,59 +118,51 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, }, } - td := setupBackupDeletionControllerTest(t, location, backup) + td := setupBackupDeletionControllerTest(t, defaultTestDbr(), location, backup) td.controller.backupStoreGetter = &fakeErrorBackupStoreGetter{} - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(ctx, td.req) assert.NotNil(t, err) assert.True(t, strings.HasPrefix(err.Error(), "error getting the backup store")) }) t.Run("missing spec.backupName", func(t *testing.T) { - td := setupBackupDeletionControllerTest(t) - td.req.Spec.BackupName = "" + dbr := defaultTestDbr() + dbr.Spec.BackupName = "" + td := setupBackupDeletionControllerTest(t, dbr) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(ctx, td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"errors":["spec.backupName is required"],"phase":"Processed"}}`), - ), - } - - assert.Equal(t, expectedActions, td.client.Actions()) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "spec.backupName is required", res.Status.Errors[0]) }) t.Run("existing deletion requests for the backup are deleted", func(t *testing.T) { - td := setupBackupDeletionControllerTest(t) + input := defaultTestDbr() + td := setupBackupDeletionControllerTest(t, input) - // add the backup to the tracker so the execution of processRequest doesn't progress + // add the backup to the tracker so the execution of reconcile doesn't progress // past checking for an in-progress backup. this makes validation easier. - td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName) - - require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(td.req)) - + td.controller.backupTracker.Add(td.req.Namespace, input.Spec.BackupName) existing := &velerov1api.DeleteBackupRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: td.req.Namespace, Name: "bar", Labels: map[string]string{ - velerov1api.BackupNameLabel: td.req.Spec.BackupName, + velerov1api.BackupNameLabel: input.Spec.BackupName, }, }, Spec: velerov1api.DeleteBackupRequestSpec{ - BackupName: td.req.Spec.BackupName, + BackupName: input.Spec.BackupName, }, } - require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(existing)) - _, err := td.client.VeleroV1().DeleteBackupRequests(td.req.Namespace).Create(context.TODO(), existing, metav1.CreateOptions{}) + err := td.fakeClient.Create(context.TODO(), existing) require.NoError(t, err) - - require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add( + existing2 := &velerov1api.DeleteBackupRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: td.req.Namespace, @@ -265,206 +174,103 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Spec: velerov1api.DeleteBackupRequestSpec{ BackupName: "some-other-backup", }, - }, - )) - - assert.NoError(t, td.controller.processRequest(td.req)) - - expectedDeleteAction := core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - "bar", - ) - - // first action is the Create of an existing DBR for the backup as part of test data setup - // second action is the Delete of the existing DBR, which we're validating - // third action is the Patch of the DBR to set it to processed with an error - require.Len(t, td.client.Actions(), 3) - assert.Equal(t, expectedDeleteAction, td.client.Actions()[1]) + } + err = td.fakeClient.Create(context.TODO(), existing2) + require.NoError(t, err) + _, err = td.controller.Reconcile(context.TODO(), td.req) + assert.NoError(t, err) + // verify "existing" is deleted + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: existing.Namespace, + Name: existing.Name, + }, &velerov1api.DeleteBackupRequest{}) + + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + // verify "existing2" remains + assert.NoError(t, td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: existing2.Namespace, + Name: existing2.Name, + }, &velerov1api.DeleteBackupRequest{})) }) - t.Run("deleting an in progress backup isn't allowed", func(t *testing.T) { - td := setupBackupDeletionControllerTest(t) - - td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName) + dbr := defaultTestDbr() + td := setupBackupDeletionControllerTest(t, dbr) - err := td.controller.processRequest(td.req) + td.controller.backupTracker.Add(td.req.Namespace, dbr.Spec.BackupName) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"errors":["backup is still in progress"],"phase":"Processed"}}`), - ), - } - - assert.Equal(t, expectedActions, td.client.Actions()) - }) - - t.Run("patching to InProgress fails", func(t *testing.T) { - backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() - location := builder.ForBackupStorageLocation("velero", "default").Result() - - td := setupBackupDeletionControllerTest(t, location, backup) - - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("bad") - }) - - err := td.controller.processRequest(td.req) - assert.EqualError(t, err, "error patching DeleteBackupRequest: bad") - - expectedActions := []core.Action{ - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - backup.Namespace, - backup.Name, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"InProgress"}}`), - ), - } - assert.Equal(t, expectedActions, td.client.Actions()) - }) - - t.Run("patching backup to Deleting fails", func(t *testing.T) { - backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() - location := builder.ForBackupStorageLocation("velero", "default").Result() - - td := setupBackupDeletionControllerTest(t, location, backup) - - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, td.req, nil - }) - td.client.PrependReactor("patch", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("bad") - }) - - err := td.controller.processRequest(td.req) - assert.EqualError(t, err, "error patching Backup: bad") - - expectedActions := []core.Action{ - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - backup.Namespace, - backup.Name, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"InProgress"}}`), - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - backup.Namespace, - backup.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"Deleting"}}`), - ), - } - assert.Equal(t, expectedActions, td.client.Actions()) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "backup is still in progress", res.Status.Errors[0]) }) t.Run("unable to find backup", func(t *testing.T) { - td := setupBackupDeletionControllerTest(t) - err := td.controller.processRequest(td.req) - require.NoError(t, err) + td := setupBackupDeletionControllerTest(t, defaultTestDbr()) - expectedActions := []core.Action{ - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"errors":["backup not found"],"phase":"Processed"}}`), - ), - } + _, err := td.controller.Reconcile(context.TODO(), td.req) + require.NoError(t, err) - assert.Equal(t, expectedActions, td.client.Actions()) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "backup not found", res.Status.Errors[0]) }) - t.Run("unable to find backup storage location", func(t *testing.T) { backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() - td := setupBackupDeletionControllerTest(t, backup) + td := setupBackupDeletionControllerTest(t, defaultTestDbr(), backup) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"errors":["backup storage location default not found"],"phase":"Processed"}}`), - ), - } - - assert.Equal(t, expectedActions, td.client.Actions()) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "backup storage location default not found", res.Status.Errors[0]) }) t.Run("backup storage location is in read-only mode", func(t *testing.T) { backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result() - td := setupBackupDeletionControllerTest(t, location, backup) + td := setupBackupDeletionControllerTest(t, defaultTestDbr(), location, backup) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"errors":["cannot delete backup because backup storage location default is currently in read-only mode"],"phase":"Processed"}}`), - ), - } - - assert.Equal(t, expectedActions, td.client.Actions()) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "cannot delete backup because backup storage location default is currently in read-only mode", res.Status.Errors[0]) }) - t.Run("full delete, no errors", func(t *testing.T) { + + input := defaultTestDbr() + + // Clear out resource labels to make sure the controller adds them and does not + // panic when encountering a nil Labels map + // (https://github.com/vmware-tanzu/velero/issues/1546) + input.Labels = nil + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - restore1 := builder.ForRestore("velero", "restore-1").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result() - restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result() - restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1api.RestorePhaseCompleted).Backup("some-other-backup").Result() - - td := setupBackupDeletionControllerTest(t, backup, restore1, restore2, restore3) - - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore1) - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2) - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3) + restore1 := builder.ForRestore(velerov1api.DefaultNamespace, "restore-1").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result() + restore2 := builder.ForRestore(velerov1api.DefaultNamespace, "restore-2").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result() + restore3 := builder.ForRestore(velerov1api.DefaultNamespace, "restore-3").Phase(velerov1api.RestorePhaseCompleted).Backup("some-other-backup").Result() location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -481,8 +287,6 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, } - require.NoError(t, td.fakeClient.Create(context.Background(), location)) - snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, @@ -492,26 +296,10 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Provider: "provider-1", }, } - require.NoError(t, td.sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(snapshotLocation)) + td := setupBackupDeletionControllerTest(t, input, backup, restore1, restore2, restore3, location, snapshotLocation) - // Clear out req labels to make sure the controller adds them and does not - // panic when encountering a nil Labels map - // (https://github.com/vmware-tanzu/velero/issues/1546) - td.req.Labels = nil - - td.client.PrependReactor("get", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) td.volumeSnapshotter.SnapshotsTaken.Insert("snap-1") - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, td.req, nil - }) - - td.client.PrependReactor("patch", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) - snapshots := []*volume.Snapshot{ { Spec: volume.SnapshotSpec{ @@ -529,77 +317,56 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } - td.backupStore.On("GetBackupVolumeSnapshots", td.req.Spec.BackupName).Return(snapshots, nil) - td.backupStore.On("GetBackupContents", td.req.Spec.BackupName).Return(ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), nil) - td.backupStore.On("DeleteBackup", td.req.Spec.BackupName).Return(nil) + td.backupStore.On("GetBackupVolumeSnapshots", input.Spec.BackupName).Return(snapshots, nil) + td.backupStore.On("GetBackupContents", input.Spec.BackupName).Return(ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), nil) + td.backupStore.On("DeleteBackup", input.Spec.BackupName).Return(nil) td.backupStore.On("DeleteRestore", "restore-1").Return(nil) td.backupStore.On("DeleteRestore", "restore-2").Return(nil) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-name":"foo"}},"status":{"phase":"InProgress"}}`), - ), - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - types.MergePatchType, - []byte(`{"status":{"phase":"Deleting"}}`), - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("restores"), - td.req.Namespace, - "restore-1", - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("restores"), - td.req.Namespace, - "restore-2", - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"Processed"}}`), - ), - core.NewDeleteCollectionAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), - ), + // the dbr should be deleted + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + if err == nil { + t.Logf("status of the dbr: %s, errors in dbr: %v", res.Status.Phase, res.Status.Errors) } - velerotest.CompareActions(t, expectedActions, td.client.Actions()) + // backup CR, restore CR restore-1 and restore-2 should be deleted + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: backup.Name, + }, &velerov1api.Backup{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-1", + }, &velerov1api.Restore{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-2", + }, &velerov1api.Restore{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + // restore-3 should remain + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-3", + }, &velerov1api.Restore{}) + assert.Nil(t, err) + + td.backupStore.AssertCalled(t, "DeleteBackup", input.Spec.BackupName) + td.backupStore.AssertCalled(t, "DeleteRestore", "restore-1") + td.backupStore.AssertCalled(t, "DeleteRestore", "restore-2") // Make sure snapshot was deleted assert.Equal(t, 0, td.volumeSnapshotter.SnapshotsTaken.Len()) }) - t.Run("full delete, no errors, with backup name greater than 63 chars", func(t *testing.T) { backup := defaultBackup(). ObjectMeta( @@ -609,26 +376,24 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup.UID = "uid" backup.Spec.StorageLocation = "primary" - restore1 := builder.ForRestore("velero", "restore-1"). + restore1 := builder.ForRestore(backup.Namespace, "restore-1"). Phase(velerov1api.RestorePhaseCompleted). - Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). + Backup(backup.Name). Result() - restore2 := builder.ForRestore("velero", "restore-2"). + restore2 := builder.ForRestore(backup.Namespace, "restore-2"). Phase(velerov1api.RestorePhaseCompleted). - Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). + Backup(backup.Name). Result() - restore3 := builder.ForRestore("velero", "restore-3"). + restore3 := builder.ForRestore(backup.Namespace, "restore-3"). Phase(velerov1api.RestorePhaseCompleted). Backup("some-other-backup"). Result() - td := setupBackupDeletionControllerTest(t, backup, restore1, restore2, restore3) - td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) - td.req.Namespace = "velero" - td.req.Name = "foo-abcde" - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore1) - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2) - td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3) + dbr := pkgbackup.NewDeleteBackupRequest(backup.Name, "uid") + dbr.Namespace = velerov1api.DefaultNamespace + dbr.Name = "foo-abcde" + // Clear out resource labels to make sure the controller adds them + dbr.Labels = nil location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -644,7 +409,6 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, }, } - require.NoError(t, td.fakeClient.Create(context.Background(), location)) snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -655,23 +419,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Provider: "provider-1", }, } - require.NoError(t, td.sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(snapshotLocation)) - - // Clear out req labels to make sure the controller adds them - td.req.Labels = make(map[string]string) - - td.client.PrependReactor("get", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) - td.volumeSnapshotter.SnapshotsTaken.Insert("snap-1") - - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, td.req, nil - }) - - td.client.PrependReactor("patch", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) + td := setupBackupDeletionControllerTest(t, dbr, backup, restore1, restore2, restore3, location, snapshotLocation) snapshots := []*volume.Snapshot{ { @@ -690,83 +438,64 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } - td.backupStore.On("GetBackupVolumeSnapshots", td.req.Spec.BackupName).Return(snapshots, nil) - td.backupStore.On("GetBackupContents", td.req.Spec.BackupName).Return(ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), nil) - td.backupStore.On("DeleteBackup", td.req.Spec.BackupName).Return(nil) + td.backupStore.On("GetBackupVolumeSnapshots", dbr.Spec.BackupName).Return(snapshots, nil) + td.backupStore.On("GetBackupContents", dbr.Spec.BackupName).Return(ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), nil) + td.backupStore.On("DeleteBackup", dbr.Spec.BackupName).Return(nil) td.backupStore.On("DeleteRestore", "restore-1").Return(nil) td.backupStore.On("DeleteRestore", "restore-2").Return(nil) - err := td.controller.processRequest(td.req) + td.volumeSnapshotter.SnapshotsTaken.Insert("snap-1") + + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-name":"the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}},"status":{"phase":"InProgress"}}`), - ), - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - types.MergePatchType, - []byte(`{"status":{"phase":"Deleting"}}`), - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("restores"), - td.req.Namespace, - "restore-1", - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("restores"), - td.req.Namespace, - "restore-2", - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"Processed"}}`), - ), - core.NewDeleteCollectionAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), - ), + // the dbr should be deleted + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + if err == nil { + t.Logf("status of the dbr: %s, errors in dbr: %v", res.Status.Phase, res.Status.Errors) } - velerotest.CompareActions(t, expectedActions, td.client.Actions()) + // backup CR, restore CR restore-1 and restore-2 should be deleted + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: backup.Name, + }, &velerov1api.Backup{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-1", + }, &velerov1api.Restore{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-2", + }, &velerov1api.Restore{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + + // restore-3 should remain + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-3", + }, &velerov1api.Restore{}) + assert.Nil(t, err) // Make sure snapshot was deleted assert.Equal(t, 0, td.volumeSnapshotter.SnapshotsTaken.Len()) }) - t.Run("backup is not downloaded when there are no DeleteItemAction plugins", func(t *testing.T) { backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - td := setupBackupDeletionControllerTest(t, backup) + input := defaultTestDbr() + // Clear out resource labels to make sure the controller adds them and does not + // panic when encountering a nil Labels map + // (https://github.com/vmware-tanzu/velero/issues/1546) + input.Labels = nil location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -783,8 +512,6 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, } - require.NoError(t, td.fakeClient.Create(context.Background(), location)) - snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, @@ -794,26 +521,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Provider: "provider-1", }, } - require.NoError(t, td.sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(snapshotLocation)) - - // Clear out req labels to make sure the controller adds them and does not - // panic when encountering a nil Labels map - // (https://github.com/vmware-tanzu/velero/issues/1546) - td.req.Labels = nil - - td.client.PrependReactor("get", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) + td := setupBackupDeletionControllerTest(t, defaultTestDbr(), backup, location, snapshotLocation) td.volumeSnapshotter.SnapshotsTaken.Insert("snap-1") - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, td.req, nil - }) - - td.client.PrependReactor("patch", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) - snapshots := []*volume.Snapshot{ { Spec: volume.SnapshotSpec{ @@ -831,72 +541,43 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } - td.backupStore.On("GetBackupVolumeSnapshots", td.req.Spec.BackupName).Return(snapshots, nil) - td.backupStore.On("DeleteBackup", td.req.Spec.BackupName).Return(nil) + td.backupStore.On("GetBackupVolumeSnapshots", input.Spec.BackupName).Return(snapshots, nil) + td.backupStore.On("DeleteBackup", input.Spec.BackupName).Return(nil) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - td.backupStore.AssertNotCalled(t, "GetBackupContents", td.req.Spec.BackupName) - - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-name":"foo"}},"status":{"phase":"InProgress"}}`), - ), - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - types.MergePatchType, - []byte(`{"status":{"phase":"Deleting"}}`), - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"Processed"}}`), - ), - core.NewDeleteCollectionAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), - ), + td.backupStore.AssertNotCalled(t, "GetBackupContents", mock.Anything) + td.backupStore.AssertCalled(t, "DeleteBackup", input.Spec.BackupName) + + // the dbr should be deleted + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + if err == nil { + t.Logf("status of the dbr: %s, errors in dbr: %v", res.Status.Phase, res.Status.Errors) } - velerotest.CompareActions(t, expectedActions, td.client.Actions()) + // backup CR should be deleted + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: backup.Name, + }, &velerov1api.Backup{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) // Make sure snapshot was deleted assert.Equal(t, 0, td.volumeSnapshotter.SnapshotsTaken.Len()) }) - t.Run("backup is still deleted if downloading tarball fails for DeleteItemAction plugins", func(t *testing.T) { backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - td := setupBackupDeletionControllerTest(t, backup) + input := defaultTestDbr() + // Clear out resource labels to make sure the controller adds them and does not + // panic when encountering a nil Labels map + // (https://github.com/vmware-tanzu/velero/issues/1546) + input.Labels = nil location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -913,8 +594,6 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, } - require.NoError(t, td.fakeClient.Create(context.Background(), location)) - snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, @@ -924,26 +603,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Provider: "provider-1", }, } - require.NoError(t, td.sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(snapshotLocation)) - - // Clear out req labels to make sure the controller adds them and does not - // panic when encountering a nil Labels map - // (https://github.com/vmware-tanzu/velero/issues/1546) - td.req.Labels = nil - - td.client.PrependReactor("get", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) + td := setupBackupDeletionControllerTest(t, defaultTestDbr(), backup, location, snapshotLocation) td.volumeSnapshotter.SnapshotsTaken.Insert("snap-1") - td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, td.req, nil - }) - - td.client.PrependReactor("patch", "backups", func(action core.Action) (bool, runtime.Object, error) { - return true, backup, nil - }) - snapshots := []*volume.Snapshot{ { Spec: volume.SnapshotSpec{ @@ -961,218 +623,72 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } - td.backupStore.On("GetBackupVolumeSnapshots", td.req.Spec.BackupName).Return(snapshots, nil) - td.backupStore.On("GetBackupContents", td.req.Spec.BackupName).Return(nil, fmt.Errorf("error downloading tarball")) - td.backupStore.On("DeleteBackup", td.req.Spec.BackupName).Return(nil) + td.backupStore.On("GetBackupVolumeSnapshots", input.Spec.BackupName).Return(snapshots, nil) + td.backupStore.On("GetBackupContents", input.Spec.BackupName).Return(nil, fmt.Errorf("error downloading tarball")) + td.backupStore.On("DeleteBackup", input.Spec.BackupName).Return(nil) - err := td.controller.processRequest(td.req) + _, err := td.controller.Reconcile(context.TODO(), td.req) require.NoError(t, err) - expectedActions := []core.Action{ - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-name":"foo"}},"status":{"phase":"InProgress"}}`), - ), - core.NewGetAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - types.MergePatchType, - []byte(`{"status":{"phase":"Deleting"}}`), - ), - core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - td.req.Namespace, - td.req.Spec.BackupName, - ), - core.NewPatchAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - td.req.Name, - types.MergePatchType, - []byte(`{"status":{"phase":"Processed"}}`), - ), - core.NewDeleteCollectionAction( - velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), - td.req.Namespace, - pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), - ), + td.backupStore.AssertCalled(t, "GetBackupContents", input.Spec.BackupName) + td.backupStore.AssertCalled(t, "DeleteBackup", input.Spec.BackupName) + + // the dbr should be deleted + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + if err == nil { + t.Logf("status of the dbr: %s, errors in dbr: %v", res.Status.Phase, res.Status.Errors) } - velerotest.CompareActions(t, expectedActions, td.client.Actions()) + // backup CR should be deleted + err = td.fakeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: velerov1api.DefaultNamespace, + Name: backup.Name, + }, &velerov1api.Backup{}) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) // Make sure snapshot was deleted assert.Equal(t, 0, td.volumeSnapshotter.SnapshotsTaken.Len()) }) -} + t.Run("Expired request will be deleted if the status is processed", func(t *testing.T) { + expired := time.Date(2018, 4, 3, 12, 0, 0, 0, time.UTC) + input := defaultTestDbr() + input.CreationTimestamp = metav1.Time{ + Time: expired, + } + input.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed + td := setupBackupDeletionControllerTest(t, input) + td.backupStore.On("DeleteBackup", mock.Anything).Return(nil) + _, err := td.controller.Reconcile(context.TODO(), td.req) + require.NoError(t, err) -func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { - - now := time.Date(2018, 4, 4, 12, 0, 0, 0, time.UTC) - unexpired1 := time.Date(2018, 4, 4, 11, 0, 0, 0, time.UTC) - unexpired2 := time.Date(2018, 4, 3, 12, 0, 1, 0, time.UTC) - expired1 := time.Date(2018, 4, 3, 12, 0, 0, 0, time.UTC) - expired2 := time.Date(2018, 4, 3, 2, 0, 0, 0, time.UTC) - - tests := []struct { - name string - requests []*velerov1api.DeleteBackupRequest - expectedDeletions []string - }{ - { - name: "no requests", - }, - { - name: "older than max age, phase = '', don't delete", - requests: []*velerov1api.DeleteBackupRequest{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "name", - CreationTimestamp: metav1.Time{Time: expired1}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: "", - }, - }, - }, - }, - { - name: "older than max age, phase = New, don't delete", - requests: []*velerov1api.DeleteBackupRequest{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "name", - CreationTimestamp: metav1.Time{Time: expired1}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseNew, - }, - }, - }, - }, - { - name: "older than max age, phase = InProcess, don't delete", - requests: []*velerov1api.DeleteBackupRequest{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "name", - CreationTimestamp: metav1.Time{Time: expired1}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseInProgress, - }, - }, - }, - }, - { - name: "some expired, some not", - requests: []*velerov1api.DeleteBackupRequest{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "unexpired-1", - CreationTimestamp: metav1.Time{Time: unexpired1}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseProcessed, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "expired-1", - CreationTimestamp: metav1.Time{Time: expired1}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseProcessed, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "unexpired-2", - CreationTimestamp: metav1.Time{Time: unexpired2}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseProcessed, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "expired-2", - CreationTimestamp: metav1.Time{Time: expired2}, - }, - Status: velerov1api.DeleteBackupRequestStatus{ - Phase: velerov1api.DeleteBackupRequestPhaseProcessed, - }, - }, - }, - expectedDeletions: []string{"expired-1", "expired-2"}, - }, - } + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + assert.True(t, apierrors.IsNotFound(err), "Expected not found error, but actual value of error: %v", err) + td.backupStore.AssertNotCalled(t, "DeleteBackup", mock.Anything) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - client := fake.NewSimpleClientset() - fakeClient := velerotest.NewFakeControllerRuntimeClient(t) - sharedInformers := informers.NewSharedInformerFactory(client, 0) - - controller := NewBackupDeletionController( - velerotest.NewLogger(), - sharedInformers.Velero().V1().DeleteBackupRequests(), - client.VeleroV1(), // deleteBackupRequestClient - client.VeleroV1(), // backupClient - sharedInformers.Velero().V1().Restores().Lister(), - client.VeleroV1(), // restoreClient - NewBackupTracker(), - nil, - sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - fakeClient, - sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), - nil, // csiSnapshotLister - nil, // csiSnapshotContentLister - nil, // csiSnapshotClient - nil, // new plugin manager func - nil, // backupStoreGetter - metrics.NewServerMetrics(), - nil, // discovery helper, - ).(*backupDeletionController) - - fakeClock := &clock.FakeClock{} - fakeClock.SetTime(now) - controller.clock = fakeClock - - for i := range test.requests { - sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(test.requests[i]) - } + }) - controller.deleteExpiredRequests() + t.Run("Expired request will not be deleted if the status is not processed", func(t *testing.T) { + expired := time.Date(2018, 4, 3, 12, 0, 0, 0, time.UTC) + input := defaultTestDbr() + input.CreationTimestamp = metav1.Time{ + Time: expired, + } + input.Status.Phase = velerov1api.DeleteBackupRequestPhaseNew + td := setupBackupDeletionControllerTest(t, input) + td.backupStore.On("DeleteBackup", mock.Anything).Return(nil) - expectedActions := []core.Action{} - for _, name := range test.expectedDeletions { - expectedActions = append(expectedActions, core.NewDeleteAction(velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), "ns", name)) - } + _, err := td.controller.Reconcile(context.TODO(), td.req) + require.NoError(t, err) - velerotest.CompareActions(t, expectedActions, client.Actions()) - }) - } + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Equal(t, 1, len(res.Status.Errors)) + assert.Equal(t, "backup not found", res.Status.Errors[0]) + + }) } diff --git a/pkg/restic/common.go b/pkg/restic/common.go index e8648cd6cb..23c09e558e 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -17,6 +17,7 @@ limitations under the License. package restic import ( + "context" "fmt" "os" "strings" @@ -26,10 +27,10 @@ import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -242,18 +243,21 @@ type SnapshotIdentifier struct { // GetSnapshotsInBackup returns a list of all restic snapshot ids associated with // a given Velero backup. -func GetSnapshotsInBackup(backup *velerov1api.Backup, podVolumeBackupLister velerov1listers.PodVolumeBackupLister) ([]SnapshotIdentifier, error) { - selector := labels.Set(map[string]string{ - velerov1api.BackupNameLabel: label.GetValidName(backup.Name), - }).AsSelector() +func GetSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]SnapshotIdentifier, error) { + podVolumeBackups := &velerov1api.PodVolumeBackupList{} + options := &client.ListOptions{ + LabelSelector: labels.Set(map[string]string{ + velerov1api.BackupNameLabel: label.GetValidName(backup.Name), + }).AsSelector(), + } - podVolumeBackups, err := podVolumeBackupLister.List(selector) + err := kbClient.List(ctx, podVolumeBackups, options) if err != nil { return nil, errors.WithStack(err) } var res []SnapshotIdentifier - for _, item := range podVolumeBackups { + for _, item := range podVolumeBackups.Items { if item.Status.SnapshotID == "" { continue } diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 954defee0e..7f3e0c5032 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -17,6 +17,7 @@ limitations under the License. package restic import ( + "context" "os" "sort" "testing" @@ -28,8 +29,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -369,10 +368,8 @@ func TestGetSnapshotsInBackup(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { var ( - client = fake.NewSimpleClientset() - sharedInformers = informers.NewSharedInformerFactory(client, 0) - pvbInformer = sharedInformers.Velero().V1().PodVolumeBackups() - veleroBackup = &velerov1api.Backup{} + clientBuilder = velerotest.NewFakeControllerRuntimeClientBuilder(t) + veleroBackup = &velerov1api.Backup{} ) veleroBackup.Name = "backup-1" @@ -380,12 +377,11 @@ func TestGetSnapshotsInBackup(t *testing.T) { if test.longBackupNameEnabled { veleroBackup.Name = "the-really-long-backup-name-that-is-much-more-than-63-characters" } + clientBuilder.WithLists(&velerov1api.PodVolumeBackupList{ + Items: test.podVolumeBackups, + }) - for _, pvb := range test.podVolumeBackups { - require.NoError(t, pvbInformer.Informer().GetStore().Add(pvb.DeepCopy())) - } - - res, err := GetSnapshotsInBackup(veleroBackup, pvbInformer.Lister()) + res, err := GetSnapshotsInBackup(context.TODO(), veleroBackup, clientBuilder.Build()) assert.NoError(t, err) // sort to ensure good compare of slices diff --git a/pkg/test/fake_controller_runtime_client.go b/pkg/test/fake_controller_runtime_client.go index a1f20e9bfe..d1c1b6106f 100644 --- a/pkg/test/fake_controller_runtime_client.go +++ b/pkg/test/fake_controller_runtime_client.go @@ -28,6 +28,15 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) +func NewFakeControllerRuntimeClientBuilder(t *testing.T) *k8sfake.ClientBuilder { + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + err = corev1api.AddToScheme(scheme) + require.NoError(t, err) + return k8sfake.NewClientBuilder().WithScheme(scheme) +} + func NewFakeControllerRuntimeClient(t *testing.T, initObjs ...runtime.Object) client.Client { scheme := runtime.NewScheme() err := velerov1api.AddToScheme(scheme) From d449ca06efd6a6ea91c906f52e50a6a014d2de33 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Tue, 1 Feb 2022 11:11:56 -0500 Subject: [PATCH 112/366] design for enabling existing resource policy add design doc for existing resource policy Signed-off-by: Shubham Pampattiwar add use-cases and update non-goals Signed-off-by: Shubham Pampattiwar update approach-1 and add policy-action table Signed-off-by: Shubham Pampattiwar minor updates Signed-off-by: Shubham Pampattiwar fix typos Signed-off-by: Shubham Pampattiwar add CLI details Signed-off-by: Shubham Pampattiwar dump updateAll option Signed-off-by: Shubham Pampattiwar add implementation decision Signed-off-by: Shubham Pampattiwar --- design/existing-resource-policy_design.md | 262 ++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 design/existing-resource-policy_design.md diff --git a/design/existing-resource-policy_design.md b/design/existing-resource-policy_design.md new file mode 100644 index 0000000000..84b3fb086f --- /dev/null +++ b/design/existing-resource-policy_design.md @@ -0,0 +1,262 @@ +# Add support for `ExistingResourcePolicy` to restore API +## Abstract +Velero currently does not support any restore policy on kubernetes resources that are already present in-cluster. Velero skips over the restore of the resource if it already exists in the namespace/cluster irrespective of whether the resource present in the restore is the same or different from the one present on the cluster. It is desired that Velero gives the option to the user to decide whether or not the resource in backup should overwrite the one present in the cluster. + +## Background +As of Today, Velero will skip over the restoration of resources that already exist in the cluster. The current workflow followed by Velero is (Using a `service` that is backed up for example): +- Velero tries to attempt restore of the `service` +- Fetches the `service` from the cluster +- If the `service` exists then: + - Checks whether the `service` instance in the cluster is equal to the `service` instance present in backup + - If not equal then skips the restore of the `service` and adds a restore warning (except for [ServiceAccount objects](https://github.com/vmware-tanzu/velero/blob/574baeb3c920f97b47985ec3957debdc70bcd5f8/pkg/restore/restore.go#L1246)) + - If equal then skips the restore of the `service` and mentions that the restore of resource `service` is skipped in logs + +It is desired to add the functionality to specify whether or not to overwrite the instance of resource `service` in cluster with the one present in backup during the restore process. + +Related issue: https://github.com/vmware-tanzu/velero/issues/4066 + +## Goals +- Add support for `ExistingResourcePolicy` to restore API for Kubernetes resources. + +## Non Goals +- Change existing restore workflow for `ServiceAccount` objects +- Add support for `ExistingResourcePolicy` as `recreate` for Kubernetes resources. (Future scope feature) + +## Unrelated Proposals (Completely different functionalities than the one proposed in the design) +- Add support for `ExistingResourcePolicy` to restore API for Non-Kubernetes resources. +- Add support for `ExistingResourcePolicy` to restore API for `PersistentVolume` data. + +### Use-cases/Scenarios + +### A. Production Cluster - Backup Cluster: +Let's say you have a Backup Cluster which is identical to the Production Cluster. After some operations/usage/time the Production Cluster had changed itself, there might be new deployments, some secrets might have been updated. Now, this means that the Backup cluster will no longer be identical to the Production Cluster. In order to keep the Backup Cluster up to date/identical to the Production Cluster with respect to Kubernetes resources except PV data we would like to use Velero for scheduling new backups which would in turn help us update the Backup Cluster via Velero restore. + +Reference: https://github.com/vmware-tanzu/velero/issues/4066#issuecomment-954320686 + +### B. Help identify resource delta: +Here delta resources mean the resources restored by a previous backup, but they are no longer in the latest backup. Let's follow a sequence of steps to understand this scenario: +- Consider there are 2 clusters, Cluster A, which has 3 resources - P1, P2 and P3. +- Create a Backup1 from Cluster A which has P1, P2 and P3. +- Perform restore on a new Cluster B using Backup1. +- Now, Lets say in Cluster A resource P1 gets deleted and resource P2 gets updated. +- Create a new Backup2 with the new state of Cluster A, keep in mind Backup1 has P1, P2 and P3 while Backup2 has P2' and P3. +- So the Delta here is (|Cluster B - Backup2|), Delete P1 and Update P2. +- During Restore time we would want the Restore to help us identify this resource delta. + +Reference: https://github.com/vmware-tanzu/velero/pull/4613#issuecomment-1027260446 + +## High-Level Design +### Approach 1: Add a new spec field `existingResourcePolicy` to the Restore API +In this approach we do *not* change existing velero behavior. If the resource to restore in cluster is equal to the one backed up then do nothing following current Velero behavior. For resources that already exist in the cluster that are not equal to the resource in the backup (other than Service Accounts). We add a new optional spec field `existingResourcePolicy` which can have the following values: +1. `none`: This is the existing behavior, if Velero encounters a resource that already exists in the cluster, we simply +skip restoration. +2. `update`: This option would provide the following behavior. + - Unchanged resources: Velero would update the backup/restore labels on the unchanged resources, if labels patch fails Velero adds a restore error. + - Changed resources: Velero will first try to patch the changed resource, Now if the patch: + - succeeds: Then the in-cluster resource gets updated with the labels as well as the resource diff + - fails: Velero adds a restore warning and tries to just update the backup/restore labels on the resource, if the labels patch also fails then we add restore error. +3. `recreate`: If resource already exists, then Velero will delete it and recreate the resource. + +*Note:* The `recreate` option is a non-goal for this enhancement proposal, but it is considered as a future scope. +Another thing to highlight is that Velero will not be deleting any resources in any of the policy options proposed in +this design but Velero will patch the resources in `update` policy option. + +Example: +A. The following Restore will execute the `existingResourcePolicy` restore type `none` for the `services` and `deployments` present in the `velero-protection` namespace. + +``` +Kind: Restore + +… + +includeNamespaces: velero-protection +includeResources: + - services + - deployments +existingResourcePolicy: none + +``` + +B. The following Restore will execute the `existingResourcePolicy` restore type `update` for the `secrets` and `daemonsets` present in the `gdpr-application` namespace. +``` +Kind: Restore + +… +includeNamespaces: gdpr-application +includeResources: + - secrets + - daemonsets +existingResourcePolicy: update +``` + +### Approach 2: Add a new spec field `existingResourcePolicyConfig` to the Restore API +In this approach we give user the ability to specify which resources are to be included for a particular kind of force update behaviour, essentially a more granular approach where in the user is able to specify a resource:behaviour mapping. It would look like: +`existingResourcePolicyConfig`: +- `patch:` + - `includedResources:` [ ]string +- `recreate:` + - `includedResources:` [ ]string + +*Note:* +- There is no `none` behaviour in this approach as that would conform to the current/default Velero restore behaviour. +- The `recreate` option is a non-goal for this enhancement proposal, but it is considered as a future scope. + + +Example: +A. The following Restore will execute the restore type `patch` and apply the `existingResourcePolicyConfig` for `secrets` and `daemonsets` present in the `inventory-app` namespace. +``` +Kind: Restore +… +includeNamespaces: inventory-app +existingResourcePolicyConfig: + patch: + includedResources + - secrets + - daemonsets + +``` + + +### Approach 3: Combination of Approach 1 and Approach 2 + +Now, this approach is somewhat a combination of the aforementioned approaches. Here we propose addition of two spec fields to the Restore API - `existingResourceDefaultPolicy` and `existingResourcePolicyOverrides`. As the names suggest ,the idea being that `existingResourceDefaultPolicy` would describe the default velero behaviour for this restore and `existingResourcePolicyOverrides` would override the default policy explicitly for some resources. + +Example: +A. The following Restore will execute the restore type `patch` as the `existingResourceDefaultPolicy` but will override the default policy for `secrets` using the `existingResourcePolicyOverrides` spec as `none`. +``` +Kind: Restore +… +includeNamespaces: inventory-app +existingResourceDefaultPolicy: patch +existingResourcePolicyOverrides: + none: + includedResources + - secrets + +``` + +## Detailed Design +### Approach 1: Add a new spec field `existingResourcePolicy` to the Restore API +The `existingResourcePolicy` spec field will be an `PolicyType` type field. + +Restore API: +``` +type RestoreSpec struct { +. +. +. +// ExistingResourcePolicy specifies the restore behaviour for the kubernetes resource to be restored +// +optional +ExistingResourcePolicy PolicyType + +} +``` +PolicyType: +``` +type PolicyType string +const PolicyTypeNone PolicyType = "none" +const PolicyTypePatch PolicyType = "update" +``` + +### Approach 2: Add a new spec field `existingResourcePolicyConfig` to the Restore API +The `existingResourcePolicyConfig` will be a spec of type `PolicyConfiguration` which gets added to the Restore API. + +Restore API: +``` +type RestoreSpec struct { +. +. +. +// ExistingResourcePolicyConfig specifies the restore behaviour for a particular/list of kubernetes resource(s) to be restored +// +optional +ExistingResourcePolicyConfig []PolicyConfiguration + +} +``` + +PolicyConfiguration: +``` +type PolicyConfiguration struct { + +PolicyTypeMapping map[PolicyType]ResourceList + +} +``` + +PolicyType: +``` +type PolicyType string +const PolicyTypePatch PolicyType = "patch" +const PolicyTypeRecreate PolicyType = "recreate" +``` + +ResourceList: +``` +type ResourceList struct { +IncludedResources []string +} +``` + +### Approach 3: Combination of Approach 1 and Approach 2 + +Restore API: +``` +type RestoreSpec struct { +. +. +. +// ExistingResourceDefaultPolicy specifies the default restore behaviour for the kubernetes resource to be restored +// +optional +existingResourceDefaultPolicy PolicyType + +// ExistingResourcePolicyOverrides specifies the restore behaviour for a particular/list of kubernetes resource(s) to be restored +// +optional +existingResourcePolicyOverrides []PolicyConfiguration + +} +``` + +PolicyType: +``` +type PolicyType string +const PolicyTypeNone PolicyType = "none" +const PolicyTypePatch PolicyType = "patch" +const PolicyTypeRecreate PolicyType = "recreate" +``` +PolicyConfiguration: +``` +type PolicyConfiguration struct { + +PolicyTypeMapping map[PolicyType]ResourceList + +} +``` +ResourceList: +``` +type ResourceList struct { +IncludedResources []string +} +``` + +The restore workflow changes will be done [here](https://github.com/vmware-tanzu/velero/blob/b40bbda2d62af2f35d1406b9af4d387d4b396839/pkg/restore/restore.go#L1245) + +### CLI changes for Approach 1 +We would introduce a new CLI flag called `existing-resource-policy` of string type. This flag would be used to accept the +policy from the user. The velero restore command would look somewhat like this: +``` +velero create restore --existing-resource-policy=update +``` + +Help message `Restore Policy to be used during the restore workflow, can be - none, update` + +The CLI changes will go at `pkg/cmd/cli/restore/create.go` + +We would also add a validation which checks for invalid policy values provided to this flag. + +Restore describer will also be updated to reflect the policy `pkg/cmd/util/output/restore_describer.go` + +### Implementation Decision +We have decided to go ahead with the implementation of Approach 1 as: +- It is easier to implement +- It is also easier to scale and leaves room for improvement and the door open to expanding to approach 3 +- It also provides an option to preserve the existing velero restore workflow \ No newline at end of file From 3589ab57e2bd23be98ae6b6ce27eb9cebcaec9e7 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Fri, 4 Feb 2022 13:52:27 -0500 Subject: [PATCH 113/366] Add multiple label selector support to Velero backup /restore API add initial design for enabling multiple label support for backing up resources Signed-off-by: Shubham Pampattiwar update solution approach Signed-off-by: Shubham Pampattiwar add restore API bits Signed-off-by: Shubham Pampattiwar remove CLI bits and update examples Signed-off-by: Shubham Pampattiwar update spec name to OrLabelSelector Signed-off-by: Shubham Pampattiwar update high level design Signed-off-by: Shubham Pampattiwar --- design/multiple-label-selectors_design.md | 138 ++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 design/multiple-label-selectors_design.md diff --git a/design/multiple-label-selectors_design.md b/design/multiple-label-selectors_design.md new file mode 100644 index 0000000000..bb698cb480 --- /dev/null +++ b/design/multiple-label-selectors_design.md @@ -0,0 +1,138 @@ +# Ensure support for backing up resources based on multiple labels +## Abstract +As of today Velero supports filtering of resources based on single label selector per backup. It is desired that Velero +support backing up of resources based on multiple labels (OR logic). + +**Note:** This solution is required because kubernetes label selectors only allow AND logic of labels. + +## Background +Currently, Velero's Backup/Restore API has a spec field `LabelSelector` which helps in filtering of resources based on +a **single** label value per backup/restore request. For instance, if the user specifies the `Backup.Spec.LabelSelector` as +`data-protection-app: true`, Velero will grab all the resources that possess this label and perform the backup +operation on them. The `LabelSelector` field does not accept more than one labels, and thus if the user want to take +backup for resources consisting of a label from a set of labels (label1 OR label2 OR label3) then the user needs to +create multiple backups per label rule. It would be really useful if Velero Backup API could respect a set of +labels (OR Rule) for a single backup request. + +Related Issue: https://github.com/vmware-tanzu/velero/issues/1508 + +## Goals +- Enable support for backing up resources based on multiple labels (OR Logic) in a single backup config. +- Enable support for restoring resources based on multiple labels (OR Logic) in a single restore config. + +## Use Case/Scenario +Let's say as a Velero user you want to take a backup of secrets, but all these secrets do not have one single consistent +label on them. We want to take backup of secrets having any one label in `app=gdpr`, `app=wpa` and `app=ccpa`. Here +we would have to create 3 instances of backup for each label rule. This can become cumbersome at scale. + +## High-Level Design +### Addition of `OrLabelSelectors` spec to Velero Backup/Restore API +For Velero to back up resources if they consist of any one label from a set of labels, we would like to add a new spec +field `OrLabelSelectors` which would enable user to specify them. The Velero backup would somewhat look like: + +``` +apiVersion: velero.io/v1 +kind: Backup +metadata: + name: backup-101 + namespace: openshift-adp +spec: + includedNamespaces: + - test + storageLocation: velero-sample-1 + ttl: 720h0m0s + orLabelSelectors: + - matchLabels: + app=gdpr + - matchLabels: + app=wpa + - matchLabels: + app=ccpa +``` + +**Note:** This approach will **not** be changing any current behavior related to Backup API spec `LabelSelector`. Rather we +propose that the label in `LabelSelector` spec and labels in `OrLabelSelectors` should be treated as different Velero functionalities. +Both these fields will be treated as separate Velero Backup API specs. If `LabelSelector` (singular) is present then just match that label. +And if `OrLabelSelectors` is present then match to any label in the set specified by the user. For backup case, if both the `LabelSelector` and `OrLabelSelectors` +are specified (we do not anticipate this as a real world use-case) then the `OrLabelSelectors` will take precedence, `LabelSelector` will +only be used to filter only when `OrLabelSelectors` is not specified by the user. This helps to keep both spec behaviour independent and not confuse the users. +This way we preserve the existing Velero behaviour and implement the new functionality in a much cleaner way. +For instance, let's take a look the following cases: + +1. Only `LabelSelector` specified: Velero will create a backup with resources matching label `app=protect-db` +``` +apiVersion: velero.io/v1 +kind: Backup +metadata: + name: backup-101 + namespace: openshift-adp +spec: + includedNamespaces: + - test + storageLocation: velero-sample-1 + ttl: 720h0m0s + labelSelector: + - matchLabels: + app=gdpr +``` +2. Only `OrLabelSelectors` specified: Velero will create a backup with resources matching any label from set `{app=gdpr, app=wpa, app=ccpa}` +``` +apiVersion: velero.io/v1 +kind: Backup +metadata: + name: backup-101 + namespace: openshift-adp +spec: + includedNamespaces: + - test + storageLocation: velero-sample-1 + ttl: 720h0m0s + orLabelSelectors: + - matchLabels: + app=gdpr + - matchLabels: + app=wpa + - matchLabels: + app=ccpa +``` + +Similar implementation will be done for the Restore API as well. + +## Detailed Design +With the Introduction of `OrLabelSelectors` the BackupSpec and RestoreSpec will look like: + +BackupSpec: +``` +type BackupSpec struct { +[...] +// OrLabelSelectors is a set of []metav1.LabelSelector to filter with +// when adding individual objects to the backup. Resources matching any one +// label from the set of labels will be added to the backup. If empty +// or nil, all objects are included. Optional. +// +optional +OrLabelSelectors []\*metav1.LabelSelector +[...] +} +``` + +RestoreSpec: +``` +type RestoreSpec struct { +[...] +// OrLabelSelectors is a set of []metav1.LabelSelector to filter with +// when restoring objects from the backup. Resources matching any one +// label from the set of labels will be restored from the backup. If empty +// or nil, all objects are included from the backup. Optional. +// +optional +OrLabelSelectors []\*metav1.LabelSelector +[...] +} +``` + +The logic to collect resources to be backed up for a particular backup will be updated in the `backup/item_collector.go` +around [here](https://github.com/vmware-tanzu/velero/blob/574baeb3c920f97b47985ec3957debdc70bcd5f8/pkg/backup/item_collector.go#L294). + +And for filtering the resources to be restored, the changes will go [here](https://github.com/vmware-tanzu/velero/blob/d1063bda7e513150fd9ae09c3c3c8b1115cb1965/pkg/restore/restore.go#L1769) + +**Note:** +- This feature will not be exposed via Velero CLI. \ No newline at end of file From ad6c5a5ad6940d92f9df0203b8c62581216ae1df Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 28 Apr 2022 14:47:37 +0800 Subject: [PATCH 114/366] Change the name of UT to be more consistent Signed-off-by: Daniel Jiang --- pkg/controller/backup_deletion_controller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 816f906276..d2de589b24 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -101,7 +101,7 @@ func setupBackupDeletionControllerTest(t *testing.T, req *velerov1api.DeleteBack return data } -func TestBackupDeletionControllerProcessRequest(t *testing.T) { +func TestBackupDeletionControllerReconcile(t *testing.T) { t.Run("failed to get backup store", func(t *testing.T) { backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() location := &velerov1api.BackupStorageLocation{ From 3049f6ec8c0b226a795d996fedad54f2634a3931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Thu, 28 Apr 2022 17:04:04 +0800 Subject: [PATCH 115/366] Bump up the version of library go-plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump up the version of library go-plugin to avoid blocking issue #4863 Signed-off-by: Wenkai Yin(尹文开) --- go.mod | 4 ++-- go.sum | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 2c7517d35f..a275ccd20e 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 github.com/google/uuid v1.2.0 - github.com/hashicorp/go-hclog v0.12.0 - github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 + github.com/hashicorp/go-hclog v0.14.1 + github.com/hashicorp/go-plugin v1.4.3 github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/onsi/ginkgo v1.16.5 diff --git a/go.sum b/go.sum index 5be785c5fc..9ffaee969d 100644 --- a/go.sum +++ b/go.sum @@ -379,15 +379,15 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 h1:sADP8l/FAtMyWJ9GIcQT/04Ae80ZZ75ogOrtW0DIZhc= -github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -417,6 +417,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= @@ -762,6 +764,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -848,7 +851,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1054,6 +1056,7 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1109,7 +1112,7 @@ google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwy google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 h1:z+ErRPu0+KS02Td3fOAgdX+lnPDh/VyaABEJPD4JRQs= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= From 19c3bf5c375c13e16c62bf4f6986613e9feeb820 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Thu, 28 Apr 2022 15:25:20 +0300 Subject: [PATCH 116/366] Update ROADMAP.md with link to wiki and wiki guidance Signed-off-by: OrlinVasilev --- ROADMAP.md | 43 +------------------------------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 7bf9fc7470..798acf5883 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,42 +1 @@ -## Velero Roadmap - -### About this document -This document provides a link to the [Velero Project boards](https://github.com/vmware-tanzu/velero/projects) that serves as the up to date description of items that are in the release pipeline. The release boards have separate swim lanes based on prioritization. Most items are gathered from the community or include a feedback loop with the community. This should serve as a reference point for Velero users and contributors to understand where the project is heading, and help determine if a contribution could be conflicting with a longer term plan. - -### How to help? -Discussion on the roadmap can take place in threads under [Issues](https://github.com/vmware-tanzu/velero/issues) or in [community meetings](https://velero.io/community/). Please open and comment on an issue if you want to provide suggestions, use cases, and feedback to an item in the roadmap. Please review the roadmap to avoid potential duplicated effort. - -### How to add an item to the roadmap? -One of the most important aspects in any open source community is the concept of proposals. Large changes to the codebase and / or new features should be preceded by a [proposal](https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md#proposal-process) in our repo. -For smaller enhancements, you can open an issue to track that initiative or feature request. -We work with and rely on community feedback to focus our efforts to improve Velero and maintain a healthy roadmap. - -### Current Roadmap -The following table includes the current roadmap for Velero. If you have any questions or would like to contribute to Velero, please attend a [community meeting](https://velero.io/community/) to discuss with our team. If you don't know where to start, we are always looking for contributors that will help us reduce technical, automation, and documentation debt. -Please take the timelines & dates as proposals and goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions, etc. If you depend on a specific item, we encourage you to attend community meetings to get updated status information, or help us deliver that feature by contributing to Velero. - -`Last Updated: October 2021` - -#### 1.8.0 Roadmap (to be delivered January/February 2021) - -|Issue|Description|Timeline|Notes| -|---|---|---|---| -|[4108](https://github.com/vmware-tanzu/velero/issues/4108), [4109](https://github.com/vmware-tanzu/velero/issues/4109)|Solution for CSI - Azure and AWS|2022 H1|Currently, Velero plugins for AWS and Azure cannot back up persistent volumes that were provisioned using the CSI driver. This will fix that.| -|[3229](https://github.com/vmware-tanzu/velero/issues/3229),[4112](https://github.com/vmware-tanzu/velero/issues/4112)|Moving data mover functionality from the Velero Plugin for vSphere into Velero proper|2022 H1|This work is a precursor to decoupling the Astrolabe snapshotting infrastructure.| -|[3533](https://github.com/vmware-tanzu/velero/issues/3533)|Upload Progress Monitoring|2022 H1|Finishing up the work done in the 1.7 timeframe. The data mover work depends on this.| -|[1975](https://github.com/vmware-tanzu/velero/issues/1975)|Test dual stack mode|2022 H1|We already tested IPv6, but we want to confirm that dual stack mode works as well.| -|[2082](https://github.com/vmware-tanzu/velero/issues/2082)|Delete Backup CRs on removing target location. |2022 H1|| -|[3516](https://github.com/vmware-tanzu/velero/issues/3516)|Restore issue with MutatingWebhookConfiguration v1beta1 API version|2022 H1|| -|[2308](https://github.com/vmware-tanzu/velero/issues/2308)|Restoring nodePort service that has nodePort preservation always fails if service already exists in the namespace|2022 H1|| -|[4115](https://github.com/vmware-tanzu/velero/issues/4115)|Support for multiple set of credentials for VolumeSnapshotLocations|2022 H1|| -|[1980](https://github.com/vmware-tanzu/velero/issues/1980)|Velero triggers backup immediately for scheduled backups|2022 H1|| -|[4067](https://github.com/vmware-tanzu/velero/issues/4067)|Pre and post backup and restore hooks|2022 H1|| -|[3742](https://github.com/vmware-tanzu/velero/issues/3742)|Carvel packaging for Velero for vSphere|2022 H1|AWS and Azure have been completed already.| -|[3285](https://github.com/vmware-tanzu/velero/issues/3285)|Design doc for Velero plugin versioning|2022 H1|| -|[4231](https://github.com/vmware-tanzu/velero/issues/4231)|Technical health (prioritizing giving developers confidence and saving developers time)|2022 H1|More automated tests (especially the pre-release manual tests) and more automation of the running of tests.| -|[4110](https://github.com/vmware-tanzu/velero/issues/4110)|Solution for CSI - GCP|2022 H1|Currently, the Velero plugin for GCP cannot back up persistent volumes that were provisioned using the CSI driver. This will fix that.| -|[3742](https://github.com/vmware-tanzu/velero/issues/3742)|Carvel packaging for Velero for restic|2022 H1|AWS and Azure have been completed already.| -|[3454](https://github.com/vmware-tanzu/velero/issues/3454),[4134](https://github.com/vmware-tanzu/velero/issues/4134),[4135](https://github.com/vmware-tanzu/velero/issues/4135)|Kubebuilder tech debt|2022 H1|| -|[4111](https://github.com/vmware-tanzu/velero/issues/4111)|Ignore items returned by ItemSnapshotter.AlsoHandles during backup|2022 H1|This will enable backup of complex objects, because we can then tell Velero to ignore things that were already backed up when Velero was previously called recursively.| - -Other work may make it into the 1.8 release, but this is the work that will be prioritized first. \ No newline at end of file +# Please go to the [Velero Wiki](https://github.com/vmware-tanzu/velero/wiki/) to see our latest roadmap, archived roadmaps and roadmap guidance. \ No newline at end of file From bc15fe8d9ca76270339c1509a70f5addd8670750 Mon Sep 17 00:00:00 2001 From: Ning Ding <834652870@qq.com> Date: Thu, 28 Apr 2022 20:24:12 +0800 Subject: [PATCH 117/366] cleanup restic helper folder when done Signed-off-by: Ning Ding <834652870@qq.com> --- .../velero-restic-restore-helper.go | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go b/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go index 65f5e4a60f..d9fdfccad7 100644 --- a/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go +++ b/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go @@ -38,6 +38,11 @@ func main() { case <-ticker.C: if done() { fmt.Println("All restic restores are done") + err := removeFolder() + if err != nil { + fmt.Println(err) + } + fmt.Println("Cleanup .velero folder") return } } @@ -75,3 +80,29 @@ func done() bool { return true } + +// remove .velero folder +func removeFolder() error { + children, err := ioutil.ReadDir("/restores") + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR reading /restores directory: %s\n", err) + return err + } + + for _, child := range children { + if !child.IsDir() { + fmt.Printf("%s is not a directory, skipping.\n", child.Name()) + continue + } + + donePath := filepath.Join("/restores", child.Name(), ".velero") + + err = os.RemoveAll(donePath) + if err != nil { + return err + } + fmt.Printf("Deleted %s", donePath) + } + + return nil +} From 06d3d731ed078b9c759bdcc6bd4a8bac3ffee964 Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 25 Apr 2022 17:48:38 +0800 Subject: [PATCH 118/366] Refactor restic repository Signed-off-by: Ming --- changelogs/unreleased/4859-qiuming-best | 1 + .../bases/velero.io_resticrepositories.yaml | 11 +- config/crd/v1/bases/velero.io_schedules.yaml | 4 - config/crd/v1/crds/crds.go | 4 +- config/rbac/role.yaml | 20 ++ ...pository.go => restic_repository_types.go} | 13 +- pkg/apis/velero/v1/schedule_types.go | 1 - pkg/cmd/server/server.go | 21 +- .../restic_repository_controller.go | 234 +++++++----------- .../restic_repository_controller_test.go | 198 +++++++++++++++ pkg/restic/mocks/repository_manager.go | 147 +++++++++++ 11 files changed, 481 insertions(+), 173 deletions(-) create mode 100644 changelogs/unreleased/4859-qiuming-best rename pkg/apis/velero/v1/{restic_repository.go => restic_repository_types.go} (78%) create mode 100644 pkg/controller/restic_repository_controller_test.go create mode 100644 pkg/restic/mocks/repository_manager.go diff --git a/changelogs/unreleased/4859-qiuming-best b/changelogs/unreleased/4859-qiuming-best new file mode 100644 index 0000000000..f92fc7fa00 --- /dev/null +++ b/changelogs/unreleased/4859-qiuming-best @@ -0,0 +1 @@ +Convert Restic Repository resource/controller to the Kubebuilder framework diff --git a/config/crd/v1/bases/velero.io_resticrepositories.yaml b/config/crd/v1/bases/velero.io_resticrepositories.yaml index 65ff08eb36..27c97c7fb7 100644 --- a/config/crd/v1/bases/velero.io_resticrepositories.yaml +++ b/config/crd/v1/bases/velero.io_resticrepositories.yaml @@ -16,9 +16,16 @@ spec: singular: resticrepository scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: + description: TODO(2.0) After converting all resources to use the runtime-controller + client, the genclient and k8s:deepcopy markers will no longer be needed + and should be removed. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -81,6 +88,8 @@ spec: type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index 9fe76d7b56..b090c08c0f 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -17,10 +17,6 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: Name of the schedule - jsonPath: .metadata.name - name: Name - type: string - description: Status of the schedule jsonPath: .status.phase name: Status diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 54838c3551..325c9a6d30 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -35,9 +35,9 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWty\x0f\xceV\r\xa9\xddI*I\xe9\xb6kgSJv=\xae\x913\x97\xa99@DS\xec\x98\x04\x18\xa0)YI忧\x1a \xf4\xa4\x1ev\xd5Lx\xb1\x85G\xe3\xeb\xaf\x1f\xe8&GY\x96\x8dTK\x9f\xd0y\xb2f\x02\xaa%|a4\xf2\xcb\xe7\xcf\u007f\xf69\xd9\xf1\xf2\xc7\xd13\x19=\x81\xbbγm>\xa2\xb7\x9d+\xf0\x1eK2\xc4dͨAVZ\xb1\x9a\x8c\x00\x941\x96\x95\f{\xf9\tPX\xc3\xce\xd65\xbal\x81&\u007f\xee\xe68\xef\xa8\xd6\xe8\x82\xf0t\xf4\xf2\x87\xfcO\xf9\x0f#\x80\xc2a\xd8\xfeD\rzVM;\x01\xd3\xd5\xf5\b\xc0\xa8\x06'\xd0Z\xbd\xb4uנC\xcf֡ϗX\xa3\xb39ّo\xb1\x90S\x17\xcev\xed\x04\xb6\x13qs\x8f(j\xf3h\xf5\xa7 \xe7c\x94\x13\xa6j\xf2\xfc\xf7\xc1\xe9_\xc9sX\xd2֝S\xf5\x00\x8e0\xeb\xc9,\xbaZ\xb9\xe3\xf9\x11\x80/l\x8b\x13x\x10(\xad*P\x8f\x00z\x02\x02\xb4\xacWq\xf9c\x94UTب\x88\x19\xc0\xb6h~z\x9c~\xfa\xfdlo\x18\xa0u\xb6EǔԋώYwF\x014\xfa\xc2Qˁ\xf4[\x11\x18W\x81\x16{\xa2\a\xae0\x81B\xddc\x00[\x02W\xe4\xc1a\xebУ\x89\x16\xde\x13\f\xb2H\x19\xb0\xf3\u007fb\xc19\xccЉ\x18\xf0\x95\xedj-n\xb0D\xc7ర\vC\xff\xde\xc8\xf6\xc06\x1cZ+ƞ\xe3\xedC\x86\xd1\x19U\xc3R\xd5\x1d\xbe\x03e44j\r\x0e\xe5\x14\xe8̎\xbc\xb0\xc4\xe7\xf0\x9bu\bdJ;\x81\x8a\xb9\xf5\x93\xf1xA\x9cܹ\xb0M\xd3\x19\xe2\xf58x&\xcd;\xb6Ώ5.\xb1\x1e{Zd\xca\x15\x151\x16\xdc9\x1c\xab\x96\xb2\x00\xdd\x04\x97\xce\x1b\xfd\x9d\xeb\x03\xc0\xdf\xeea\xe5\xb5\xd8ֳ#\xb3ؙ\b\xcev\xc6\x02\xe2m@\x1eT\xbf5j\xb1%Z\x86\x84\x9d\x8f\u007f\x99=A::\x18\xe3\x90\xfd\xc0\xfbv\xa3ߚ@\b#S\xa2\x8bF,\x9dm\x82L4\xba\xb5d8\xfc(jBsH\xbf\xef\xe6\r\xb1\xd8\xfd_\x1dz\x16[\xe5p\x17b\x1c\xe6\b]\xab\x15\xa3\xceaj\xe0N5X\xdf)\x8f_\xdd\x00´τ\xd8\xebL\xb0\x9b\x9e\x0e\x17G\xd6v&R\n9a\xafô0k\xb1\x10\xf3\t\x83\xb2\x95J*Bl@i\x1d\xa8\xa3\xf5\xf9\x9e\xe8\xe1Еg\xae\x8a箝\xb1uj\x81\xbf\xda(\xf3p\xd1\x01\xb6\x9f\x87\xf6$p\x92Yb\x18c/\x1c|\\y$\x14\xa0N\x9bW\x15:\f{$\x8bQ!\xeee=\xb1uk\x11\x1cT\xd2\xf9\x91\x84\x13\x86\b*[}A\x8dG\xdb\a\x84\xc3\x12\x1d\x1aq\xf7\x98!Z\x1b\xf2\b+2),b\x8a\x05\xb6\x03Z\xcc#\xeaa\x88\xa7\xa9\x873\xd9s\x10\xf0O\x8fӔ1\x13\xc3=t>>\xf7\x02=\U00094135~T\\]q\xf6\xed\xb4\x8c\x87\x85\xdc\xc1\x16\x14\xb4\x84\x05\xee%c \xe3\x19\x95\x06[\x0eJ\x94[\x1b$\xc0\x1c\xf6;\xde\xc5Lѧ\xa4m\n\x17\xeaAI\x8e\"\r\u007f\x9b}x\x18\xffu\x88\xf9\x8d\x16\xa0\x8a\x02\xbd\bR\x8c\r\x1a~\a\xbe+*P^\xd4 \x87z&3y\xa3\f\x95\xe89\xef\xcf@\xe7?\xbf\xff2\xcc\x1e\xc0/\xd6\x01\xbe\xa8\xa6\xad\xf1\x1dPd|\x93\xfe\x92ϐ\x8ftl$\u008a\xb8\xa2\xc3KkÀxW\xaf\xf6*\xa8\xcb\xea\x19\xc1\xf6\xeav\b5=\xe3\x04n$\xcaw`\xfeG\x02\xeb\xbf7'\xa4\xfe.\x06Ѝ,\xba\x89\xe06\xf7\xddnDnAr\xa5\x18\xd8\xd1b\x81.\x14\bCOHޒ\x12\xbf\a\xeb\x84\x01cwD\x04\xc1b\xbd\x98\x8fP\x1f\x81\xfe\xfc\xfe\xcbI\xc4\xfb|\x01\x19\x8d/\xf0\x1e\xc8DnZ\xab\xbf\xcf\xe1)x\xc7ڰz\x91\x93\x8a\xcaz<Ŭ5\xf5Zt\xae\xd4\x12\xc1\xdb\x06a\x85u\x9d\xc5zC\xc3J\xad\x85\x85d8\xf17\x05\xadr|\xd6[S\x95\xf1\xf4\xe1\xfe\xc3$\"\x13\x87Z\x84|'\xb7SIR5H\xb9\x10\xef\xbc\xe0\x8dG\x97fz|\x17݇-\x14\x952\v\x8c\xfa\"\x94\x9d\xdcB\xf9\xed[\xe2\xf8\xf8\xeaO\xcf@\tp\x988\xfeo\x97\xe8\x95ʅJ\xf5\n\xe5\x1ev\xbc\xfc\xacr\xd2\x188\x83\x8cA?m\v/\xaa\x15ز\x1f\xdb%\xba%\xe1j\xbc\xb2\xee\x99\xcc\"\x13\xd7̢\x0f\xf8q(\xed\xc7߅?o\xd6%\x14\xe4\xd7*\x14\x16\u007f\v\xad\xe4\x1c?~\x93R\xa9V\xbc\xfe\x1e\xbb\x9d\xf5\x05\xcc\xe1^\t\x8bUEE\x95\x9a\x80>Ǟ\b&\x92\x8aS\xc7Ԭ\xcc\xfa\xab\xbb\xb2\x10\xda9A\xb4\xce\xfan3SF\xcb\xff\x9e<\xcb\xf8\x9b\x18\xec\xe8\xaa\xf0\xfd\xc7\xf4\xfe\xdb8xGo\x8a\xd5\x13\x85n\xf4\x91\xd6N\xb5PY\x12\xba\vu\xd9ǽũ\xae\x1c\xa8\v7k^U\x18z\xa3Z_Y\x9e\xde_\xc01\xdb,L\x18\xb6\x06\xe8\xcb\xc1$K\x1c\xf7l\x15x\x06O\x14u\x01K\xac\xed\x87j\xec\x1eI\xac9\u0088Ե\x01\xcfp\xb0\xbe\x16\xa1\xb4dR@\xed#̆;\x87\x835\xad\xd5\a#\xfb\x9ep0\xb95\xcd\xc1DT\U000aad8a\x15w\xfe5\x8dUؐ\x98\x8d\xf1ͽ\x98Pܾ\xb9\xb5*\xac\x14\x8e\xfb\xaf\x98\xce[\xf9\xeexGx\x8f\xe1tD\xc7\xd4`\xe8W\x02\x0eX)\x9f\x0e\x19\xb2(\xecȋ[CN\x15q\xa8CY'Ug\xa9\xa8F\r\x9b\x97\\\xf0$\x1dfh\xe8o\x87\xaa\x98$\xa8\xf3\xa8C\xef9\x00\xfax_i]\xa3x\x02\xd2\xc6g\"\xe2h\x85\xe9\xeaZ\xcdk\x9c\x00\xbb\xeex\xfaL\x005\xe8\xbdZ\\\x8a\xa0\xdf\xe2\xaa\xd8\xf1\xf5[@\xcdmǛ\x96\xaf\x0f\xa5\x9e\x8a[\xdf{\xc1\xeb\xda\xceJ\xf9KP\x1ee͐\xc7m\x82\xfa\xbc\xcbɃ\xa6k\x8e\x8f\xc9\xe0\x01W\x03\xa3S\xf3\xe8\xec¡?\xb6L\x96\f8\xd0\x04d\xf0K\xf0\x8eW\x11\xd0\x1ft\x89\x83~\x19T\xb6N\xdemY\xd5`\xbaf\x8eN\x88\x98\xaf\x19}b$\xa5\x86\xa1\x1e:\xd4\xde[&\xb7\x12R\xb6\x8b\xa2\xfan\xa2P&\xbcR\x12\xffe\v\x9a|[\xab\xf5\x80ܤI\xb8^\xc5}%\x8e\xb6\x1e\x93\xa2P\xc2?̽\xb6\xf7\x0f\xa0\xee\xad9Q\r\xa6\x90!\xc3\u007f\xfcÙۘ\f\xe3\xe2 \x95\xf6\xf3B\xe8\xcfr\xca\xd79\xe1̅\xefY9\xbe6\xed\xcd\xf6\x16_\xcaxA\xf4p\xbe\xdbM]ljj\xff\x98o\x99\xa3\x06\x89:\x1a\f\xc8\xf5\x8e\xec\xfe\xbdY?\xb2\xbd\xd9T!\xc5\x1c\xea\x87\xc3O\r77{_\x0e\xc2\xcf\xc2\x1aM\xf13\t|\xfe2\x82\xfe]ڧ\xf49@\x06\xff\x17\x00\x00\xff\xffñ\x1b\xae\xa0\x19\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xc1n\xe36\x10\xbd\xfb+\x06\xdb\xc3^*y\x17=\xb4ЭM[ h\x12,\x9cE.E\x0f\x145\xb2\xa7\xa1H\x96\x1c:u\xbf\xbe\x18J\x8aeY\x897\v\xacn&g\x1e\xdf̛\x19ҫ\xa2(V\xca\xd3\x03\x86H\xceV\xa0<ῌV~\xc5\xf2\xf1\xa7X\x92[\xef?\xae\x1e\xc96\x15\\\xa5Ȯ\xdb`t)h\xfc\x15[\xb2\xc4\xe4\xec\xaaCV\x8dbU\xad\x00\x94\xb5\x8e\x95,G\xf9\t\xa0\x9d\xe5\xe0\x8c\xc1Plі\x8f\xa9\xc6:\x91i0d\xf0\xf1\xe8\xfd\x87\xf2\xc7\xf2\xc3\n@\a\xcc\ue7e9\xc3Ȫ\xf3\x15\xd8d\xcc\n\xc0\xaa\x0e+\b\x18\x99t@\xef\"\xb1\v\x84\xb1ܣ\xc1\xe0Jr\xab\xe8Q˱\xdb\xe0\x92\xaf\xe0\xb8\xd1{\x0f\x94\xfap6\x19h3\x02\x1d\xf2\x96\xa1\xc8\u007f,n\xdfP\xe4l\xe2M\n\xca,\x11\xc9ۑ\xec6\x19\x15\xce\f䀨\x9d\xc7\n\ue10bW\x1a\x9b\x15\xc0\x90\x82̭\x18\x82\xdc\u007f\xec\xb1\xf4\x0e;Փ\x06p\x1e\xedϟ\xae\x1f~\xb8?Y\x06\xf0\xc1y\fLc|\xfd7\x11v\xb2\n\xd0`ԁ<紿\x17\xc0\xde\n\x1aQ\x14#\xf0\x0eGR\xd8\f\x1c\xc0\xb5\xc0;\x8a\x10\xd0\a\x8ch{\x8dO\x80A\x8c\x94\x05W\xff\x8d\x9aK\xb8\xc7 0\x10w.\x99F\na\x8f\x81!\xa0v[K\xff=cG`\x97\x0f5\x8aqH\xf2\xf1#\xcb\x18\xac2\xb0W&\xe1\xf7\xa0l\x03\x9d:@@9\x05\x92\x9d\xe0e\x93X\u00ad\v\bd[W\xc1\x8e\xd9\xc7j\xbd\xde\x12\x8f\x05\xad]\xd7%K|X\xe7ڤ:\xb1\vq\xdd\xe0\x1e\xcd:ҶPA\xef\x88Qs\n\xb8V\x9e\x8aL\xdd\xe6\xa2.\xbb\xe6\xbb0\xb4@|\u007f\u0095\x0f\xa2m\xe4@v;\xd9\xc8\xd5\xf6\x8a\x02Rn@\x11\xd4\xe0\xdaGqL\xb4,Iv6\xbf\xdd\u007f\x86\xf1\xe8,\xc6<\xfb9\xefG\xc7x\x94@\x12F\xb6\xc5Ћ\xd8\x06\xd7eL\xb4\x8dwd9\xffІ\xd0\xce\xd3\x1fS\xdd\x11\x8b\xee\xff$\x8c,Z\x95p\x95\xbb\x1cj\x84\xe4\x1b\xc5ؔpm\xe1Juh\xaeT\xc4o.\x80d:\x16\x92\xd8/\x93`:\xa0\xe6\xc6}\xd6&\x1b\xe3\fyA\xaf\xf9\\\xb8\xf7\xa8E>ɠ\xb8RK:\xf7\x06\xb4.\x80:\xb3/O\xa0\x97[W\xbeZ\xe9\xc7\xe4\xef\xd9\x05\xb5\xc5\x1b\xd7c\u038df\xdc~Y\xf2\x19\xc9\xc9d\xe9\xdb\x18\x97\rϰ\x01x\xa7xҿ\xac\xc8>\x8f\x81\xc5x^\x11!\v\xa1\xa4\x9d\xad\xb2\x1a\u007f\xcf\x15e\xf5\xe1BL\xb7\v.\x12\xd2\xce=\x81k\x19\xed\x14t\xe0\xba\x10I\x8d\x10\x92}\x13\xd9~~_7Rx-a\xb8@t33\x1f\xf3\xde&c\x06\xacB\xbb\xce+\xa6\xda\xe0\xf2\x91\xf2I\xd9P\x8fr\xe8{\xff\xeb\xf3\xbdw&u\xf8|\xdd\\\x88\xe0\xe1\xd4zZ8\xfd\xc2@EB\x81pzq\x9e~C\xadD\xf0\xae\x19H\f\x05\x1d%\xbe7\xc4 \x92S\xc0\xd9\x04-\x96\xdbcf\xb3Tm3\x93\xb9Ƴ\xedY\xfe\xbeh|\xb0\xe2\x14\xdf2@\xb2Øl\x9dB@\xcb\x03L\xbeQ\xbfz\x84\x18\x15y\xd2>\xf2\xa2\xbaP\x017\xe7\x1e#1\x01\x03\x96\x85i\xbf=\xa9\xf9-\x94E[\xea\xb4օNq\x05ra\x14\x02tf!\xef-\xacnP5\xe7}\\\xc0\x9d\xe3\xe5\xad\x17#\\슳\xc5(\xef\x92f\xa2s\xec\x1byX9\xf6\x90\xd2\x1a=cs7\u007f\xbd\xbf{w\xf2\x18\xcf?\xb5\xb3\r\xf5\u007f=\xe0ϿV=*6\x0f\xe3\x03[\x16\xff\x0f\x00\x00\xff\xff\x83\xf9\xd9\xe0\xf4\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\x0f\xbeϯ \xf2\x1e\xf2\x16\x88=I{h\xe0[\xbai\x81\xa0\xc9f\xb1\x1b\xec\xa5\xe8A#Ѷ\xb2\xb2\xa4J\xd4l\xa7E\xff{Aɞ\x0f\x8f\x93I\x02\xd47\x89\x1f\"\x1f\xf2\xa1\xe4UUU+\xe1\xf5=\x86\xa8\x9dm@x\x8d\u007f\x12Z^\xc5\xfa\xe1e\xac\xb5[o_\xac\x1e\xb4U\r\\\xa5Hn\xb8\xc5\xe8R\x90\xf8\x1a[m5igW\x03\x92P\x82D\xb3\x02\x10\xd6:\x12\xbc\x1dy\t \x9d\xa5\xe0\x8c\xc1Puh뇴\xc1M\xd2Fa\xc8Χ\xa3\xb7\xcf\xeb\x1f\xeb\xe7+\x00\x190\x9b\u007f\xd0\x03F\x12\x83o\xc0&cV\x00V\f\xd8@\xc0HZ\x06\xf4.jrAc\xac\xb7h0\xb8Z\xbbU\xf4(\xf9\xd8.\xb8\xe4\x1b8\b\x8a\xf5\x18RI\xe76;\xba\x9d\x1c\xed\xb2\xc8\xe8H\xbf.\x8a\xdf\xeaHYś\x14\x84Y\n$\x8b\xa3\xb6]2\"\x9c)\xf0\x01Q:\x8f\r\\s,^HT+\x80\x11\x82\x1c[\x05B\xa9\f\xaa07A[\xc2p\xe5L\x1a&0+\xf8\x18\x9d\xbd\x11\xd47PO\xb0\xd7g\x90e\xdd\t\xb0W\x1d\x8ek\xda\xf1\xe1JP\xd9(\xe2\xed\x8b\x12\xb6\xecq\x10ͨ\xe9<\xdaW7o\xee\u007f\xb8;\xd9\x06P\x18eОr\xcd>\xbc\u007f\xfd\xfe\xff\xdf\xd7Ͽ\x83W-a\xe0Ro1\x90\xb6\x1d\bc8\xff\xdc)\x11\xc8A\x8a\b\xd4#\x84dI\x0fX\x1d\xdab\xef\x1b@\x1a\x8d\x96\x9ee\xc5\x0emY\x82\xb0\n\x1e^\xc6F!z\xe9\xfc\x0e\x06\x11\x1e0Dx\xd4ƀu`\x9c\xed0\xc0\x06\xc1\"\xaa\f\xea\xf4\xb1m\xec]2\x8a\xc5\x01\a\xb7EU\xef\x15|p\x9e#\x9e:c4:P\xe2hw\x96\xfcSƧh\x81b.p\x9e=N\xe5D5B\n\xae\x05\xeau\x84\x80>`D[\xd8q\xe2\x18XIXp\x9b\x8f(\xa9\x86;\f\xecf\x8a|\xc4\x15\x02J\xd7Y\xfd\xd7\xdewF\x96\x0f5\x82pl\xcf×\xdb\xc7\n\x03[a\x12>\xcbX\fb\a\x01\xf9\x14H\xf6\xc8_V\x895\xbcs\x01A\xdb\xd65\xd0\x13\xf9جם\xa6i\x14H7\f\xc9jڭs\xf9\xf4&\x91\vq\xadp\x8bf\x1duW\x89 {M()\x05\\\v\xaf\xab\x1c\xba\xcd\xe3\xa0\x1e\xd4\xff\xf6-\xf1\xf4$\xd6Ҙ\x91\x82\xb6ݑ \xf3\xf43\x15`\xa2\x82\x8e FӒ\xc5\x01h\xdebtn\u007f\xbe\xfb\xb0\xef\xc6\\\x8c9\xfa\x19\xf7\x83a<\x94\x80\x01Ӷ\xc5P\x8a\xd8\x067d\x9fh\x95w\xdaR^\x94F\x9d9\x8di3h\xe2\xba\xff\x910\x12ת\x86\xab<\x1f\xb9\x17\x93g\"\xaa\x1a\xdeX\xb8\x12\x03\x9a+\x11\xf1?/\x00#\x1d+\x06\xf6\xcbJp<\xda\xe7\xca\x05\xb5#\xc14}?Q\xaf\xf9D\xbd\xf3(\xb9|\x8c \x9b\xeaV\xcb\xcc\rh]\x00q\xa6_\x9f\xb8^\xa6.\u007f\x1b!\x1f\x92\xbf#\x17D\x87o]\xf19W\x9a\xc5\xf6Ӓ\xcd\x14\x1c\x0f\xcaBc\\V<\xf3\r@\xbd\xa0#\xfe\x92\xd0v?\x06\x16\xf3\xf9L\x11r!\x04\xd3\xd9\n+\xf1\x97\xdcQV\xee.\xe4\xf4n\xc1\x84S\xea\xdd#\xb8\x96\xd0\x1e;\x1dc]\xc8d\x93\x87\xf6W\x05[n\xbe7\x8a\x1b\xaf\xd5\x18.\x04z;S\x9fpoS\xb9EH\xcbJ\xba\xc1\v\xd2\x1b\x83\xcbG\xf2\xc7m\xa3\x8b\x97]\xe1\xfe\xb7\xe3\xbd\xe5k\x17\xf7\x17\xf5\x85\f\xeeO\xb5\x8f\x1b\xa7l\x8c\xa1p*\x10N\x9f\x1c\xa7\xdf\xd8+\x11\xbcSc\x10cCG\xce\xef+r\xe0\x92뀳\tZ-\xd3c\xa6\xb3\xd4m3\x95y\x8dg\xe2\x19~_4>HP\x8a_3@\xb2\xc1\x04\xb6L!\xf0s\xa1\xb8\xc97\xea7\x8f\x10#\"\x1dч\x1fV\x17:\xe0\xed\xb9\xc5\x14\x18;\x03~\xf4\x9c\xf0\xedQ\xcco\xa1\\\xb4%\xa6\xb5.\f\x82\xca˭bGg\x1a\xfcB\x16\x1b\x83\rPH\xe7\xe2\xcf\xcd\x15\x8cQt\x97\xb2{W\xb4\xcae;\x9a\x80ظD\x9f\x80\x9e\xfa\xf3(\xe0B9.D\xea{\x11/\xc5y\xc3:K\r\xb1\x9fߗC@\x9b\x86\xf3c*\xb8\xc6Dž\xdd[\x14\xea\x9c\xc7\x15\\;Z\x16}2\xc3EV\x9cmF~\x97\xa8\xa3:\xc7B\xe4㝴ٿ\xb3\xa6DFn\xc1\xdf\xff\xac\x0e4\x13R\xa2'T\xd7\xf3_\xa3'ON\xfet\xf2R:[\xfeLb\x03\xbf\xfd\xbe*\a\xa3\xba\x9f\xfe^x\xf3\xdf\x00\x00\x00\xff\xffo=\x1agQ\x0e\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85\xb4\xf0[\xce\xf1\xa1ƥ\xb9 v\xfdR\xf4\x81+\xcdZCȅu\xe0d\x81 \xea\x01\xe1YX\xe6a\xab\r\xb8L\xdayL\x88H\x8f[\xcf·a\xb5g(\x15\x0ekv\xc6dy\xa0\xfc=\x9a\xef\x1eq\x9c\x98\xffy\xf7֫h\x92a!\xaeꖺD\xf5\xee\xd3\xedß\xefz\xd50Ѓz\x96 -\bx\xe0\xf5\x03\xa6^\xe9\xe02\xe1\xc0 I\r\x95\xa3\x16\xa5\xc1U@&mH\x02h\x03%\x1a\xa9S\x99\x04D\xb9\xb3\xcdt\x95\xa7\xb0A\x02w\xddt(\x8d.\xd18\x19V\xa8/\x1d\x8bԩ\x1dp\xfc\x86&\xe5[y-BˊS\xaf;Lk\x1c\xbcnK\xdb\xf2\xcf\x00\xf7\b\x035\x12\n\xf4\xe6gL\xdc\x1a\xee\xd0\x10\x99\xc0u\xa2\xd5\x0e\r!\x90\xe8G%\xff\xd3ж\xa4\xb1\x8e\x15\xc9am6\xda\xc2\xeb\\\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xa3@\xa5:\xf4\xb8\x89]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05K\x9c袨\x94t\xfbK6\xaarS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+f]\xb15^\x17\xe9\x1f\x82D\xed\x9b\x1e\xaf\ak\xc5\x17\xb6\x97G$@\x86\xd3+\x8c\xef\xeag\xd1\x02MU\x84\xce盻\xfb\xae2I;D\x9fq\xefhX+\x02\x02L\xaa-֫qkt\xc14Q\xa5\xa5\x96\xca\xf1\x1fI.Q\r\xe1\xb7զ\x90\x8e\xe4\xfe\xef\n\xad#Y\xadᚷ'\xd2ê\xa4Փ\xae\xe1V\xc1\xb5(0\xbf\x16\x16\xbf\xb9\x00\bi\xbb\"`\xe3D\xd0\xddY\x87\x8d=j\x9d\x1f\xc2.8!\xaf\xb0\xc6\xefJLzK\x86\xfaɭLxa\xb0\xe5kL\xc0\xc0\xfa\xf92\xbej\xa16=\xd4|X\u007f\xd4\xda\xc4\xee\t\a4\xa161\xeb\x83_&\xd0䟰(i\xb9ΰx_7#\x16\t\xa3\xb4\xf1v\xc2f\x19̛\xae\xad\x1a\x1c\x18\x15\x1e.C\xc2k'\xd3\xdaj\x1c\xa0y\x1cQ\xcf\xd8VT\xb9{ \x97\x01\xed\xbd\xfe\x8c\xd6\xc9d\xac\xe5`\x12\xefG;\x06y\xa3%\x84]\x86\x86\x16'\xff\xc0\xf6n\x94.\U0001ac58\xb2\xc9\x13O\xb4en<\x02d;\xf3\x1cJ\x9d\xc2Ώ\x04\x9b}`\xfaP6\xad|6Z\xe7(\xc6P\xc3/I^\xa5\x986\x1e\xd5(.\x83\xd9\xde\x1ctb\xdfSHEZF\x9e\x1e\xb1\xaa\x9a_'\xe6\xc9\xfb\x950\bd(\xa4\xf24Az\xb7d3\xa1pT\xa4\xc3b\x82ϣ\x1a\xe9\v\xf9\xb8b\x93\xe3\x158S\x8d\xe9z\xa0!\x8c\x11\xfb#\x98\x05\xff|\tdM\x9fڜ\xe72a\x0f\xb41ڌ\x1aC31\xbf\xffC\xc02\xad\x9fb@\xfa\x1b\xb5k7'H\xf8\x18\x04\x1b\xcc\xc4Njc\x87\x1e\x0e~\xc1\xa4r8\xb5\x8e\x84\x83Tn\xb7h\x88\x16;ԍ\xff}\f\xac\xe3&\x82\x8a9.\xf8\x83y\xb5B'\xe11\x1aSSaSt{^\x80\xdc6\x02K/`+s\x87\xecK\xcd1\xdaqtf%\xf7\x92\x00\xc5\xee\xbdT\n\xe1\x92\xec\xa69\xd2F\xf4\x18`5$\xe0\xfd\xf2p\x86a\x19D\x90\x84Ʃ\xe0(\x884X\xf8\xe8\xca=\xaf\x8f\xb6\x86=\xc0w\x1f\xdfc:\a\x19\xc4k\xea\xc1\xa4\xde\r<\x9d.\v<\xc1(\x92\x9dI\xb1\x9b֜\xf1|\f\xed\x02\x04<\xe1\xde{V\xa3\x87˱B\xa2\x15\rI\x83\x1c\xd0c3\xf2\x84{&UG\xe8\xa2\xe8-Q\x15_\x9ep\x1f\xdbt\x00*\xf1W\xc7(<\xbaT\xc1\xb3\x88YJmi@\xad\xd7\x0e8\x1d7YXf\x94B\t\x88\x9f8\xedF`\xbd\xb0\xf4\x13\xee\xdfX/>Z5\x99,\x17 @\x06\x1b,\xf2\n\v\xf1\xd8\a\x91˴\x19\x8c\xd7\xc9\x02\x8a\xb7\xea\x02>jG\xff\xdc|\x91\x96XT)\xbc\xd7h?j\xc75\xdf\x14b?\x89\x13\x01\xf6\x9dyY*\xbf-\x10.\x8b\xc6oy\xe0-\x94T\xb4\x11\x9b\xb4p\xab\xe8|\xe6\xf1Y\"\xa6\f\x03s\x9e\xad\xa2\xb2\x1c\xd1UZ\xadx\x9b\x0e\xa3- \xda\xe5\xab\x16\x956=I],\xa48\xcab\xcd\xde=\xedV\xfe\x97\x83X\xf8\xb1b\xb0\xccE\x82)\xa4\x15\xc7\xdb9\xf0.\x1c>\xca\x04\n4\x8f\b%\xed\x1b\xf1J\xb5\xc0\x92\xfbr\x82\x16ƻ\x16\xa1\xd4\xdbB\x1a\xc7؊V}d\xcb \xe6\xa8\xe6\x13Q\xf6\xe3\xcd\xe3f\xc9\xdb;\xfbCQ\xe8wo\x8f\x97\xed,\v\xe5u\xe8\x83x&\xbd\xfbQ\b\x0e\xf6\xfeB\xdb+\xab\xf7\xafq\xbb\xa1\x90Ʈ\xe1\x1dߝ\xe7\xd8\xed\x1f\xa2\x84\x9d\xa1\xa2H\x12'\xd2\x02\xe9\xc9N\xe4\xe4>\x90\xf1V\x80\xb9w&\xf4\xf6\xc0\x83\x8a31ϙ\xb6~\xcf\xdfJ\xcc\xf9v\xeb\xfc\t\xf7\xe7\x17\a\xd6\xeb\xfcV\x9d\xc7\xd1$\x9b\u007f`\xb4\x1a\xafE\xab|\x0f\xe7\xfc\xdb9;fK\x96\xc8\t\xce\xdb\x02\xad\x8en\xca\xd7\xcfK\x8e\x02\x9c\xe1 \xdb\xe3usIK.\xfc\xdc,\xa2u\xba\xd4\xd6-b듶\xce\a\x00{\xee\xf6H\x840\xe6\xf4WG\rAl\x1d\x1a\xb0N\x9bp!Jfw\x10 '\xc9\xdbyٓ\xa8\x9bh\xa4'L\x87\xcc\xf3\xd6Bx\x9b~\xeeoJ\xe9\xff\xf34\x13v\x96\x98vit\x82\xd6ΫR\xe4\xce1\x13\xb0m\x82\xb5\xc2\x1f\u07b6Q\xa69&\x94\x1c\xca2W\x9c\xa0=\xe1`s\xf3\xa5\x13w&3D\u007fǨ\xf2)<\x02\xe7T\x15\x85\x18^\xceG\xb3{\xed{\x87\x05X\x13\xf3\a&\xf3X\xb1QY\xe67\xd7*\xf9[s<\n\xa9ny x\xfb͜\x15\b\xa6\x1cO=\xca\\\x87\xfe\xad@\x9a\x8a\xd8\xf3+\x84\xabf\xcdw5\x06{\x92=\xbcɈ\x97\x14\x903\xad\xb4\xeb\x06k\xea\x91\xdeX\xd8Jc]\xcb\xf0\x02\xaa\xd2\xf2u\xf2\xb7=c\xaa\x1bcN>b\xfe\xe4{w\u008a\x99~\xae\x13#\x96\x1c\xac\x03\xf8\x99\xd8!\xc8-H\a\xa8\x12])\x0ex\x91\xb9\xa0a\x16P\xf4B\xf4\x9bI\xe4\x9e\xd9鬪\"\x1e\x90\x15k\xa7T\xb3ѱn\x97\x1f\x84\x8c\x8bN\xc1ibu\xb2@]\xcdn\xedm\xe9gv\xf8\u07bd\f\x98B|\x91EU\x80(H,K\u038d[\x9f?\x18\xd2e\xbc\xac\x9f\x85tu\x06\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8a\x8d\xfbP\xcb\u007f4\xdfd\xaa\b\xd8\n\x99Wf\x81\x8d^,\x99\xa5\xe7\xb6\xda<\xbd\xfca,\x9e\x91\x15\x83\x19\x19t_\xe04\xcf\xef\x1f\xa5Y\xe62\u007f2\xf8\xf2\xaeii$i\xa9\x9e\xf3Ngi\xb2\xf7\xda\xf7Nk\xe5\x15j?\xe5\x9e\xceReN^\xddӦ\xbc\xba\xa7\xaf\xee\xe9\xab{:(\xaf\xee\xe9\xab{\xfaꞎ\x97W\xf7\xb4S^\xdd\xd3\xe8\xfd#\x86\xc3\x15t><;\x99\xab\xc8\x14\x8c9\xb6gƪ3\x8d\xae\xf3\xca:4K2\xa4o\xc7{\x8e\xe4\xd0'\xbeɊ?\x06\x9cҚ6u\xa5\xdd\xf4\x9a\x94iZ\x92a1\xf9o1\"\xbc\xf0\xe84\xe8\xe9l\xfb\xd8\x04\xba\xb9\xb4\xb9~\xeex\x93\xae\xe6\xff7\x01\x88\xd3a\xf8Zz\xfe\x1b\x9fn\xceU?\xf7\x8d\xcf\x01\x81\xe3\xdfd^ydZ\xdbL2\xdb\xf1D\xfc\xa9\x1d>`9\xb8\\\xe8\x83iz\x89߿i,#\xb2ͦs\xcc\xea[Ktb\xf7v\xdd\xff\xc5\xe9:\xe3lbf\xcf\xd2e\xfe\xfb#:\xba\xaa\xc7nZ{\xd0\xd3\xfaۿ!\xc6\x13\x14\xb5\x01%s/\x80@\xa1\a?\xfcT\xfa#\xf2\xc9\xeb|\xfe\xa0\x16\x9f\x97\xb64\x1b\xad\xc9\x1f\x9a\xdfU\xbe\"\am\xd9\a\x02\xb3\xf9f1LCL\x96\xd9x\xfe\xd8\f\xd5%\xb9e\xb1g\xf0\x88<\xb2\xf8\xec\xb18x\x80\xbf؍\xcd\x19\x8b\xf6\xdab\xf3þMVXd.X'\xc3k\x96\xe4\x89\x19`р\xc5e{E\xe7xu2\xb7\xe6\xd1:\x92\xd95\x9e\xaf5Kr,\x9f+&K+\x8a\xd7\xe8ܬ&\xe3j>\x92\xf8U\x19Y/\x9f\xfb\xfd\x92~\xfe\xf1\xfc\xaa\xa8\xac\xaa\xa8\xb3\xc0<\xcfQySK\xb3\xa5\xa2P]\x9a\x19\xd5d=\x1d\x198*\x1f\xea0\xd7\xe9\xd8Tf\xb3\xa0\xa63\x9c\x8e\x91\x1d\xcb}\x8a\xc8k:B\xb2\x9b\xf1\xb4\xd8\r\x98զ\x99\x06\xe3_Շ2\xbf\xd7\xe6\xff\v\r\xfc\xdaIk\x93\xa2\x99=\x95,a}\x96\xed~\xbcr0\xfe\xe0s\x9d\xf019\xb5\xea\x9ex\xa6\xbc(\xdd|>\x92\xc0\x8fR\xa5^\x93h\xb1t|\x1a~\xa1\x82\x13\xcc\x1a7k:\xe3\xb6\xf5h\a\xa7-\x8b\xa5 \xa3\x9e\xc2f\xef\xa3Bv\r7\"ɚ\x86\x13\x14y\xe4LX:\xd9\x17\xc2\xc1ys\x8c\xbd\f=\xa9\xe6|\r\xf0\x83n\"\b\x9d\xcfC'\xe8ZY\x94\xf9\x1e*\x8bp\xde'\xf4u\xa7\xb0IݱJ\x946\xd3\xe1%\x81\x88\x83\xd8]\xbf\xc7H\xbc$\xbc#\x90\xe4\xbaJ\x9b\x11\x8e\x88[\xa8=|z`O\x8e\xbf\x9eNگ\xcckO-\x9c\xab\x06\x1f\xa1O\x90\x9cz\xb6\xef\xa9\x1a 럖\xf3f\x9c\x81\v\xef\xd0\x1cB7\xb3\xfe\xa7\xd7\xfe\xb8G\xbd\x1a\u007f\xfaeռFs\x16\x81\xac\u007fo,\xe6\x81\x1f\xff\xa8[\"JW\x99z{M*\xc3\x0fN\x10\x11\xf4\xef1\x9c\xf6\xc4O\xfb\xdcٌ,\xdb\a\xd0\xda\b\xc3\xecsk#\xf2k\x1e\v\x9a|=\xc7\xef\xae\xfe9\xb4\x15\xd1?M\x9c\xa3\xeb\x80\x1f蘙\xe9'j\xd3\u070e\xd7@s\xc7\xf0\xb0\xc7\xdd\x14\xeb\xe3ם+\xf8\x88\xcf#\xb57\x8a&qx\x18\xf5w\x9a\x98r\x84b\xecy\xb3\xa3S\xdc5\xbd\xf8By\xc4Z\xf4\xcdܠ\xf9 R-\xf2\xbcC\xd1_\x1e\x8f\x89\xf5\x8fr\xeb\xc3F\t\xcd\xe9O\a-&\r\xd7Q\xa35e\xb0F\x97\xd4A\xa5E\xb3ô\xa3$\xf5\x1eޭ\xa96\a\xef\xac\xd4\v\x13~\xf9\xf5\xac]\xa3\"I\xb0t\xf5\xfdH\xf7\xfd\xc9\xf3s\xfe#\xa4\x05\xbc\xeb\v\xfa\xd0\xc2o9LJ\x1a\x97\xe6\x82\xd8\xf5K\xd1\a\xae4k\xf1,\x91*I\xad\xb3=\xdc\u007f/f(\xeak\xa5\x15\xb5q\xd0\xeb\xc1|I\xcc%\x87\xc3\xf9\xe6pij\xd5ju&J\xf9\x80\xc6J\xad\xae@\x94\x12\xbf8T\xf4\x97]?\xfdծ\xa5\xbeܽ={\x92*\xbd\x82\xeb\xca:]|F\xab+\x93\xe0{\xdcJ%\x9d\xd4\xea\xac@'R\xe1\xc4\xd5\x19\x80PJ;Aݖ\xfe\x04H\xb4rF\xe79\x9a\xd5#\xaa\xf5S\xb5\xc1M%\xf3\x14\r\x03\x0fK\xef\xbe[\xffe\xfd\xdd\x19@b\x90\xa7\xdf\xcb\x02\xad\x13Ey\x05\xaa\xca\xf33\x00%\n\xbc\x02\x9bd\x98V9\xda\xf5\x0es4z-\xf5\x99-1\xa1\xd5\x1e\x8d\xae\xca+h\u007f\xf0\x93jL\xfc.\xee\xea\xf9ܕK\xeb~\xecu\u007f\x90\xd6\xf1Oe^\x19\x91w\xd6\xe3^+\xd5c\x95\v\xd3\xf6\x9f\x01\xd8D\x97x\x05\x1fi\xa9R$\x98\x9e\x01\xd4\x1b\xe3\xa5W ҔI%\xf2OF*\x87\xe6Z\xe7U\x11H\xb4\x82\x14mbd\xe9\x98\x14wN\xb8ʂނ˰\xbb\x0e\xb5\x9f\xadV\x9f\x84ˮ`myܺ̄\r\xbfz\x12y\x00u\x97\xdb\x13n\xd6\x19\xa9\x1e\xc7V{\a\xd7F+\xc0/\xa5AK(CʜU\x8f\xf0\x9c\xa1\x02\xa7\xc1T\x8aQ\xf9^$OU9\x82H\x89\xc9z\x80g\x8dI\xbfs\x0e\x97\xfb\f!\x17ց\x93\x05\x82\xa8\x17\x84ga\x19\x87\xad6\xe02i\xe7iB@z\xd8zt>\f\xbb=B\xa9pX\xa3\xd3\x01\x15\xa4z} \x91=\x98\xef\x1eq\x1c\x98\xffy\xf7\xd6\xcbM\x92a!\xaeꑺD\xf5\xee\xd3\xedß\xefz\xdd0\x90\x83z\x97 -\bx`\xa1\x06S\xab\x1f\xb8L80H\\C\xe5hDip\x15(\x936 \x01\xb4\x81\x12\x8dԩL\x02Ey\xb2\xcdt\x95\xa7\xb0A\"\uee99P\x1a]\xa2q2\xa8\x8do\x1d3\xd1\xe9\x1d`\xfc\x866\xe5Gy)B˂S+\x03\xa65\x1d\xbclK\xdb\xe2\xcf\x04\xee\x01\x06\x1a$\x14\xe8\xcdϘ\xb85ܡ!0\x01\xebD\xab\x1d\x1a\xa2@\xa2\x1f\x95\xfcO\x03ے\xc4:\x16$\x87\xb5.\xb7\x8d\x95O\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xab@\xa5:\xf0x\x88]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05\xf3\x98袨\x94t\xfbK\xb6trS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+F]\xb1\x89\\\x17\xe9\x1f\x02G\xed\x9b\x1e\xae\a\xba\xe2\x1b\x1b\xb1#\x1c k\xe6\x05\xc6O\xf5\xbbh\tM]D\x9d\xcf7w\xf7]a\x92vH}\xa6{G\xc2Z\x16\x10\xc1\xa4\xdab\xad\x8d[\xa3\v\x86\x89*-\xb5T\x8e\xffHr\x89jH~[m\n\xe9\x88\xef\xff\xae\xd0:\xe2\xd5\x1a\xae\xd9g\x90\x1cV%iO\xba\x86[\x05ע\xc0\xfcZX\xfc\xe6\f J\xdb\x15\x116\x8e\x05]w7\x1c\xec\xa9\xd6\xf9!\xb8\xa6\t~\x05\x1d\xbf+1\xe9\xa9\f͓[\x99\xb0b\xb0\xe5kL\xc0\xc0\xfa\xf96\xae\xb5P\x9b\x1e\x1a>\xec?jmb}\xc2\x01L\xa8M\xcc\xfa\xe0\x97\tj\xf2OX\x94\xa4\xae3(\xde\xd7\xc3\bE\xa2Qڄ \xc1Y\x06\xf3\xa6k\xab\x06\aF\x85\x97ː赓im5\x0e\xa8y\x9c\xa2\x1e\xb1\xad\xa8r\xf7@~\x1c\xed\xbd\xfe\x8c\xd6\xc9dl\xe4`\x13\xefG'\x06~\xa3%\n\xbb\f\r)'\xff\xc0\xf6n\x14.\xb0\xceXL\xd9\xe4\x89'r\x99\x1bO\x01\xb2\x9dy\x0e\xa5Na\xe7W\x82\xcd> }ț\x96?\x1b\xads\x14cT\xc3/I^\xa5\x986a\xce(]\x06\xbb\xbd9\x98\xc4\x01\xa1\x90\x8a\xa4\x8c\xc2/BU5\xbfN\xec\x93\xfd\x950\bd(\xa4\xf20A\xfa\xb0d3!pԤ\xc3b\x02ϣ\x12\xe9\x1b\x05\x9eb\x93\xe3\x158S\x8d\xc9z\x80!\x8c\x11\xfb#4\vA\xf3\x12\x925sjs\x9e\xcb\x04\x89X\x8d\xd1f\xaa1i&\xf6\xf7\u007fH\xb0L\xeb\xa7\x18\"\xfd\x8dƵ\xce\t\x12>\x9b\xc0\x063\xb1\x93\xda\xd8a\x84\x83_0\xa9\x1cN\xe9\x91p\x90\xca\xed\x16\r\xc1›\x89\xbf\x8f\x11븉\xa0f\x8e3\xfe`_-ӉyL\x8d\xa9\xad\xb0)\x9e\x84\n\x8c8Y\xec\xaa\x04\xa9R\xb9\x93i%r\x90\xca:\xa1\x12\xbf?\xd1\xe07\xbe?\x98\x13\x88\x03\xfc\xbd\x01\x0e\xbb .\xf5<\x9bVH\xe1h\xa1\u0378p\x84v\bf\x9a\f\x1bA\x16PO\xb9\xa3\xb6\x19:\xc5ը\xa4\xecR[\xbbs\xd1r\xca\a\x85\xb9\xd8`\x0e\x16sL\x9c6\xd3\xe4\x89\x11\x02\xdfb\xed\xe7\x04eG,i\xeb3HPg\x8dhۜ\x86\xe7L&\x99\x8f\xdfH\xca\x18\x16\xa4\x1a-[\fQ\x96\xf9\xfeئ!F2\xea\xc5\xe6\x8cF\xdb\"\xcc\xc7\x10\xee\x94!i[\xa4\rnی5\xeeS\xbd\x11\x9bW\xa2\xf7\xd0T_%\xec\xb7\a\xd3_^؉ܒ\xcew\xb7[\xc0\xa2t\xfb\v\x90.\xf4\xc6@\xa5\x00\xab\xc5\xe3wƸӴ\xe5v8\xfbŵ\xe5E\xb8֠\xf1;a\x1a;\xab\xbb\xdaW-b؇\xee\xcc\v\x90ۆa\xe9\x05le\xee\x90c\xa99D;\x81\xce,\xe7^\x92@\xb1\xbe\x97Z!\\\x92\xdd4Gڈ\x19\x03Z\r\x01\xf8\xb8<\x9ca\x98\a\x11 \xa1\t*8\v\"\r\x16>\xbbr\xcf\xfa\xd1\xf6p\x04\xf8\xee\xe3{L\xe7H\x06\xf1\x92z\xb0\xa9w\x83H\xa7\x8b\x02o0\ndgS\x1c\xa65g<\x9fC\xbb\x00\x01O\xb8\xf7\x91\xd5\xe8\xe1r\xac\x11kE\x03\xd2 '\xf4،<\xe1\x9eA\xd5\x19\xba(xKDŷ'\xdc\xc7\x0e\x1d\x10\x95\xf0\xabs\x14\x9e\xba\xd4\xc1\xbb\x88Q\xa5\xb65D\xadu\a\x9c\x8e\xdb,,3J\xa1\x05\x8a\x9f\xb8\xed\x86a\xbd\xb4\xf4\x13\xee\xdfX\xcf>ҚL\x96\v(@\x06\x1b,\xb2\x86\x85|\xec\x83\xc8e\xda,\xc6z\xb2\x00⭺\x80\x8f\xda\xd1?7_\xa4%\x14U\n\xef5ڏ\xdaq\xcf7%\xb1\xdfĉ\x04\xf6\x93Y-\x95w\vD\x97E\xeb\xb78\xb0\v%\x11m\xd8&-\xdc*:\x9fy\xfa,aS\x86\x019\x8fVQY\xce\xe8*\xadV\xec\xa6\xc3j\v\x80v\xf1\xaaY\xa5M\x8fS\x17\v!\x8e\xa2X\xa3wO\xde\xca\xffr\x90\v?\xd6\f\x96\xb9H0\x85\xb4\xe2|;'ޅ\xc3G\x99@\x81\xe6\x11\xa1$\xbf\x11/T\v,\xb9o'Ha|h\x11Z\xed\x16\xd28\xc4V\xa4\xf5\x91#\x03\x9b\xa3\x86Odُ\x0f\x8f\xdb%\xbbw\x8e\x87\xa2\xa8߽\xd2]\xe6Y\x16\xf2\xeb0\x06\xf1H\xfa\xf0\xa3\x10\x9c\xec\xfd\x85\xdc+\x8b\xf7\xafq\xdePHc\xd7\xf0\x8e/\xb4s\xec\xce\x0fY\xc2\xceRQ \t\x13i\x81\xe4d'r\n\x1f\xc8x+\xc0\xdc\a\x13z{\x10Ař\x98\xe7L[\xef\xf3\xb7\x12s\xbe\xdd:\u007f\xc2\xfd\xf9Ł\xf5:\xbfU\xe7q0\xc9\xe6\x1f\x18\xad&j\xd1*\xdf\xc39\xffv\u0381\xd9\x12\x159!x[ \xd5\xd1C\xf9\xfay\xc9Q\x80\xce\xda!j\xa1\xc9\xcd%-\x85\xf0s\xbb\x88\x96\xe9R[\xb7\b\xadO\xda:\x9f\x00\xec\x85\xdb#\x19\u0098\xd3_\x9d5\x04\xb1uh\xc0:m\u0085(\x99\xddA\x82\x9c8o\xe7yO\xacn\xb2\x91\x1e0\x1d2\xcf[\v\xe1m\xfa\xb9\xbf)\xa5\xff\xcf\xc3L8Xbإ\xd1\tZ;/J\x91\x9ec&a\xdb$k\x85?\xbcm\xa3LsL*9\xb4e\xa18\x91\xf6\x84\x83\xcd͗Nޙ\xcc\x10\xfd\x1d#ʧ\xe0\b\\\xe8T\x14bx9\x1f\x8d\ued5f\x1d\x14\xb0\x06\xe6\x0fL\xe6\xb1b\xa3\xb2,n\xaeE\xf2\xb7\x16x\x14R\xdd\xf2B\xf0\xf6\x9b\x05+\x10L9\x9ez\x94\xb9\x0e\xf3[\x864\x1d\xb1\xe7W\bW͚\xefj\f\xf68{x\x93\x11\xcf)\xa0`Zi\xd7M\xd6\xd4+\xbd\xb1\xb0\x95ƺ\x16\xe1\x05P\xa5\xe5\xeb\xe4o{\xc6T7Ɯ|\xc4\xfc\xc9\xcf\xee\xa4\x153\xfd\\\x17F,9X\a\xe2gb\x87 \xb7 \x1d\xa0Jt\xa58\xe1E悖Y\x00\xd13\xd1;\x93H\x9fٙ\xac\xaa\"\x9e +\x96N\xa9f\xb3c\xdd)?\b\x19\x97\x9d\x82\xd3\xd8\xead\x81\xba\x9au\xedm\xebWv\xf8ٽ\n\x98B|\x91EU\x80(\x88-K\u038d[_?\x18\xcae<\xaf\x9f\x85tu\x05\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8aM\xf8P\xf3\u007f\xb4\xded\xaa\t\xd8\n\x99Wf\x81\x8d^̙\xa5\xe7\xb6\xda<\xbd\xfca,\x1e\x91\x15\x1332\xe9\xbe h\x9e\xf7\x1f\xa5Y\x162\u007f2\xf8\xf2\xa1ii$I\xa9\x9e\x8bNgar\xf4ڏNk\xe1\x15j?\x15\x9e\xceBeL^\xc3Ӧ\xbd\x86\xa7\xaf\xe1\xe9kx:h\xaf\xe1\xe9kx\xfa\x1a\x9e\x8e\xb7\xd7\xf0\xb4\xd3^\xc3\xd3h\xff\x11\x83\xe1\x8a3\xb7G\x06Da\x15Y\x821\x87\xf6\xccZu\xa5\xd1u^Y\x87fI\x85\xf4\xed\xf8̑\x1a\xfa\xc4\x0fY\xf1\x17zSRӖ\xae\xb4N\xaf)\x99&\x95\f\xca\xe4\xbfň\x88£ˠ\xa7\xab\xedc\v\xe8\xe6\xca\xe6\xfa\xb5\xe3M\xb9\x9a\xff\xdf\x04A\x9c\x0e\xcb\xd7\xdc\xf3\xdf\xf8tk\xae\xfa\xb5o|\x0e\b\x18\xff&\xeb\xca#\xcb\xdaf\x8aَ\x17\xe2Oy\xf8@\xcb\xc1\xe5B\x9f\x98\xa6W\xf8\xfd\x9b\xa6eD\xb5\xd9t\x8dY}k\x89N\xecޮ\xfb\xbf8]W\x9cM\xec\xecY\xba\xcc\u007f\u007fDGW\xf5\xd8-k\x0frZ\u007f\xfb7\xa4\xf1\x04Dm@\xc9\xdc3 @\xe8\x91\x1f~*\xfd\x11\xf9d=\x9f?\xa8\xc5ץ-\xadFk\xea\x87\xe6\xbd\xcaWԠ-\xfb@`\xb6\xde,\x06i\x88\xa92\x1b\xaf\x1f\x9b\x81\xba\xa4\xb6,\xf6\f\x1eQG\x16_=\x16G\x1e\xe0/vckƢ\xa3\xb6\xd8\xfa\xb0oS\x15\x16Y\v֩\xf0\x9a\x05yb\x05X4\xc1⪽\xa2k\xbc:\x95[\xf3\xd4:R\xd95^\xaf5\vr\xac\x9e+\xa6J+\n\xd7\xe8ڬ\xa6\xe2j>\x93\xf8U\x15Y/_\xfb\xfd\x92q\xfe\xf1\xfa\xaa\xa8\xaa\xaa\xa8\xb3\xc0<\xceQuSK\xab\xa5\xa2\xa8\xba\xb42\xaa\xa9z:\xb2pT=\xd4a\xadӱ\xad\xccVAMW8\x1d\x03;V\xfb\x14Q\xd7t\x04d\xb7\xe2iq\x180+M3\x03ƿ\xaa\x0fm\xde\xd7\xe6\xff\v\t\xfc\xdaMk\x93\xa2\x99=\x95,A}\x16\xed~\xber\xb0\xfe\xe0s\x9d\xf019\x8d\xea\x9ex\xa6\xa2(\xdd|>\x92\xc0\x8fR\xa5^\x92HY:1\r\xbfP\xc1\x05fM\x985]q\xdbF\xb4\x83Ӗ\xc5R\x90QOa\xb3\xf7Y!\xbb\x86\x1b\x91d\xcd\xc0\t\x88\xbcr&,\x9d\xec\v\xe1\xe0\xbc9\xc6^\x86\x99\xd4s\xbe\x06\xf8A7\x19\x84\xce\xe7\xa1\x13p\xad,\xca|\x0f\x95E8\xef\x03\xfa\xbaSؤ\xecX%J\x9b\xe9\xf0\x92@\xc4A\xec\xae?c$_\x12\xde\x11Hr]\xa5\xcd\nG\xd8-\xd4\x1e>=p$\xc7_O'\xedW\xe6u\xa4\x16\xceU\x83\x8f\xd0'@N=\x1e\xb1\x88d\xd3Y\x15\xeb\xb4\x11\x8f\xf8A\xfbw5bh֟\xd1{Z\xa5\xb6\xab!\xc7Z\x17MNJ\xb2\xdf\xdb\x10`{\xf5Rk[\x9b\x84\"l\xa7L\ue31e;\x97Gl\xee\xfe\xfe\x83ߐ\x93\x05\xae\xdfW>õ*\x85\xb1H\x94\x0e\x1b\xf5\x936Ӗ0\xd3ϐ\xeb\x9a\x0e\xdf\x0f\xf7a\x90\xafz8\x99v\xd2n\xfc\x13\x16A|\x03\xe9bD\xfea|f\xe7\xb0\xdcaⱜ\x98\xdeN\xc2\x12\xd6\xeaD\xb2-\xe2\x14\x05߰|\xbb7\x16\x8eEdGLFe\xf1\xa7g\x85\xe6sPT{\xab<\xa7f^]\xf9\xc7\xc1\xc4\xc0\xe01\xf3A\xf6o0|\xcc署@ֿ\xf7\xe6\xcd8\x13.\xbcCsH\xba\x19\xfd\x9f\xd6\xfd\xf1\x88z5\xfe\xf4˪y\x8d\xe6,\x82\xb2\xfe\xbd\xb1\x98\a~\xfc\xa3n\x89(]ej\xf7\x9aT\x86\x1f\x9c \xe8\xdfc8퉟\xf6\xb9\xb3\x19^\xb6\x0f\xa0\xb5\x19\x86\xd9\xe7\xd6F\xf8\xd7<\x164\xf9z\x8e\xf7\xae\xfe9\xb4\x15\xc1?\x8d\x9d\xa3z\xc0\x0ft\xcc\xec\xf4\x13\x8din\xc7kB\xf3\xc4\xf0\xb0\xc7\xdd\x14\xea\xe3ם+\xf8\x88\xcf#\xbd7\x8a6qx\x18\xf5w\x9a\x98r\x86b\xecy\xb3\xa3[\xdc5\xb3\xf8By\xc4Z\xf4\xcd\xdc`\xf8 S-\xf2\xbc\x03\xd1_\x1e\x8f\xb1\xf5\x8fr\xeb\xd3F\t\xed\xe9O\a#&\r\xd7Q\xa35e\xb0FU\xea\xa0Ӣ\xd9a\xda\x11\x92ڇw{\xaa\xcd\xc1;+\xb5b\xc2/\xbf\x9e\xb5:*\x92\x04KWߏt\x1f\x859\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0e95ec2f6a..0438c75381 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -72,6 +72,26 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - resticrepositories + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - resticrepositories/status + verbs: + - get + - patch + - update - apiGroups: - velero.io resources: diff --git a/pkg/apis/velero/v1/restic_repository.go b/pkg/apis/velero/v1/restic_repository_types.go similarity index 78% rename from pkg/apis/velero/v1/restic_repository.go rename to pkg/apis/velero/v1/restic_repository_types.go index d78f8851b3..6a44c2a318 100644 --- a/pkg/apis/velero/v1/restic_repository.go +++ b/pkg/apis/velero/v1/restic_repository_types.go @@ -64,9 +64,15 @@ type ResticRepositoryStatus struct { LastMaintenanceTime *metav1.Time `json:"lastMaintenanceTime,omitempty"` } +// TODO(2.0) After converting all resources to use the runtime-controller client, +// the genclient and k8s:deepcopy markers will no longer be needed and should be removed. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - +// +kubebuilder:object:root=true +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type ResticRepository struct { metav1.TypeMeta `json:",inline"` @@ -80,7 +86,12 @@ type ResticRepository struct { Status ResticRepositoryStatus `json:"status,omitempty"` } +// TODO(2.0) After converting all resources to use the runtime-controller client, +// the k8s:deepcopy marker will no longer be needed and should be removed. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories/status,verbs=get;update;patch // ResticRepositoryList is a list of ResticRepositories. type ResticRepositoryList struct { diff --git a/pkg/apis/velero/v1/schedule_types.go b/pkg/apis/velero/v1/schedule_types.go index a3987180da..ab950eab7f 100644 --- a/pkg/apis/velero/v1/schedule_types.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -84,7 +84,6 @@ type ScheduleStatus struct { // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Name",type="string",JSONPath=".metadata.name",description="Name of the schedule" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="Status of the schedule" // +kubebuilder:printcolumn:name="Schedule",type="string",JSONPath=".spec.schedule",description="A Cron expression defining when to run the Backup" // +kubebuilder:printcolumn:name="LastBackup",type="date",JSONPath=".status.lastBackup",description="The last time a Backup was run for this schedule" diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 271051f674..e5f3267243 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -739,29 +739,12 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } - resticRepoControllerRunInfo := func() controllerRunInfo { - resticRepoController := controller.NewResticRepositoryController( - s.logger, - s.sharedInformerFactory.Velero().V1().ResticRepositories(), - s.veleroClient.VeleroV1(), - s.mgr.GetClient(), - s.resticManager, - s.config.defaultResticMaintenanceFrequency, - ) - - return controllerRunInfo{ - controller: resticRepoController, - numWorkers: defaultControllerWorkers, - } - } - enabledControllers := map[string]func() controllerRunInfo{ controller.BackupSync: backupSyncControllerRunInfo, controller.Backup: backupControllerRunInfo, controller.GarbageCollection: gcControllerRunInfo, controller.BackupDeletion: deletionControllerRunInfo, controller.Restore: restoreControllerRunInfo, - controller.ResticRepo: resticRepoControllerRunInfo, } // Note: all runtime type controllers that can be disabled are grouped separately, below: enabledRuntimeControllers := make(map[string]struct{}) @@ -833,6 +816,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) } + if err := controller.NewResticRepoConciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.resticManager).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) + } + if _, ok := enabledRuntimeControllers[controller.ServerStatusRequest]; ok { r := controller.ServerStatusRequestReconciler{ Scheme: s.mgr.GetScheme(), diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index cd4139640c..9a1fa51e21 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -18,59 +18,46 @@ package controller import ( "context" - "encoding/json" "strings" "time" - jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/client-go/tools/cache" + + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/restic" - - "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/vmware-tanzu/velero/pkg/util/kube" ) -type resticRepositoryController struct { - *genericController +const ( + repoSyncPeriod = 5 * time.Minute +) - resticRepositoryClient velerov1client.ResticRepositoriesGetter - resticRepositoryLister velerov1listers.ResticRepositoryLister - kbClient client.Client - repositoryManager restic.RepositoryManager +type ResticRepoReconciler struct { + client.Client + namespace string + logger logrus.FieldLogger + clock clock.Clock defaultMaintenanceFrequency time.Duration - - clock clock.Clock + repositoryManager restic.RepositoryManager } -// NewResticRepositoryController creates a new restic repository controller. -func NewResticRepositoryController( - logger logrus.FieldLogger, - resticRepositoryInformer velerov1informers.ResticRepositoryInformer, - resticRepositoryClient velerov1client.ResticRepositoriesGetter, - kbClient client.Client, - repositoryManager restic.RepositoryManager, - defaultMaintenanceFrequency time.Duration, -) Interface { - c := &resticRepositoryController{ - genericController: newGenericController(ResticRepo, logger), - resticRepositoryClient: resticRepositoryClient, - resticRepositoryLister: resticRepositoryInformer.Lister(), - kbClient: kbClient, - repositoryManager: repositoryManager, - defaultMaintenanceFrequency: defaultMaintenanceFrequency, - - clock: &clock.RealClock{}, +func NewResticRepoConciler(namespace string, logger logrus.FieldLogger, client client.Client, + defaultMaintenanceFrequency time.Duration, repositoryManager restic.RepositoryManager) *ResticRepoReconciler { + c := &ResticRepoReconciler{ + client, + namespace, + logger, + clock.RealClock{}, + defaultMaintenanceFrequency, + repositoryManager, } if c.defaultMaintenanceFrequency <= 0 { @@ -78,124 +65,106 @@ func NewResticRepositoryController( c.defaultMaintenanceFrequency = restic.DefaultMaintenanceFrequency } - c.syncHandler = c.processQueueItem - - resticRepositoryInformer.Informer().AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: c.enqueue, - }, - ) - - c.resyncPeriod = 5 * time.Minute - c.resyncFunc = c.enqueueAllRepositories - return c } -// enqueueAllRepositories lists all restic repositories from cache and enqueues all -// of them so we can check each one for maintenance. -func (c *resticRepositoryController) enqueueAllRepositories() { - c.logger.Debug("resticRepositoryController.enqueueAllRepositories") - - repos, err := c.resticRepositoryLister.List(labels.Everything()) - if err != nil { - c.logger.WithError(errors.WithStack(err)).Error("error listing restic repositories") - return - } - - for _, repo := range repos { - c.enqueue(repo) - } +func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.ResticRepositoryList{}, repoSyncPeriod) + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1api.ResticRepository{}). + Watches(s, nil). + Complete(r) } -func (c *resticRepositoryController) processQueueItem(key string) error { - log := c.logger.WithField("key", key) - log.Debug("Running processQueueItem") - - ns, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - log.WithError(errors.WithStack(err)).Error("error splitting queue key") - return nil +func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.logger.WithField("resticRepo", req.String()) + resticRepo := &velerov1api.ResticRepository{} + if err := r.Get(ctx, req.NamespacedName, resticRepo); err != nil { + if apierrors.IsNotFound(err) { + log.Warnf("restic repository %s in namespace %s is not found", req.Name, req.Namespace) + return ctrl.Result{}, nil + } + log.WithError(err).Error("error getting restic repository") + return ctrl.Result{}, err } - log = c.logger.WithField("namespace", ns).WithField("name", name) - - req, err := c.resticRepositoryLister.ResticRepositories(ns).Get(name) - if apierrors.IsNotFound(err) { - log.Debug("Unable to find ResticRepository") - return nil - } + // Initialize the patch helper. + patchHelper, err := patch.NewHelper(resticRepo, r.Client) if err != nil { - return errors.Wrap(err, "error getting ResticRepository") + log.WithError(err).Error("Error getting a patch helper to update restic repository resource") + return ctrl.Result{}, errors.WithStack(err) } - // Don't mutate the shared cache - reqCopy := req.DeepCopy() + if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.ResticRepositoryPhaseNew { + if err = r.initializeRepo(ctx, resticRepo, log, patchHelper); err != nil { + log.WithError(err).Error("error initialize repository") + return ctrl.Result{}, errors.WithStack(err) + } - if req.Status.Phase == "" || req.Status.Phase == velerov1api.ResticRepositoryPhaseNew { - return c.initializeRepo(reqCopy, log) + return ctrl.Result{}, nil } // If the repository is ready or not-ready, check it for stale locks, but if // this fails for any reason, it's non-critical so we still continue on to the // rest of the "process" logic. log.Debug("Checking repository for stale locks") - if err := c.repositoryManager.UnlockRepo(reqCopy); err != nil { + if err := r.repositoryManager.UnlockRepo(resticRepo); err != nil { log.WithError(err).Error("Error checking repository for stale locks") } - switch req.Status.Phase { + switch resticRepo.Status.Phase { case velerov1api.ResticRepositoryPhaseReady: - return c.runMaintenanceIfDue(reqCopy, log) + return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, patchHelper, log) case velerov1api.ResticRepositoryPhaseNotReady: - return c.checkNotReadyRepo(reqCopy, log) + return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, patchHelper, log) } - return nil + return ctrl.Result{}, nil } -func (c *resticRepositoryController) initializeRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger, patchHelper *patch.Helper) error { log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid loc := &velerov1api.BackupStorageLocation{} - if err := c.kbClient.Get(context.Background(), client.ObjectKey{ + + if err := r.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { - return c.patchResticRepository(req, repoNotReady(err.Error())) + return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) } repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { - r.Status.Message = err.Error() - r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady + return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + rr.Status.Message = err.Error() + rr.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady - if r.Spec.MaintenanceFrequency.Duration <= 0 { - r.Spec.MaintenanceFrequency = metav1.Duration{Duration: c.defaultMaintenanceFrequency} + if rr.Spec.MaintenanceFrequency.Duration <= 0 { + rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} } }) } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { - r.Spec.ResticIdentifier = repoIdentifier + if err := r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + rr.Spec.ResticIdentifier = repoIdentifier - if r.Spec.MaintenanceFrequency.Duration <= 0 { - r.Spec.MaintenanceFrequency = metav1.Duration{Duration: c.defaultMaintenanceFrequency} + if rr.Spec.MaintenanceFrequency.Duration <= 0 { + rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} } }); err != nil { return err } - if err := ensureRepo(req, c.repositoryManager); err != nil { - return c.patchResticRepository(req, repoNotReady(err.Error())) + if err := ensureRepo(req, r.repositoryManager); err != nil { + return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) } - return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) { - req.Status.Phase = velerov1api.ResticRepositoryPhaseReady - req.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} + return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + rr.Status.Phase = velerov1api.ResticRepositoryPhaseReady + rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) } @@ -218,10 +187,10 @@ func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.Repositor return nil } -func (c *resticRepositoryController) runMaintenanceIfDue(req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger) error { log.Debug("resticRepositoryController.runMaintenanceIfDue") - now := c.clock.Now() + now := r.clock.Now() if !dueForMaintenance(req, now) { log.Debug("not due for maintenance") @@ -233,17 +202,15 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *velerov1api.Restic // prune failures should be displayed in the `.status.message` field but // should not cause the repo to move to `NotReady`. log.Debug("Pruning repo") - if err := c.repositoryManager.PruneRepo(req); err != nil { + if err := r.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - if patchErr := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { - r.Status.Message = err.Error() - }); patchErr != nil { + if patchErr := patchHelper.Patch(ctx, req); patchErr != nil { + req.Status.Message = err.Error() return patchErr } } - - return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) { - req.Status.LastMaintenanceTime = &metav1.Time{Time: now} + return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + rr.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } @@ -251,7 +218,7 @@ func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool { return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now) } -func (c *resticRepositoryController) checkNotReadyRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil @@ -261,11 +228,10 @@ func (c *resticRepositoryController) checkNotReadyRepo(req *velerov1api.ResticRe // we need to ensure it (first check, if check fails, attempt to init) // because we don't know if it's been successfully initialized yet. - if err := ensureRepo(req, c.repositoryManager); err != nil { - return c.patchResticRepository(req, repoNotReady(err.Error())) + if err := ensureRepo(req, r.repositoryManager); err != nil { + return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) } - - return c.patchResticRepository(req, repoReady()) + return r.patchResticRepository(ctx, req, patchHelper, log, repoReady()) } func repoNotReady(msg string) func(*velerov1api.ResticRepository) { @@ -285,37 +251,11 @@ func repoReady() func(*velerov1api.ResticRepository) { // patchResticRepository mutates req with the provided mutate function, and patches it // through the Kube API. After executing this function, req will be updated with both // the mutation and the results of the Patch() API call. -func (c *resticRepositoryController) patchResticRepository(req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error { - // Record original json - oldData, err := json.Marshal(req) - if err != nil { - return errors.Wrap(err, "error marshalling original ResticRepository") - } - +func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger, mutate func(*velerov1api.ResticRepository)) error { mutate(req) - - // Record new json - newData, err := json.Marshal(req) - if err != nil { - return errors.Wrap(err, "error marshalling updated ResticRepository") - } - - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) - if err != nil { - return errors.Wrap(err, "error creating json merge patch for ResticRepository") - } - - // empty patch: don't apply - if string(patchBytes) == "{}" { - return nil - } - - // patch, and if successful, update req - var patched *velerov1api.ResticRepository - if patched, err = c.resticRepositoryClient.ResticRepositories(req.Namespace).Patch(context.TODO(), req.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}); err != nil { - return errors.Wrap(err, "error patching ResticRepository") + if err := patchHelper.Patch(ctx, req); err != nil { + log.WithError(err).Errorf("error updating restic repository resource %s in namespace %s with err %s", req.Name, req.Namespace, err.Error()) + return err } - req = patched - return nil } diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go new file mode 100644 index 0000000000..ba394a6e35 --- /dev/null +++ b/pkg/controller/restic_repository_controller_test.go @@ -0,0 +1,198 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + resticmokes "github.com/vmware-tanzu/velero/pkg/restic/mocks" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +const defaultMaintenanceFrequency = 10 * time.Minute + +func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { + mgr := &resticmokes.RepositoryManager{} + if mockOn != "" { + mgr.On(mockOn, arg).Return(ret) + } + return NewResticRepoConciler( + velerov1api.DefaultNamespace, + velerotest.NewLogger(), + velerotest.NewFakeControllerRuntimeClient(t), + defaultMaintenanceFrequency, + mgr, + ) +} + +func mockResticRepositoryCR() *velerov1api.ResticRepository { + return &velerov1api.ResticRepository{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "repo", + }, + Spec: velerov1api.ResticRepositorySpec{ + MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + }, + } + +} + +func TestPatchResticRepository(t *testing.T) { + rr := mockResticRepositoryCR() + reconciler := mockResticRepoReconciler(t, rr, "", nil, nil) + err := reconciler.Client.Create(context.TODO(), rr) + assert.NoError(t, err) + patchHelper, err := patch.NewHelper(rr, reconciler.Client) + assert.NoError(t, err) + err = reconciler.patchResticRepository(context.Background(), rr, patchHelper, reconciler.logger, repoReady()) + assert.NoError(t, err) + assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + err = reconciler.patchResticRepository(context.Background(), rr, patchHelper, reconciler.logger, repoNotReady("not ready")) + assert.NoError(t, err) + assert.NotEqual(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) +} + +func TestCheckNotReadyRepo(t *testing.T) { + rr := mockResticRepositoryCR() + reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) + err := reconciler.Client.Create(context.TODO(), rr) + assert.NoError(t, err) + patchHelper, err := patch.NewHelper(rr, reconciler.Client) + assert.NoError(t, err) + err = reconciler.checkNotReadyRepo(context.TODO(), rr, patchHelper, reconciler.logger) + assert.NoError(t, err) + assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhase("")) + rr.Spec.ResticIdentifier = "s3:test.amazonaws.com/bucket/restic" + err = reconciler.checkNotReadyRepo(context.TODO(), rr, patchHelper, reconciler.logger) + assert.NoError(t, err) + assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) +} + +func TestRunMaintenanceIfDue(t *testing.T) { + rr := mockResticRepositoryCR() + reconciler := mockResticRepoReconciler(t, rr, "PruneRepo", rr, nil) + err := reconciler.Client.Create(context.TODO(), rr) + assert.NoError(t, err) + patchHelper, err := patch.NewHelper(rr, reconciler.Client) + assert.NoError(t, err) + lastTm := rr.Status.LastMaintenanceTime + err = reconciler.runMaintenanceIfDue(context.TODO(), rr, patchHelper, reconciler.logger) + assert.NoError(t, err) + assert.NotEqual(t, rr.Status.LastMaintenanceTime, lastTm) + + rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} + lastTm = rr.Status.LastMaintenanceTime + err = reconciler.runMaintenanceIfDue(context.TODO(), rr, patchHelper, reconciler.logger) + assert.NoError(t, err) + assert.Equal(t, rr.Status.LastMaintenanceTime, lastTm) +} + +func TestInitializeRepo(t *testing.T) { + rr := mockResticRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) + err := reconciler.Client.Create(context.TODO(), rr) + assert.NoError(t, err) + patchHelper, err := patch.NewHelper(rr, reconciler.Client) + assert.NoError(t, err) + locations := &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: rr.Spec.BackupStorageLocation, + }, + } + + err = reconciler.Client.Create(context.TODO(), locations) + assert.NoError(t, err) + err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger, patchHelper) + assert.NoError(t, err) + assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) +} + +func TestResticRepoReconcile(t *testing.T) { + tests := []struct { + name string + repo *velerov1api.ResticRepository + expectNil bool + }{ + { + name: "test on api server not found", + repo: &velerov1api.ResticRepository{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "unknown", + }, + Spec: velerov1api.ResticRepositorySpec{ + MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + }, + }, + expectNil: true, + }, + { + name: "test on initialize repo", + repo: &velerov1api.ResticRepository{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "repo", + }, + Spec: velerov1api.ResticRepositorySpec{ + MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + }, + }, + expectNil: true, + }, + { + name: "test on repo with new phase", + repo: &velerov1api.ResticRepository{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "repo", + }, + Spec: velerov1api.ResticRepositorySpec{ + MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + }, + Status: velerov1api.ResticRepositoryStatus{ + Phase: velerov1api.ResticRepositoryPhaseNew, + }, + }, + expectNil: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + reconciler := mockResticRepoReconciler(t, test.repo, "", test.repo, nil) + err := reconciler.Client.Create(context.TODO(), test.repo) + assert.NoError(t, err) + _, err = reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.repo.Namespace, Name: test.repo.Name}}) + if test.expectNil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/restic/mocks/repository_manager.go new file mode 100644 index 0000000000..b164cb3e00 --- /dev/null +++ b/pkg/restic/mocks/repository_manager.go @@ -0,0 +1,147 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.*/ +// Code generated by mockery v2.10.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + restic "github.com/vmware-tanzu/velero/pkg/restic" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// RepositoryManager is an autogenerated mock type for the RepositoryManager type +type RepositoryManager struct { + mock.Mock +} + +// ConnectToRepo provides a mock function with given fields: repo +func (_m *RepositoryManager) ConnectToRepo(repo *v1.ResticRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Forget provides a mock function with given fields: _a0, _a1 +func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 restic.SnapshotIdentifier) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, restic.SnapshotIdentifier) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// InitRepo provides a mock function with given fields: repo +func (_m *RepositoryManager) InitRepo(repo *v1.ResticRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewBackupper provides a mock function with given fields: _a0, _a1 +func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (restic.Backupper, error) { + ret := _m.Called(_a0, _a1) + + var r0 restic.Backupper + if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) restic.Backupper); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(restic.Backupper) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *v1.Backup) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewRestorer provides a mock function with given fields: _a0, _a1 +func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (restic.Restorer, error) { + ret := _m.Called(_a0, _a1) + + var r0 restic.Restorer + if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) restic.Restorer); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(restic.Restorer) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *v1.Restore) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PruneRepo provides a mock function with given fields: repo +func (_m *RepositoryManager) PruneRepo(repo *v1.ResticRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UnlockRepo provides a mock function with given fields: repo +func (_m *RepositoryManager) UnlockRepo(repo *v1.ResticRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} From d20c34fe745623b383049d6c762f735c499bd0e0 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 2 May 2022 13:26:49 -0400 Subject: [PATCH 119/366] Add aws limitation for migration, add limitation section Signed-off-by: Abigail McCarthy --- site/content/docs/main/migration-case.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index 9ad7b809ae..f9ec0a57e9 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -3,9 +3,21 @@ title: "Cluster migration" layout: docs --- -## Using Backups and Restores +Velero can help you port your resources from one cluster to another, as long as you point each Velero instance to the same cloud object storage location. -Velero can help you port your resources from one cluster to another, as long as you point each Velero instance to the same cloud object storage location. This scenario assumes that your clusters are hosted by the same cloud provider. **Note that Velero does not natively support the migration of persistent volumes snapshots across cloud providers.** If you would like to migrate volume data between cloud platforms, please enable [restic][2], which will backup volume contents at the filesystem level. +## Migration Limitations + +Before migrating you should consider the following, + +* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [restic][2], which will backup volume contents at the filesystem level. +* Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. +* Migrating workloads across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered before migration, including the compatibility of API groups between clusters for each custom resource. If a Kubernetes version upgrade breaks the compatibility of core/native API groups, migrating with Velero will not be possible without first updating the impacted custom resources. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). +* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [restic][2]. + + +## Migration Scenario + +This scenario assumes that your clusters are hosted by the same cloud provider 1. *(Cluster 1)* Assuming you haven't already been checkpointing your data with the Velero `schedule` operation, you need to first back up your entire cluster (replacing `` as desired): @@ -50,10 +62,7 @@ Check that the second cluster is behaving as expected: If you encounter issues, make sure that Velero is running in the same namespace in both clusters. -## Migrating Workloads Across Different Kubernetes Versions -Migration across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered: compatibility of API groups between clusters for each custom resource, and if a Kubernetes version upgrade breaks the compatibility of core/native API groups. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). -**Note:** Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. [1]: how-velero-works.md#set-a-backup-to-expire [2]: restic.md From 965f6b606cba20c87039123e934f7ab59e0abed9 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 2 May 2022 13:38:39 -0400 Subject: [PATCH 120/366] Update headers in supported providers page Signed-off-by: Abigail McCarthy --- site/content/docs/main/supported-providers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/supported-providers.md b/site/content/docs/main/supported-providers.md index 86475c389f..fe8c03d3a1 100644 --- a/site/content/docs/main/supported-providers.md +++ b/site/content/docs/main/supported-providers.md @@ -5,7 +5,7 @@ layout: docs Velero supports a variety of storage providers for different backup and snapshot operations. Velero has a plugin system which allows anyone to add compatibility for additional backup and volume storage platforms without modifying the Velero codebase. -## Velero supported providers +## Provider plugins maintained by the Velero maintainers {{< table caption="Velero supported providers" >}} @@ -20,7 +20,7 @@ Velero supports a variety of storage providers for different backup and snapshot Contact: [#Velero Slack](https://kubernetes.slack.com/messages/velero), [GitHub Issues](https://github.com/vmware-tanzu/velero/issues) -## Community supported providers +## Provider plugins maintained by the Velero community {{< table caption="Community supported providers" >}} | Provider | Object Store | Volume Snapshotter | Plugin Documentation | Contact | From 2e1c35fe39e97cfef9226f42937eb85b1a6f37bd Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 3 May 2022 05:35:16 -0400 Subject: [PATCH 121/366] Update resource filtering page Signed-off-by: Abigail McCarthy --- site/content/docs/main/resource-filtering.md | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/site/content/docs/main/resource-filtering.md b/site/content/docs/main/resource-filtering.md index ddcd97e4ac..18da47c891 100644 --- a/site/content/docs/main/resource-filtering.md +++ b/site/content/docs/main/resource-filtering.md @@ -5,16 +5,18 @@ layout: docs *Filter objects by namespace, type, or labels.* -Velero includes all objects in a backup or restore when no filtering options are used. +This page describes how to use the include and exclude flags with the `velero backup` and `velero restore` commands. Velero includes all objects in a backup or restore when no filtering options are used. ## Includes -Only specific resources are included, excluding all others. +Only specific resources are included, all others are excluded. Wildcard takes precedence when both a wildcard and specific resource are included. ### --include-namespaces +Namespaces to include. Default is `*`, all namespaces. + * Backup a namespace and it's objects. ```bash @@ -27,7 +29,9 @@ Wildcard takes precedence when both a wildcard and specific resource are include velero restore create --include-namespaces , ``` -### --include-resources +### --include-resources + +Kubernetes resources to include in the backup, formatted as resource.group, such as storageclasses.storage.k8s.io (use `*` for all resources). * Backup all deployments in the cluster. @@ -49,7 +53,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include ### --include-cluster-resources - This option can have three possible values: +Includes cluster-scoped resources. This option can have three possible values: * `true`: all cluster-scoped resources are included. @@ -78,7 +82,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Backup a namespace and include cluster-scoped resources. ```bash - velero backup create --include-namespaces --include-cluster-resources=true + velero backup create --include-namespaces --include-cluster-resources=true ``` ### --selector @@ -104,6 +108,8 @@ Wildcard excludes are ignored. ### --exclude-namespaces +Namespaces to exclude. + * Exclude kube-system from the cluster backup. ```bash @@ -118,6 +124,8 @@ Wildcard excludes are ignored. ### --exclude-resources +Kubernetes resources to exclude, formatted as resource.group, such as storageclasses.storage.k8s.io. + * Exclude secrets from the backup. ```bash @@ -133,6 +141,3 @@ Wildcard excludes are ignored. ### velero.io/exclude-from-backup=true * Resources with the label `velero.io/exclude-from-backup=true` are not included in backup, even if it contains a matching selector label. - - - From 99486a50f7cf46b805ba11ebdea33412dd43745d Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 3 May 2022 09:44:51 -0400 Subject: [PATCH 122/366] Add info about enviroment variables in pre/post hooks Signed-off-by: Abigail McCarthy --- site/content/docs/main/backup-hooks.md | 27 +++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/site/content/docs/main/backup-hooks.md b/site/content/docs/main/backup-hooks.md index 7c849cf0a0..30760e5246 100644 --- a/site/content/docs/main/backup-hooks.md +++ b/site/content/docs/main/backup-hooks.md @@ -26,21 +26,21 @@ You can use the following annotations on a pod to make Velero execute a hook whe * `pre.hook.backup.velero.io/command` * The command to execute. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]` * `pre.hook.backup.velero.io/on-error` - * What to do if the command returns a non-zero exit code. Defaults to Fail. Valid values are Fail and Continue. Optional. + * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. * `pre.hook.backup.velero.io/timeout` - * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults to 30s. Optional. + * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults is 30s. Optional. #### Post hooks * `post.hook.backup.velero.io/container` - * The container where the command should be executed. Defaults to the first container in the pod. Optional. + * The container where the command should be executed. Default is the first container in the pod. Optional. * `post.hook.backup.velero.io/command` * The command to execute. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]` * `post.hook.backup.velero.io/on-error` - * What to do if the command returns a non-zero exit code. Defaults to Fail. Valid values are Fail and Continue. Optional. + * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. * `post.hook.backup.velero.io/timeout` - * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults to 30s. Optional. + * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults is 30s. Optional. ### Specifying Hooks in the Backup Spec @@ -86,6 +86,23 @@ To use multiple commands, wrap your target command in a shell and separate them pre.hook.backup.velero.io/command='["/bin/bash", "-c", "echo hello > hello.txt && echo goodbye > goodbye.txt"]' ``` +## Using Environment Variables in pre and post hooks + +You are able to use environment variables from your pods in your pre and post hook commands by including a shell command before using the environment variable. For example, MYSQL_ROOT_PASSWORD is an environment variable defined in pod called "mysql". To use MYSQL_ROOT_PASSWORD in your pre-hook, you'd include a shell, like `/bin/sh`, before calling your environment variable: + +``` +pre: +- exec: + container: mysql + command: + - /bin/sh + - -c + - mysql --password=$MYSQL_ROOT_PASSWORD -e "FLUSH TABLES WITH READ LOCK" + onError: Fail +``` + +Your pod must have integrated a shell to use environment variables. + [1]: api-types/backup.md [2]: https://github.com/vmware-tanzu/velero/blob/main/examples/nginx-app/with-pv.yaml From 89e90d9be489f880c5ad5c504840dea3069859bf Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 3 May 2022 05:23:06 +0800 Subject: [PATCH 123/366] Delete orphan CSI snapshots in backup sync controller This commit makes backup sync controller delete the volumesnapshot and volumesnapshotcontent created by the backup which is cleaned up as orphan Signed-off-by: Daniel Jiang --- Makefile | 1 + changelogs/unreleased/4887-reasonerjt | 1 + pkg/cmd/server/server.go | 3 +- pkg/controller/backup_controller.go | 2 +- pkg/controller/backup_sync_controller.go | 32 ++++++++++++++++++- pkg/controller/backup_sync_controller_test.go | 3 ++ pkg/util/csi/reset.go | 6 +++- 7 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/4887-reasonerjt diff --git a/Makefile b/Makefile index df82b92ef4..8dcf6ee509 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,7 @@ all-containers: container-builder-env @$(MAKE) --no-print-directory container BIN=velero-restic-restore-helper local: build-dirs +# Add DEBUG=1 to enable debug locally GOOS=$(GOOS) \ GOARCH=$(GOARCH) \ VERSION=$(VERSION) \ diff --git a/changelogs/unreleased/4887-reasonerjt b/changelogs/unreleased/4887-reasonerjt new file mode 100644 index 0000000000..069850f9c5 --- /dev/null +++ b/changelogs/unreleased/4887-reasonerjt @@ -0,0 +1 @@ +Delete orphan CSI snapshots in backup sync controller \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index e366ca3299..8b8ba16993 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -302,6 +302,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s scheme := runtime.NewScheme() velerov1api.AddToScheme(scheme) corev1api.AddToScheme(scheme) + snapshotv1api.AddToScheme(scheme) ctrl.SetLogger(logrusr.NewLogger(logger)) @@ -599,6 +600,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.mgr.GetClient(), s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), + csiVSLister, s.config.backupSyncPeriod, s.namespace, s.csiSnapshotClient, @@ -863,7 +865,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string if err := s.mgr.Start(s.ctx); err != nil { s.logger.Fatal("Problem starting manager", err) } - return nil } diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 48f35fb823..45559f8b33 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -679,7 +679,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { backup.Status.CSIVolumeSnapshotsAttempted = len(backup.CSISnapshots) for _, vs := range backup.CSISnapshots { - if *vs.Status.ReadyToUse { + if vs.Status != nil && boolptr.IsSetToTrue(vs.Status.ReadyToUse) { backup.Status.CSIVolumeSnapshotsCompleted++ } } diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 6a1f7c5622..2a96b139d5 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -20,7 +20,9 @@ import ( "context" "time" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" kuberrs "k8s.io/apimachinery/pkg/api/errors" @@ -29,6 +31,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" + "github.com/vmware-tanzu/velero/pkg/util/kube" + "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/features" @@ -48,6 +52,7 @@ type backupSyncController struct { kbClient client.Client podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister + csiVSLister snapshotv1listers.VolumeSnapshotLister csiSnapshotClient *snapshotterClientSet.Clientset kubeClient kubernetes.Interface namespace string @@ -62,6 +67,7 @@ func NewBackupSyncController( kbClient client.Client, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, + csiVSLister snapshotv1listers.VolumeSnapshotLister, syncPeriod time.Duration, namespace string, csiSnapshotClient *snapshotterClientSet.Clientset, @@ -85,6 +91,7 @@ func NewBackupSyncController( defaultBackupLocation: defaultBackupLocation, defaultBackupSyncPeriod: syncPeriod, backupLister: backupLister, + csiVSLister: csiVSLister, csiSnapshotClient: csiSnapshotClient, kubeClient: kubeClient, @@ -358,11 +365,34 @@ func (c *backupSyncController) deleteOrphanedBackups(locationName string, backup if backup.Status.Phase != velerov1api.BackupPhaseCompleted || backupStoreBackups.Has(backup.Name) { continue } - if err := c.backupClient.Backups(backup.Namespace).Delete(context.TODO(), backup.Name, metav1.DeleteOptions{}); err != nil { log.WithError(errors.WithStack(err)).Error("Error deleting orphaned backup from cluster") } else { log.Debug("Deleted orphaned backup from cluster") + c.deleteCSISnapshotsByBackup(backup.Name, log) + } + } +} + +func (c *backupSyncController) deleteCSISnapshotsByBackup(backupName string, log logrus.FieldLogger) { + if !features.IsEnabled(velerov1api.CSIFeatureFlag) { + return + } + m := client.MatchingLabels{velerov1api.BackupNameLabel: label.GetValidName(backupName)} + if vsList, err := c.csiVSLister.List(label.NewSelectorForBackup(label.GetValidName(backupName))); err != nil { + log.WithError(err).Warnf("Failed to list volumesnapshots for backup: %s, the deletion will be skipped", backupName) + } else { + for _, vs := range vsList { + name := kube.NamespaceAndName(vs.GetObjectMeta()) + log.Debugf("Deleting volumesnapshot %s", name) + if err := c.kbClient.Delete(context.TODO(), vs); err != nil { + log.WithError(err).Warnf("Failed to delete volumesnapshot %s", name) + } } } + vsc := &snapshotv1api.VolumeSnapshotContent{} + log.Debugf("Deleting volumesnapshotcontents for backup: %s", backupName) + if err := c.kbClient.DeleteAllOf(context.TODO(), vsc, m); err != nil { + log.WithError(err).Warnf("Failed to delete volumesnapshotcontents for backup: %s", backupName) + } } diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 0b615bdb54..6ba8253733 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -344,6 +344,7 @@ func TestBackupSyncControllerRun(t *testing.T) { fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), + nil, // csiVSLister time.Duration(0), test.namespace, nil, // csiSnapshotClient @@ -565,6 +566,7 @@ func TestDeleteOrphanedBackups(t *testing.T) { fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), + nil, // csiVSLister time.Duration(0), test.namespace, nil, // csiSnapshotClient @@ -659,6 +661,7 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), + nil, // csiVSLister time.Duration(0), test.namespace, nil, // csiSnapshotClient diff --git a/pkg/util/csi/reset.go b/pkg/util/csi/reset.go index efe10ad011..5065aae780 100644 --- a/pkg/util/csi/reset.go +++ b/pkg/util/csi/reset.go @@ -37,6 +37,10 @@ func ResetVolumeSnapshotContent(snapCont *snapshotv1api.VolumeSnapshotContent) e return fmt.Errorf("the volumesnapshotcontent '%s' does not have snapshothandle set", snapCont.Name) } - snapCont.Spec.VolumeSnapshotRef = corev1.ObjectReference{} + // set the VolumeSnapshotRef to non-existing one to bypass the validation webhook + snapCont.Spec.VolumeSnapshotRef = corev1.ObjectReference{ + Namespace: fmt.Sprintf("ns-%s", snapCont.UID), + Name: fmt.Sprintf("name-%s", snapCont.UID), + } return nil } From 6421f8ad02815ab6d1fdfbc3a22b90d30b29f180 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 2 May 2022 16:37:28 +0800 Subject: [PATCH 124/366] Make waiting VolumeSnapshot to ready process parallel. Signed-off-by: Xun Jiang --- changelogs/unreleased/4889-jxun | 1 + go.mod | 2 ++ go.sum | 28 ++++++++++++++++++ pkg/cmd/server/server.go | 1 + pkg/controller/backup_controller.go | 45 +++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 changelogs/unreleased/4889-jxun diff --git a/changelogs/unreleased/4889-jxun b/changelogs/unreleased/4889-jxun new file mode 100644 index 0000000000..332bf21d0a --- /dev/null +++ b/changelogs/unreleased/4889-jxun @@ -0,0 +1 @@ +Make waiting VolumeSnapshot to ready process parallel. \ No newline at end of file diff --git a/go.mod b/go.mod index 2c7517d35f..63f1fa9ff1 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/Azure/go-autorest/autorest v0.11.21 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.3.0 + github.com/apex/log v1.9.0 github.com/aws/aws-sdk-go v1.28.2 github.com/bombsimon/logrusr v1.1.0 github.com/evanphx/json-patch v4.11.0+incompatible @@ -37,6 +38,7 @@ require ( golang.org/x/mod v0.4.2 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/api v0.56.0 google.golang.org/grpc v1.40.0 k8s.io/api v0.22.2 diff --git a/go.sum b/go.sum index 5be785c5fc..5b8d4bc7d7 100644 --- a/go.sum +++ b/go.sum @@ -112,14 +112,21 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= +github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= +github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.28.2 h1:j5IXG9CdyLfcVfICqo1PXVv+rua+QQHbkXuvuU/JF+8= github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -424,6 +431,7 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -465,6 +473,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= @@ -473,6 +483,7 @@ github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqf github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -485,6 +496,7 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -536,6 +548,7 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -588,6 +601,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -595,6 +609,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -604,7 +619,10 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -644,6 +662,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -712,6 +737,7 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -840,6 +866,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1183,6 +1210,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 8b8ba16993..b9d4f18dbb 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -650,6 +650,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.metrics, s.config.formatFlag.Parse(), csiVSLister, + s.csiSnapshotClient, csiVSCLister, csiVSClassLister, backupStoreGetter, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 45559f8b33..2a168c0215 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -27,9 +27,11 @@ import ( "os" "time" + "github.com/apex/log" jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -37,11 +39,13 @@ import ( "k8s.io/apimachinery/pkg/util/clock" kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" "github.com/vmware-tanzu/velero/pkg/util/csi" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/storage" @@ -87,6 +91,7 @@ type backupController struct { backupStoreGetter persistence.ObjectBackupStoreGetter formatFlag logging.Format volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister + volumeSnapshotClient *snapshotterClientSet.Clientset volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister } @@ -109,6 +114,7 @@ func NewBackupController( metrics *metrics.ServerMetrics, formatFlag logging.Format, volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, + volumeSnapshotClient *snapshotterClientSet.Clientset, volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister, backupStoreGetter persistence.ObjectBackupStoreGetter, @@ -132,6 +138,7 @@ func NewBackupController( metrics: metrics, formatFlag: formatFlag, volumeSnapshotLister: volumeSnapshotLister, + volumeSnapshotClient: volumeSnapshotClient, volumeSnapshotContentLister: volumeSnapshotContentLister, volumeSnapshotClassLister: volumesnapshotClassLister, backupStoreGetter: backupStoreGetter, @@ -638,6 +645,11 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { backupLog.Error(err) } + err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots) + if err != nil { + backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) + } + backup.CSISnapshots = volumeSnapshots } @@ -865,3 +877,36 @@ func encodeToJSONGzip(data interface{}, desc string) (*bytes.Buffer, []error) { return buf, nil } + +// Waiting for VolumeSnapshot ReadyTosue to true is time consuming. Try to make the process parallel by +// using goroutine here instead of waiting in CSI plugin, because it's not easy to make BackupItemAction +// parallel by now. After BackupItemAction parallel is implemented, this logic should be moved to CSI plugin +// as https://github.com/vmware-tanzu/velero-plugin-for-csi/pull/100 +func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []*snapshotv1api.VolumeSnapshot) error { + eg, _ := errgroup.WithContext(ctx) + timeout := 10 * time.Minute + interval := 5 * time.Second + + for _, vs := range volumesnapshots { + volumeSnapshot := vs + eg.Go(func() error { + err := wait.PollImmediate(interval, timeout, func() (bool, error) { + tmpVS, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(volumeSnapshot.Namespace).Get(ctx, volumeSnapshot.Name, metav1.GetOptions{}) + if err != nil { + return false, errors.Wrapf(err, fmt.Sprintf("failed to get volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)) + } + if tmpVS.Status == nil || tmpVS.Status.BoundVolumeSnapshotContentName == nil || !boolptr.IsSetToTrue(tmpVS.Status.ReadyToUse) { + log.Infof("Waiting for CSI driver to reconcile volumesnapshot %s/%s. Retrying in %ds", volumeSnapshot.Namespace, volumeSnapshot.Name, interval/time.Second) + return false, nil + } + + return true, nil + }) + if err == wait.ErrWaitTimeout { + log.Errorf("Timed out awaiting reconciliation of volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name) + } + return err + }) + } + return eg.Wait() +} From a69bd8b1f3a896684e4083e2689fbd57f19c654d Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 21 Apr 2022 17:17:28 +0800 Subject: [PATCH 125/366] Remove VolumeSnapshot created during backup, when the VolumeSnapshotClass's DeletionPolicy is set to Delete. 1. Delete VolumeSnapshot directly when DeletionPolicy set to Retain. 2. Change VolumeSnapshotContent's DeletionPolicy to Retain, then delete VolumeSnapshot. After that delete VolumeSnapshotContent and change VSC DeletionPolicy to Delete back, then re-create the VolumeSnapshotContent. Signed-off-by: Xun Jiang --- changelogs/unreleased/4858-jxun | 1 + pkg/controller/backup_controller.go | 141 ++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 changelogs/unreleased/4858-jxun diff --git a/changelogs/unreleased/4858-jxun b/changelogs/unreleased/4858-jxun new file mode 100644 index 0000000000..190637cd92 --- /dev/null +++ b/changelogs/unreleased/4858-jxun @@ -0,0 +1 @@ +Remove VolumeSnapshots created during backup when CSI feature is enabled. \ No newline at end of file diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 2a168c0215..2eb59bf4b2 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -25,6 +25,7 @@ import ( "io" "io/ioutil" "os" + "sync" "time" "github.com/apex/log" @@ -32,6 +33,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" + v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -68,6 +70,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" + "sigs.k8s.io/cluster-api/util/patch" kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -676,6 +679,10 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { backupLog.Error(err) } } + + // Delete the VolumeSnapshots created in the backup, when CSI feature is enabled. + c.deleteVolumeSnapshot(volumeSnapshots, volumeSnapshotContents, *backup, backupLog) + } // Mark completion timestamp before serializing and uploading. @@ -910,3 +917,137 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo } return eg.Wait() } + +// deleteVolumeSnapshot delete VolumeSnapshot created during backup. +// This is used to avoid deleting namespace in cluster triggers the VolumeSnapshot deletion, +// which will cause snapshot deletion on cloud provider, then backup cannot restore the PV. +// If DeletionPolicy is Retain, just delete it. If DeletionPolicy is Delete, need to +// change DeletionPolicy to Retain before deleting VS, then change DeletionPolicy back to Delete. +func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api.VolumeSnapshot, + volumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent, + backup pkgbackup.Request, logger logrus.FieldLogger) { + var wg sync.WaitGroup + vscMap := make(map[string]*snapshotv1api.VolumeSnapshotContent) + for _, vsc := range volumeSnapshotContents { + vscMap[vsc.Name] = vsc + } + + for _, vs := range volumeSnapshots { + wg.Add(1) + go func(vs *snapshotv1api.VolumeSnapshot) { + defer wg.Done() + var vsc *snapshotv1api.VolumeSnapshotContent + modifyVSCFlag := false + if vs.Status.BoundVolumeSnapshotContentName != nil && + len(*vs.Status.BoundVolumeSnapshotContentName) > 0 { + vsc = vscMap[*vs.Status.BoundVolumeSnapshotContentName] + if vsc.Spec.DeletionPolicy == snapshotv1api.VolumeSnapshotContentDelete { + modifyVSCFlag = true + } + } + + // Change VolumeSnapshotContent's DeletionPolicy to Retain before deleting VolumeSnapshot, + // because VolumeSnapshotContent will be deleted by deleting VolumeSnapshot, when + // DeletionPolicy is set to Delete, but Velero needs VSC for cleaning snapshot on cloud + // in backup deletion. + if modifyVSCFlag { + logger.Debugf("Patching VolumeSnapshotContent %s", vsc.Name) + _, err := c.patchVolumeSnapshotContent(vsc, func(req *snapshotv1api.VolumeSnapshotContent) { + req.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentRetain + }) + if err != nil { + logger.Errorf("fail to modify VolumeSnapshotContent %s DeletionPolicy to Retain: %s", vsc.Name, err.Error()) + return + } + + defer func() { + logger.Debugf("Start to recreate VolumeSnapshotContent %s", vsc.Name) + err := c.recreateVolumeSnapshotContent(vsc) + if err != nil { + logger.Errorf("fail to recreate VolumeSnapshotContent %s: %s", vsc.Name, err.Error()) + } + }() + } + + // Delete VolumeSnapshot from cluster + logger.Debugf("Deleting VolumeSnapshotContent %s", vsc.Name) + err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(vs.Namespace).Delete(context.TODO(), vs.Name, metav1.DeleteOptions{}) + if err != nil { + logger.Errorf("fail to delete VolumeSnapshot %s/%s: %s", vs.Namespace, vs.Name, err.Error()) + } + }(vs) + } + + wg.Wait() +} + +func (c *backupController) patchVolumeSnapshotContent(req *snapshotv1api.VolumeSnapshotContent, mutate func(*snapshotv1api.VolumeSnapshotContent)) (*snapshotv1api.VolumeSnapshotContent, error) { + patchHelper, err := patch.NewHelper(req, c.kbClient) + if err != nil { + return nil, errors.Wrap(err, "fail to get patch helper.") + } + + // Mutate + mutate(req) + + if err := patchHelper.Patch(context.TODO(), req); err != nil { + return nil, errors.Wrapf(err, "fail to patch VolumeSnapshotContent %s", req.Name) + } + + return req, nil +} + +// recreateVolumeSnapshotContent will delete then re-create VolumeSnapshotContent, +// because some parameter in VolumeSnapshotContent Spec is immutable, e.g. VolumeSnapshotRef +// and Source. Source is updated to let csi-controller thinks the VSC is statically provsisioned with VS. +// Set VolumeSnapshotRef's UID to nil will let the csi-controller finds out the related VS is gone, then +// VSC can be deleted. +func (c *backupController) recreateVolumeSnapshotContent(vsc *snapshotv1api.VolumeSnapshotContent) error { + timeout := 1 * time.Minute + interval := 1 * time.Second + + err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Delete(context.TODO(), vsc.Name, metav1.DeleteOptions{}) + if err != nil { + return errors.Wrapf(err, "fail to delete VolumeSnapshotContent: %s", vsc.Name) + } + + // Check VolumeSnapshotContents is already deleted, before re-creating it. + err = wait.PollImmediate(interval, timeout, func() (bool, error) { + _, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Get(context.TODO(), vsc.Name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return true, nil + } + return false, errors.Wrapf(err, fmt.Sprintf("failed to get VolumeSnapshotContent %s", vsc.Name)) + } + return false, nil + }) + if err != nil { + return errors.Wrapf(err, "fail to retrieve VolumeSnapshotContent %s info", vsc.Name) + } + + // Make the VolumeSnapshotContent static + vsc.Spec.Source = snapshotv1api.VolumeSnapshotContentSource{ + SnapshotHandle: vsc.Status.SnapshotHandle, + } + // Set VolumeSnapshotRef to none exist one, because VolumeSnapshotContent + // validation webhook will check whether name and namespace are nil. + // external-snapshotter needs Source pointing to snapshot and VolumeSnapshot + // reference's UID to nil to determine the VolumeSnapshotContent is deletable. + vsc.Spec.VolumeSnapshotRef = v1.ObjectReference{ + APIVersion: snapshotv1api.SchemeGroupVersion.String(), + Kind: "VolumeSnapshot", + Namespace: "ns-" + string(vsc.UID), + Name: "name-" + string(vsc.UID), + } + // Revert DeletionPolicy to Delete + vsc.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentDelete + // ResourceVersion shouldn't exist for new creation. + vsc.ResourceVersion = "" + _, err = c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Create(context.TODO(), vsc, metav1.CreateOptions{}) + if err != nil { + return errors.Wrapf(err, "fail to create VolumeSnapshotContent %s", vsc.Name) + } + + return nil +} From da916846b1e86701a1f44d36d736b1bc74eafd6a Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 5 May 2022 08:26:50 -0400 Subject: [PATCH 126/366] continue rather than return for non-matching restore action label When iterating over applicable restore actions, if a non-matching label selector is found, velero should continue to the next action rather than returning from the restoreItem func, which ends up preventing the item's restore entirely. Signed-off-by: Scott Seago --- changelogs/unreleased/4890-sseago | 1 + pkg/restore/restore.go | 2 +- pkg/restore/restore_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4890-sseago diff --git a/changelogs/unreleased/4890-sseago b/changelogs/unreleased/4890-sseago new file mode 100644 index 0000000000..7245436dcb --- /dev/null +++ b/changelogs/unreleased/4890-sseago @@ -0,0 +1 @@ +continue rather than return for non-matching restore action label diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 67681eea5e..496b6cc570 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1106,7 +1106,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso for _, action := range ctx.getApplicableActions(groupResource, namespace) { if !action.Selector.Matches(labels.Set(obj.GetLabels())) { - return warnings, errs + continue } ctx.log.Infof("Executing item action for %v", &groupResource) diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index b31c87f7ca..dadf6bea5a 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -1270,6 +1270,11 @@ func (a *pluggableAction) AppliesTo() (velero.ResourceSelector, error) { return a.selector, nil } +func (a *pluggableAction) addSelector(selector velero.ResourceSelector) *pluggableAction { + a.selector = selector + return a +} + // TestRestoreActionModifications runs restores with restore item actions that modify resources, and // verifies that that the modified item is correctly created in the API. Verification is done by looking // at the full object in the API. @@ -1335,6 +1340,26 @@ func TestRestoreActionModifications(t *testing.T) { test.Pods(builder.ForPod("ns-1", "pod-1").Result()), }, }, + { + name: "action with non-matching label selector doesn't prevent restore", + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).Done(), + apiResources: []*test.APIResource{test.Pods()}, + actions: []velero.RestoreItemAction{ + modifyingActionGetter(func(item *unstructured.Unstructured) { + item.SetLabels(map[string]string{"updated": "true"}) + }).addSelector(velero.ResourceSelector{ + IncludedResources: []string{ + "Pod", + }, + LabelSelector: "nonmatching=label", + }), + }, + want: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "pod-1").Result()), + }, + }, // TODO action that modifies namespace/name - what's the expected behavior? } From dd0b5fea2b93586d46f12d0135d6f27936a23175 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Thu, 5 May 2022 14:24:57 -0400 Subject: [PATCH 127/366] Add wording for using commands in hooks Signed-off-by: Abigail McCarthy --- site/content/docs/main/backup-hooks.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/site/content/docs/main/backup-hooks.md b/site/content/docs/main/backup-hooks.md index 30760e5246..ef38dc7fed 100644 --- a/site/content/docs/main/backup-hooks.md +++ b/site/content/docs/main/backup-hooks.md @@ -24,7 +24,7 @@ You can use the following annotations on a pod to make Velero execute a hook whe * `pre.hook.backup.velero.io/container` * The container where the command should be executed. Defaults to the first container in the pod. Optional. * `pre.hook.backup.velero.io/command` - * The command to execute. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]` + * The command to execute. This command is not executed within a shell by default. If a shell is needed to run your command, include a shell command, like `/bin/sh`, that is supported by the container at the beginning of your command. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]`. See [examples of using pre hook commands](#backup-hook-commands-examples). Optional. * `pre.hook.backup.velero.io/on-error` * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. * `pre.hook.backup.velero.io/timeout` @@ -36,7 +36,7 @@ You can use the following annotations on a pod to make Velero execute a hook whe * `post.hook.backup.velero.io/container` * The container where the command should be executed. Default is the first container in the pod. Optional. * `post.hook.backup.velero.io/command` - * The command to execute. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]` + * The command to execute. This command is not executed within a shell by default. If a shell is needed to run your command, include a shell command, like `/bin/sh`, that is supported by the container at the beginning of your command. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]`. See [examples of using pre hook commands](#backup-hook-commands-examples). Optional. * `post.hook.backup.velero.io/on-error` * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. * `post.hook.backup.velero.io/timeout` @@ -78,7 +78,9 @@ velero backup get nginx-hook-test velero backup logs nginx-hook-test | grep hookCommand ``` -## Using Multiple Commands +## Backup hook commands examples + +### Multiple commands To use multiple commands, wrap your target command in a shell and separate them with `;`, `&&`, or other shell conditional constructs. @@ -86,9 +88,9 @@ To use multiple commands, wrap your target command in a shell and separate them pre.hook.backup.velero.io/command='["/bin/bash", "-c", "echo hello > hello.txt && echo goodbye > goodbye.txt"]' ``` -## Using Environment Variables in pre and post hooks +#### Using environment variables -You are able to use environment variables from your pods in your pre and post hook commands by including a shell command before using the environment variable. For example, MYSQL_ROOT_PASSWORD is an environment variable defined in pod called "mysql". To use MYSQL_ROOT_PASSWORD in your pre-hook, you'd include a shell, like `/bin/sh`, before calling your environment variable: +You are able to use environment variables from your pods in your pre and post hook commands by including a shell command before using the environment variable. For example, `MYSQL_ROOT_PASSWORD` is an environment variable defined in pod called `mysql`. To use `MYSQL_ROOT_PASSWORD` in your pre-hook, you'd include a shell, like `/bin/sh`, before calling your environment variable: ``` pre: @@ -101,7 +103,7 @@ pre: onError: Fail ``` -Your pod must have integrated a shell to use environment variables. +Note that the container must support the shell command you use. [1]: api-types/backup.md From 33bf59a9107cc153212ebb6a5d5bc64a4f8cf94a Mon Sep 17 00:00:00 2001 From: danfengl Date: Sun, 10 Apr 2022 10:20:16 +0000 Subject: [PATCH 128/366] Add snapshot checkpoint for CSI E2E test and fix CSI snapshot name issue 1. Add checkpoint in snapshot E2E test to verify snapshot CR should be created and snapshot should be created in cloud side after backup completion; 2. Fix snapshot name issue that CSI snapshot name in cloud side is not the same with other non-CSI cloud snapshots; Signed-off-by: danfengl --- test/e2e/backup/backup.go | 10 +- test/e2e/backups/deletion.go | 58 ++++++--- test/e2e/backups/sync_backups.go | 14 ++- test/e2e/basic/enable_api_group_versions.go | 7 +- test/e2e/bsl-mgmt/deletion.go | 55 +++++--- test/e2e/e2e_suite_test.go | 2 + test/e2e/privilegesmgmt/ssr.go | 14 ++- test/e2e/test/test.go | 15 ++- test/e2e/types.go | 9 +- test/e2e/upgrade/upgrade.go | 51 ++++++-- test/e2e/util/csi/common.go | 132 ++++++++++++++++++++ test/e2e/util/k8s/common.go | 4 +- test/e2e/util/k8s/persistentvolumes.go | 28 +++++ test/e2e/util/kibishii/kibishii_utils.go | 47 +++++-- test/e2e/util/providers/azure_utils.go | 39 ++++-- test/e2e/util/providers/common.go | 38 +++++- test/e2e/util/velero/velero_utils.go | 41 +++++- 17 files changed, 474 insertions(+), 90 deletions(-) create mode 100644 test/e2e/util/csi/common.go create mode 100644 test/e2e/util/k8s/persistentvolumes.go diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 757c2e589b..103568e058 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -39,13 +39,17 @@ func BackupRestoreWithRestic() { } func BackupRestoreTest(useVolumeSnapshots bool) { + kibishiiNamespace := "kibishii-workload" var ( backupName, restoreName string + client TestClient + err error ) - kibishiiNamespace := "kibishii-workload" - client, err := NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { Skip("Volume snapshots not supported on kind") diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 69c8bd3558..0c5c2deed4 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -25,6 +25,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/pkg/errors" + waitutil "k8s.io/apimachinery/pkg/util/wait" . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" @@ -47,11 +48,14 @@ func BackupDeletionWithRestic() { func backup_deletion_test(useVolumeSnapshots bool) { var ( backupName string + client TestClient + err error ) - client, err := NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup deletion tests") - + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { Skip("Volume snapshots not supported on kind") @@ -98,11 +102,13 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa if err := CreateNamespace(oneHourTimeout, client, deletionTest); err != nil { return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", deletionTest) } - defer func() { - if err := DeleteNamespace(context.Background(), client, deletionTest, true); err != nil { - fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", deletionTest)) - } - }() + if !VeleroCfg.Debug { + defer func() { + if err := DeleteNamespace(context.Background(), client, deletionTest, true); err != nil { + fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", deletionTest)) + } + }() + } if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots); err != nil { @@ -131,28 +137,50 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa if err != nil { return err } + var snapshotCheckPoint SnapshotCheckPoint if useVolumeSnapshots { - var snapshotCheckPoint SnapshotCheckPoint - snapshotCheckPoint.ExpectCount = 2 - snapshotCheckPoint.NamespaceBackedUp = deletionTest - err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName, snapshotCheckPoint) + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, deletionTest, backupName, KibishiiPodNameList) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") if err != nil { - return err + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") + } + err = WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, + backupName, snapshotCheckPoint) + if err != nil { + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } err = DeleteBackupResource(context.Background(), veleroCLI, backupName) if err != nil { return err } + if useVolumeSnapshots { + err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, + backupName, snapshotCheckPoint) + if err != nil { + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") + } + } err = ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, 5) if err != nil { return err } if useVolumeSnapshots { - err = SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName) + err = waitutil.PollImmediate(30*time.Second, 3*time.Minute, + func() (bool, error) { + err := SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + bslConfig, backupName, snapshotCheckPoint) + if err == nil { + return true, nil + } + return false, err + }) if err != nil { - return err + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index 8d1eb3d166..2fb2df6d7b 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -52,10 +52,16 @@ func (b *SyncBackups) Init() { func BackupsSyncTest() { test := new(SyncBackups) - client, err := NewTestClient() - if err != nil { - println(err.Error()) - } + var ( + client TestClient + err error + ) + + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) + Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") BeforeEach(func() { diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 1eac63b024..c307163873 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -45,10 +45,13 @@ func APIGropuVersionsTest() { resource, group string err error ctx = context.Background() + client TestClient ) - client, err := NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for group version tests") + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) BeforeEach(func() { resource = "rockbands" diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index a034b2da3d..f385d73712 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -19,6 +19,7 @@ import ( "context" "flag" "fmt" + "strings" "time" "github.com/google/go-cmp/cmp" @@ -28,6 +29,7 @@ import ( . "github.com/onsi/gomega" . "github.com/vmware-tanzu/velero/test/e2e" + util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" @@ -52,8 +54,16 @@ func BslDeletionWithRestic() { BslDeletionTest(false) } func BslDeletionTest(useVolumeSnapshots bool) { - client, err := NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup deletion tests") + var ( + client TestClient + err error + ) + + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) + less := func(a, b string) bool { return a < b } BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { @@ -198,19 +208,28 @@ func BslDeletionTest(useVolumeSnapshots bool) { Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, backupName_2, 1)).To(Succeed()) }) + } else if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + By(fmt.Sprintf("CSI VolumeSnapshotContent CR in backup %s should be created", backupName_1), func() { + Expect(util.CheckVolumeSnapshotCR(client, []string{podName_1}, bslDeletionTestNs, backupName_1)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content CR.") + }) + + By(fmt.Sprintf("CSI VolumeSnapshotContent CR in backup %s should be created", backupName_2), func() { + Expect(util.CheckVolumeSnapshotCR(client, []string{podName_2}, bslDeletionTestNs, backupName_2)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content CR.") + }) } + var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_1), func() { - snapshotCheckPoint.ExpectCount = 1 - snapshotCheckPoint.PodName = podName_1 - Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.AdditionalBSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_2), func() { - snapshotCheckPoint.ExpectCount = 1 - snapshotCheckPoint.PodName = podName_2 + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") var BSLCredentials, BSLConfig string if VeleroCfg.CloudProvider == "vsphere" { BSLCredentials = VeleroCfg.AdditionalBSLCredentials @@ -219,7 +238,8 @@ func BslDeletionTest(useVolumeSnapshots bool) { BSLCredentials = VeleroCfg.CloudCredentialsFile BSLConfig = VeleroCfg.BSLConfig } - Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, BSLCredentials, VeleroCfg.AdditionalBSLBucket, BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) }) @@ -234,12 +254,12 @@ func BslDeletionTest(useVolumeSnapshots bool) { }) } - By(fmt.Sprintf("Verify if backup %s is created or not", backupName_1), func() { + By(fmt.Sprintf("Backup 1 %s should be created.", backupName_1), func() { Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, backupName_1, 10*time.Minute)).To(Succeed()) }) - By(fmt.Sprintf("Verify if backup %s is created or not", backupName_2), func() { + By(fmt.Sprintf("Backup 2 %s should be created.", backupName_2), func() { Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, backupName_2, 10*time.Minute)).To(Succeed()) }) @@ -298,15 +318,14 @@ func BslDeletionTest(useVolumeSnapshots bool) { var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs By(fmt.Sprintf("Snapshot should not be deleted in cloud object store after deleting bsl %s", backupLocation_1), func() { - snapshotCheckPoint.ExpectCount = 1 - snapshotCheckPoint.PodName = podName_1 - Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) By(fmt.Sprintf("Snapshot should not be deleted in cloud object store after deleting bsl %s", backupLocation_2), func() { - snapshotCheckPoint.ExpectCount = 1 - snapshotCheckPoint.PodName = podName_2 var BSLCredentials, BSLConfig string if VeleroCfg.CloudProvider == "vsphere" { BSLCredentials = VeleroCfg.AdditionalBSLCredentials @@ -315,7 +334,9 @@ func BslDeletionTest(useVolumeSnapshots bool) { BSLCredentials = VeleroCfg.CloudCredentialsFile BSLConfig = VeleroCfg.BSLConfig } - Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, BSLCredentials, VeleroCfg.AdditionalBSLBucket, BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) }) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a0d4c3d59f..a2d98727da 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -94,9 +94,11 @@ var _ = Describe("[ResourceFiltering][IncludeResources][Backup] Velero test on i var _ = Describe("[ResourceFiltering][IncludeResources][Restore] Velero test on include resources from the cluster restore", RestoreWithIncludeResources) var _ = Describe("[ResourceFiltering][LabelSelector] Velero test on backup include resources matching the label selector", BackupWithLabelSelector) var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup deletion", BackupDeletionWithRestic) + var _ = Describe("[Backups][Deletion][Snapshot] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) + var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index 90d7cd7f7f..ee1ed5631f 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -36,11 +36,15 @@ import ( func SSRTest() { testNS := "ssr-test" - client, err := NewTestClient() - if err != nil { - println(err.Error()) - } - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + var ( + client TestClient + err error + ) + + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) BeforeEach(func() { flag.Parse() diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 995b4fda70..811b69ac94 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -72,9 +72,11 @@ var TestClientInstance TestClient func TestFunc(test VeleroBackupRestoreTest) func() { return func() { - var err error - TestClientInstance, err = NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + By("Create test client instance", func() { + var err error + TestClientInstance, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) Expect(test.Init()).To(Succeed(), "Failed to instantiate test cases") BeforeEach(func() { flag.Parse() @@ -99,8 +101,11 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { return func() { var err error var countIt int - TestClientInstance, err = NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + By("Create test client instance", func() { + TestClientInstance, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) + //Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") for k := range tests { Expect(tests[k].Init()).To(Succeed(), fmt.Sprintf("Failed to instantiate test %s case", tests[k].GetTestMsg().Desc)) } diff --git a/test/e2e/types.go b/test/e2e/types.go index 5a0a8ae9bb..d24c40635a 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -55,7 +55,10 @@ type VerleroConfig struct { type SnapshotCheckPoint struct { NamespaceBackedUp string - SnapshotIDList []string - ExpectCount int - PodName string + // SnapshotIDList is for Azure CSI Verification + // we can get SnapshotID from VolumeSnapshotContent from a certain backup + SnapshotIDList []string + ExpectCount int + PodName []string + EnableCSI bool } diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 0d98908d55..aff943aa6c 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -27,8 +27,10 @@ import ( . "github.com/onsi/gomega" . "github.com/vmware-tanzu/velero/test/e2e" + util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" + . "github.com/vmware-tanzu/velero/test/e2e/util/providers" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -74,10 +76,13 @@ func BackupUpgradeRestoreWithRestic() { func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero UpgradeFromVelero) { var ( backupName, restoreName string + client TestClient + err error ) - - client, err := NewTestClient() - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + By("Create test client instance", func() { + client, err = NewTestClient() + Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + }) BeforeEach(func() { if !VeleroCfg.InstallVelero { Skip("Upgrade test should not be triggered if VeleroCfg.InstallVelero is set to false") @@ -166,11 +171,27 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade }) }) - if useVolumeSnapshots && VeleroCfg.CloudProvider == "vsphere" { - // TODO - remove after upload progress monitoring is implemented - By("Waiting for vSphere uploads to complete", func() { - Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, - upgradeNamespace)).To(Succeed()) + if useVolumeSnapshots { + if VeleroCfg.CloudProvider == "vsphere" { + // TODO - remove after upload progress monitoring is implemented + By("Waiting for vSphere uploads to complete", func() { + Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, + upgradeNamespace)).To(Succeed()) + }) + } else if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + By("CSI VolumeSnapshotContent CR should be created", func() { + Expect(util.CheckVolumeSnapshotCR(client, KibishiiPodNameList, upgradeNamespace, backupName)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content") + }) + } + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.NamespaceBackedUp = upgradeNamespace + By("Snapshot should be created in cloud object store", func() { + snapshotCheckPoint, err := GetSnapshotCheckPoint(client, VeleroCfg, 2, + upgradeNamespace, backupName, KibishiiPodNameList) + Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLConfig, backupName, snapshotCheckPoint)).To(Succeed()) }) } @@ -179,6 +200,20 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade fmt.Sprintf("failed to delete namespace %s", upgradeNamespace)) }) + if useVolumeSnapshots && VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + // Upgrade test is not running daily since no CSI plugin v1.0 released, because builds before + // v1.0 have issues to fail upgrade case. + By("Sleep 5 minutes to avoid snapshot recreated by unknown reason ", func() { + time.Sleep(5 * time.Minute) + }) + // TODO: add WaitUntilSnapshotsNotExistInCloud verification when Upgrade test is enabled in nightly + // err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + // VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, + // backupName, snapshotCheckPoint) + // if err != nil { + // return errors.Wrap(err, "exceed waiting for snapshot created in cloud") + // } + } // the snapshots of AWS may be still in pending status when do the restore, wait for a while // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed diff --git a/test/e2e/util/csi/common.go b/test/e2e/util/csi/common.go new file mode 100644 index 0000000000..b517b5f541 --- /dev/null +++ b/test/e2e/util/csi/common.go @@ -0,0 +1,132 @@ +/* +Copyright 2020 the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "strings" + + "github.com/pkg/errors" + + snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" +) + +func GetClients() (*kubernetes.Clientset, *snapshotterClientSet.Clientset, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + clientConfig, err := kubeConfig.ClientConfig() + if err != nil { + return nil, nil, errors.WithStack(err) + } + + client, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + return nil, nil, errors.WithStack(err) + } + + snapshotterClient, err := snapshotterClientSet.NewForConfig(clientConfig) + if err != nil { + return nil, nil, errors.WithStack(err) + } + + return client, snapshotterClient, nil +} + +func GetCsiSnapshotHandle(client TestClient, backupName string) ([]string, error) { + _, snapshotClient, err := GetClients() + if err != nil { + return nil, err + } + vscList, err1 := snapshotClient.SnapshotV1beta1().VolumeSnapshotContents().List(context.TODO(), metav1.ListOptions{}) + if err1 != nil { + return nil, err + } + var snapshotHandleList []string + for _, i := range vscList.Items { + if i.Status.SnapshotHandle == nil { + fmt.Println("SnapshotHandle is nil") + continue + } + fmt.Println(*i.Status.SnapshotHandle) + if i.Labels["velero.io/backup-name"] == backupName { + tmp := strings.Split(*i.Status.SnapshotHandle, "/") + snapshotHandleList = append(snapshotHandleList, tmp[len(tmp)-1]) + } + } + if err != nil { + return nil, err + } + if len(snapshotHandleList) == 0 { + return snapshotHandleList, errors.New(fmt.Sprintf("No VolumeSnapshotContent from backup %s", backupName)) + } + return snapshotHandleList, nil +} +func GetVolumeSnapshotContentNameByPod(client TestClient, podName, namespace, backupName string) (string, error) { + pvcList, err := GetPvcByPodName(context.Background(), namespace, podName) + if err != nil { + return "", err + } + if len(pvcList) != 1 { + return "", errors.New(fmt.Sprintf("Only 1 PVC of pod %s should be found under namespace %s", podName, namespace)) + } + pvList, err := GetPvByPvc(context.Background(), namespace, pvcList[0]) + if err != nil { + return "", err + } + if len(pvList) != 1 { + return "", errors.New(fmt.Sprintf("Only 1 PV of PVC %s pod %s should be found under namespace %s", pvcList[0], podName, namespace)) + } + pv_value, err := GetPersistentVolume(context.Background(), client, "", pvList[0]) + fmt.Println(pv_value.Annotations["pv.kubernetes.io/provisioned-by"]) + if err != nil { + return "", err + } + _, snapshotClient, err := GetClients() + if err != nil { + return "", err + } + vsList, err := snapshotClient.SnapshotV1beta1().VolumeSnapshots(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return "", err + } + for _, i := range vsList.Items { + if pvcList[0] == *i.Spec.Source.PersistentVolumeClaimName && + i.Labels["velero.io/backup-name"] == backupName { + return *i.Status.BoundVolumeSnapshotContentName, nil + } + } + return "", errors.New(fmt.Sprintf("Fail to get VolumeSnapshotContentName for pod %s under namespace %s", podName, namespace)) +} + +func CheckVolumeSnapshotCR(client TestClient, pods []string, nampespace, backupName string) error { + for _, podName := range pods { + if snapshotContentName, err := GetVolumeSnapshotContentNameByPod(client, podName, nampespace, backupName); err != nil || snapshotContentName == "" { + return errors.Wrap(err, "Fail to get Azure CSI snapshot content") + } else { + fmt.Println("Found volumesnapshotcontent: " + snapshotContentName) + } + } + return nil +} diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 06d7cdaea3..43812a74a4 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -99,7 +99,7 @@ func GetPvcByPodName(ctx context.Context, namespace, podName string) ([]string, return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) } -func GetPvByPvc(ctx context.Context, pvc string) ([]string, error) { +func GetPvByPvc(ctx context.Context, namespace, pvc string) ([]string, error) { // Example: // NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE // pvc-3f784366-58db-40b2-8fec-77307807e74b 1Gi RWO Delete Bound bsl-deletion/kibishii-data-kibishii-deployment-0 kibishii-storage-class 6h41m @@ -110,7 +110,7 @@ func GetPvByPvc(ctx context.Context, pvc string) ([]string, error) { CmdLine2 := &common.OsCommandLine{ Cmd: "grep", - Args: []string{pvc}, + Args: []string{namespace + "/" + pvc}, } CmdLine3 := &common.OsCommandLine{ diff --git a/test/e2e/util/k8s/persistentvolumes.go b/test/e2e/util/k8s/persistentvolumes.go new file mode 100644 index 0000000000..2f414cda44 --- /dev/null +++ b/test/e2e/util/k8s/persistentvolumes.go @@ -0,0 +1,28 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetPersistentVolume(ctx context.Context, client TestClient, namespace string, persistentVolume string) (*corev1.PersistentVolume, error) { + return client.ClientGo.CoreV1().PersistentVolumes().Get(ctx, persistentVolume, metav1.GetOptions{}) +} diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index d99b1b939a..005354f28d 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -28,7 +28,9 @@ import ( veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" . "github.com/vmware-tanzu/velero/test/e2e" + util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/providers" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -36,6 +38,8 @@ const ( jumpPadPod = "jump-pad" ) +var KibishiiPodNameList = []string{"kibishii-deployment-0", "kibishii-deployment-1"} + // RunKibishiiTests runs kibishii tests on the provider. func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, restoreName, backupLocation, kibishiiNamespace string, useVolumeSnapshots bool) error { @@ -47,7 +51,7 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re veleroFeatures := VeleroCfg.Features kibishiiDirectory := VeleroCfg.KibishiiDirectory if _, err := GetNamespace(context.Background(), client, kibishiiNamespace); err == nil { - fmt.Printf("Workload namespace %s exists, delete it first.", kibishiiNamespace) + fmt.Printf("Workload namespace %s exists, delete it first.\n", kibishiiNamespace) if err = DeleteNamespace(context.Background(), client, kibishiiNamespace, true); err != nil { fmt.Println(errors.Wrapf(err, "failed to delete the namespace %q", kibishiiNamespace)) } @@ -73,19 +77,46 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re RunDebug(context.Background(), veleroCLI, veleroNamespace, backupName, "") return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace) } - - if providerName == "vsphere" && useVolumeSnapshots { - // Wait for uploads started by the Velero Plug-in for vSphere to complete - // TODO - remove after upload progress monitoring is implemented - fmt.Println("Waiting for vSphere uploads to complete") - if err := WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, kibishiiNamespace); err != nil { - return errors.Wrapf(err, "Error waiting for uploads to complete") + var snapshotCheckPoint SnapshotCheckPoint + var err error + if useVolumeSnapshots { + if providerName == "vsphere" { + // Wait for uploads started by the Velero Plug-in for vSphere to complete + // TODO - remove after upload progress monitoring is implemented + fmt.Println("Waiting for vSphere uploads to complete") + if err := WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, kibishiiNamespace); err != nil { + return errors.Wrapf(err, "Error waiting for uploads to complete") + } + } else if providerName == "azure" && strings.EqualFold(veleroFeatures, "EnableCSI") { + if err := util.CheckVolumeSnapshotCR(client, KibishiiPodNameList, kibishiiNamespace, backupName); err != nil { + return errors.Wrapf(err, "Fail to get Azure CSI snapshot content") + } + } + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, kibishiiNamespace, backupName, KibishiiPodNameList) + if err != nil { + return errors.Wrap(err, "Fail to get snapshot checkpoint") + } + err = WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, + backupName, snapshotCheckPoint) + if err != nil { + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } + fmt.Printf("Simulating a disaster by removing namespace %s\n", kibishiiNamespace) if err := DeleteNamespace(oneHourTimeout, client, kibishiiNamespace, true); err != nil { return errors.Wrapf(err, "failed to delete namespace %s", kibishiiNamespace) } + if useVolumeSnapshots && providerName == "azure" && strings.EqualFold(veleroFeatures, "EnableCSI") { + err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, + backupName, snapshotCheckPoint) + if err != nil { + return errors.Wrap(err, "exceed waiting for snapshot created in cloud") + } + } + time.Sleep(5 * time.Minute) // the snapshots of AWS may be still in pending status when do the restore, wait for a while // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 diff --git a/test/e2e/util/providers/azure_utils.go b/test/e2e/util/providers/azure_utils.go index a34b05a416..8c579c74fb 100644 --- a/test/e2e/util/providers/azure_utils.go +++ b/test/e2e/util/providers/azure_utils.go @@ -226,12 +226,11 @@ func deleteBlob(p pipeline.Pipeline, accountName, containerName, blobName string _, err = blobURL.Delete(ctx, azblob.DeleteSnapshotsOptionNone, azblob.BlobAccessConditions{}) return err } -func (s AzureStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) (bool, error) { +func (s AzureStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName string) (bool, error) { accountName, accountKey, err := getStorageCredential(cloudCredentialsFile, bslConfig) if err != nil { log.Fatal("Fail to get : accountName and accountKey, " + err.Error()) } - credential, err := azblob.NewSharedKeyCredential(accountName, accountKey) if err != nil { log.Fatal("Invalid credentials with error: " + err.Error()) @@ -250,7 +249,7 @@ func (s AzureStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPref _, err = containerURL.Create(ctx, azblob.Metadata{}, azblob.PublicAccessNone) handleErrors(err) - fmt.Printf("Finding backup %s blobs in Azure container/bucket %s\n", backupObject, containerName) + fmt.Printf("Finding backup %s blobs in Azure container/bucket %s\n", backupName, containerName) for marker := (azblob.Marker{}); marker.NotDone(); { listBlob, err := containerURL.ListBlobsFlatSegment(ctx, marker, azblob.ListBlobsSegmentOptions{}) if err != nil { @@ -259,8 +258,8 @@ func (s AzureStorage) IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPref marker = listBlob.NextMarker for _, blobInfo := range listBlob.Segment.BlobItems { - if strings.Contains(blobInfo.Name, backupObject) { - fmt.Printf("Blob name: %s exist in %s\n", backupObject, blobInfo.Name) + if strings.Contains(blobInfo.Name, backupName) { + fmt.Printf("Blob name: %s exist in %s\n", backupName, blobInfo.Name) return true, nil } } @@ -317,7 +316,7 @@ func mapLookup(data map[string]string) func(string) string { return data[key] } } -func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error { +func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupName string, snapshotCheck SnapshotCheckPoint) error { ctx := context.Background() @@ -351,7 +350,7 @@ func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupO // // if config["snapsIncrementalConfigKey"] is empty, default to nil; otherwise, parse i snapsClient.Authorizer = authorizer snaps := &snapsClient - //return ListByResourceGroup(ctx, snaps, envVars[resourceGroupEnvVar], backupObject, snapshotCount) + //return ListByResourceGroup(ctx, snaps, envVars[resourceGroupEnvVar], backupName, snapshotCount) req, err := snaps.ListByResourceGroupPreparer(ctx, envVars[resourceGroupEnvVar]) if err != nil { return autorest.NewErrorWithError(err, "compute.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing request") @@ -364,11 +363,29 @@ func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupO result, err := snaps.ListByResourceGroupResponder(resp) snapshotCountFound := 0 backupNameInSnapshot := "" + if err != nil { + errors.Wrap(err, fmt.Sprintf("Fail to list snapshots %s\n", envVars[resourceGroupEnvVar])) + } + if result.Value == nil { + errors.New(fmt.Sprintf("No snapshots in Azure resource group %s\n", envVars[resourceGroupEnvVar])) + } for _, v := range *result.Value { - backupNameInSnapshot = *v.Tags["velero.io-backup"] - fmt.Println(backupNameInSnapshot) - if backupObject == backupNameInSnapshot { - snapshotCountFound++ + if snapshotCheck.EnableCSI { + for _, s := range snapshotCheck.SnapshotIDList { + fmt.Println("Azure CSI local snapshot CR: " + s) + fmt.Println("Azure provider snapshot name: " + *v.Name) + if s == *v.Name { + fmt.Printf("Azure snapshot %s is created.\n", s) + snapshotCountFound++ + } + } + } else { + fmt.Println(v.Tags) + backupNameInSnapshot = *v.Tags["velero.io-backup"] + fmt.Println(backupNameInSnapshot) + if backupName == backupNameInSnapshot { + snapshotCountFound++ + } } } if err != nil { diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index 26fc8083cb..accfafec18 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -24,6 +24,8 @@ import ( "github.com/pkg/errors" + waitutil "k8s.io/apimachinery/pkg/util/wait" + . "github.com/vmware-tanzu/velero/test/e2e" velero "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -31,11 +33,11 @@ import ( type ObjectsInStorage interface { IsObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) (bool, error) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupObject string) error - IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObject string, snapshotCheck SnapshotCheckPoint) error + IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupName string, snapshotCheck SnapshotCheckPoint) error } func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { - fmt.Printf("|| VERIFICATION || - Backup %s should exist in storage %s", backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - Backup %s should exist in storage [%s]", backupName, bslPrefix) exist, _ := IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if !exist { return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected", backupName)) @@ -109,9 +111,37 @@ func DeleteObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPr return nil } -func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string) error { +func WaitUntilSnapshotsExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { + err := waitutil.PollImmediate(30*time.Second, 3*time.Minute, + func() (bool, error) { + err := SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + bslConfig, backupName, snapshotCheckPoint) + if err == nil { + return true, nil + } + return false, err + }) + return err +} + +func WaitUntilSnapshotsNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { + err := waitutil.PollImmediate(30*time.Second, 3*time.Minute, + func() (bool, error) { + snapshotCheckPoint.ExpectCount = 0 + err := SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + bslConfig, backupName, snapshotCheckPoint) + if err == nil { + return true, nil + } + return false, err + }) + return err +} + +func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { fmt.Printf("|| VERIFICATION || - Snapshots should not exist in cloud, backup %s\n", backupName) - var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.ExpectCount = 0 err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 4ca48d4152..449f6e0a50 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -40,7 +40,10 @@ import ( cliinstall "github.com/vmware-tanzu/velero/pkg/cmd/cli/install" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" + . "github.com/vmware-tanzu/velero/test/e2e" common "github.com/vmware-tanzu/velero/test/e2e/util/common" + util "github.com/vmware-tanzu/velero/test/e2e/util/csi" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" ) const BackupObjectsPrefix = "backups" @@ -491,7 +494,7 @@ func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, return err } -func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace, podName string) ([]string, error) { +func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace string, podNameList []string) ([]string, error) { checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.spec.resourceHandle.name}{\"=\"}{.status.snapshotID}{\"\\n\"}{end}'") fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) @@ -504,14 +507,20 @@ func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace stdout = strings.Replace(stdout, "'", "", -1) lines := strings.Split(stdout, "\n") var result []string - for _, curLine := range lines { fmt.Println("curLine:" + curLine) curLine = strings.Replace(curLine, "\n", "", -1) if len(curLine) == 0 { continue } - if podName != "" && !strings.Contains(curLine, podName) { + var Exist bool + for _, podName := range podNameList { + if podName != "" && strings.Contains(curLine, podName) { + Exist = true + break + } + } + if !Exist { continue } snapshotID := curLine[strings.LastIndex(curLine, ":")+1:] @@ -519,6 +528,7 @@ func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace snapshotIDDec, _ := b64.StdEncoding.DecodeString(snapshotID) fmt.Println("snapshotIDDec:" + string(snapshotIDDec)) result = append(result, string(snapshotIDDec)) + fmt.Println(result) } fmt.Println(result) return result, nil @@ -797,3 +807,28 @@ func GetResticRepositories(ctx context.Context, veleroNamespace, targetNamespace return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) } + +func GetSnapshotCheckPoint(client TestClient, VeleroCfg VerleroConfig, expectCount int, namespaceBackedUp, backupName string, kibishiiPodNameList []string) (SnapshotCheckPoint, error) { + var snapshotCheckPoint SnapshotCheckPoint + + snapshotCheckPoint.ExpectCount = expectCount + snapshotCheckPoint.NamespaceBackedUp = namespaceBackedUp + snapshotCheckPoint.PodName = kibishiiPodNameList + if strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + snapshotCheckPoint.EnableCSI = true + if err := util.CheckVolumeSnapshotCR(client, kibishiiPodNameList, namespaceBackedUp, backupName); err != nil { + return snapshotCheckPoint, errors.Wrapf(err, "Fail to get Azure CSI snapshot content") + } + var err error + snapshotCheckPoint.SnapshotIDList, err = util.GetCsiSnapshotHandle(client, backupName) + if err != nil { + return snapshotCheckPoint, errors.New(fmt.Sprintf("Fail to get CSI SnapshotHandle for backup %s", backupName)) + } + fmt.Println(snapshotCheckPoint) + if len(snapshotCheckPoint.SnapshotIDList) != expectCount { + return snapshotCheckPoint, errors.New(fmt.Sprintf("Length of SnapshotIDList is not as expected %d", expectCount)) + } + } + fmt.Println(snapshotCheckPoint) + return snapshotCheckPoint, nil +} From 034cda257152febcdc9f6c179e406f2ab6295c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 6 May 2022 14:47:46 +0800 Subject: [PATCH 129/366] Make in-progress PVB/PVR as failed when restic controller restarts to avoid hanging backup/restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make in-progress PVB/PVR as failed when restic controller restarts to avoid hanging backup/restore Fixes #4772 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4893-ywk253100 | 1 + .../pod_volume_backup_controller.go | 68 +++++---- .../pod_volume_backup_controller_test.go | 6 +- .../pod_volume_restore_controller.go | 58 ++++---- .../pod_volume_restore_controller_test.go | 134 ++++++++++++------ pkg/util/kube/utils.go | 10 ++ pkg/util/kube/utils_test.go | 23 +++ 7 files changed, 205 insertions(+), 95 deletions(-) create mode 100644 changelogs/unreleased/4893-ywk253100 diff --git a/changelogs/unreleased/4893-ywk253100 b/changelogs/unreleased/4893-ywk253100 new file mode 100644 index 0000000000..751c57bac9 --- /dev/null +++ b/changelogs/unreleased/4893-ywk253100 @@ -0,0 +1 @@ +Make in-progress PVB/PVR as failed when restic controller restarts to avoid hanging backup/restore \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 80f6f23330..3656bd081f 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -30,7 +30,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -87,37 +86,40 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ log.Info("PodVolumeBackup starting") - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(&pvb, r.Client) - if err != nil { - log.WithError(err).Error("getting patch helper to update this resource") - return ctrl.Result{}, errors.WithStack(err) - } - - defer func() { - // Always attempt to patch the PVB object and status after each reconciliation. - if err := patchHelper.Patch(ctx, &pvb); err != nil { - log.WithError(err).Error("updating PodVolumeBackup resource") - return - } - }() - // Only process items for this node. if pvb.Spec.Node != r.NodeName { return ctrl.Result{}, nil } - // Only process new items. - if pvb.Status.Phase != "" && pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseNew { - log.Debug("PodVolumeBackup is not new, not processing") + switch pvb.Status.Phase { + case "", velerov1api.PodVolumeBackupPhaseNew: + case velerov1api.PodVolumeBackupPhaseInProgress: + original := pvb.DeepCopy() + pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed + pvb.Status.Message = fmt.Sprintf("got a PodVolumeBackup with unexpected status %q, this may be due to a restart of the controller during the backing up, mark it as %q", + velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) + pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} + if err := kube.Patch(ctx, original, &pvb, r.Client); err != nil { + log.WithError(err).Error("error updating PodVolumeBackup status") + return ctrl.Result{}, err + } + log.Warn(pvb.Status.Message) + return ctrl.Result{}, nil + default: + log.Debug("PodVolumeBackup is not new or in-progress, not processing") return ctrl.Result{}, nil } r.Metrics.RegisterPodVolumeBackupEnqueue(r.NodeName) // Update status to InProgress. + original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseInProgress pvb.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} + if err := kube.Patch(ctx, original, &pvb, r.Client); err != nil { + log.WithError(err).Error("error updating PodVolumeBackup status") + return ctrl.Result{}, err + } var pod corev1.Pod podNamespacedName := client.ObjectKey{ @@ -125,13 +127,13 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ Name: pvb.Spec.Pod.Name, } if err := r.Client.Get(ctx, podNamespacedName, &pod); err != nil { - return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("getting pod %s/%s", pvb.Spec.Pod.Namespace, pvb.Spec.Pod.Name)) + return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("getting pod %s/%s", pvb.Spec.Pod.Namespace, pvb.Spec.Pod.Name), log) } var resticDetails resticDetails resticCmd, err := r.buildResticCommand(ctx, log, &pvb, &pod, &resticDetails) if err != nil { - return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command") + return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command", log) } defer os.Remove(resticDetails.credsFile) @@ -159,7 +161,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ if strings.Contains(stderr, "snapshot is empty") { emptySnapshot = true } else { - return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running Restic backup, stderr=%s", stderr)) + return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running Restic backup, stderr=%s", stderr), log) } } log.Debugf("Ran command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr) @@ -177,11 +179,12 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ snapshotID, err = r.ResticExec.GetSnapshotID(cmd) if err != nil { - return r.updateStatusToFailed(ctx, &pvb, err, "getting snapshot id") + return r.updateStatusToFailed(ctx, &pvb, err, "getting snapshot id", log) } } // Update status to Completed with path & snapshot ID. + original = pvb.DeepCopy() pvb.Status.Path = resticDetails.path pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseCompleted pvb.Status.SnapshotID = snapshotID @@ -189,6 +192,10 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ if emptySnapshot { pvb.Status.Message = "volume was empty so no snapshot was taken" } + if err = kube.Patch(ctx, original, &pvb, r.Client); err != nil { + log.WithError(err).Error("error updating PodVolumeBackup status") + return ctrl.Result{}, err + } latencyDuration := pvb.Status.CompletionTimestamp.Time.Sub(pvb.Status.StartTimestamp.Time) latencySeconds := float64(latencyDuration / time.Second) @@ -280,15 +287,26 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l // the PVB with the new progress. func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { return func(progress velerov1api.PodVolumeOperationProgress) { + original := pvb.DeepCopy() pvb.Status.Progress = progress + if err := kube.Patch(context.Background(), original, pvb, r.Client); err != nil { + log.WithError(err).Error("error update progress") + } } } -func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string) (ctrl.Result, error) { +func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { + original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed pvb.Status.Message = msg pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - return ctrl.Result{}, errors.Wrap(err, msg) + + if err = kube.Patch(ctx, original, pvb, r.Client); err != nil { + log.WithError(err).Error("error updating PodVolumeBackup status") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil } type resticDetails struct { diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index ffc5f662cb..1a67b6995f 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -179,16 +179,16 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), expectedRequeue: ctrl.Result{}, }), - Entry("in progress phase pvb on same node should not be processed", request{ + Entry("in progress phase pvb on same node should be marked as failed", request{ pvb: pvbBuilder(). Phase(velerov1api.PodVolumeBackupPhaseInProgress). Node("test_node"). Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - expectedProcessed: false, + expectedProcessed: true, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). - Phase(velerov1api.PodVolumeBackupPhaseInProgress). + Phase(velerov1api.PodVolumeBackupPhaseFailed). Result(), expectedRequeue: ctrl.Result{}, }), diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 59ac901a8c..a35ca74673 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -31,7 +31,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -101,24 +100,21 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req if they interfere with volumes being restored: %s index %d`, restic.InitContainer, restic.InitContainer, resticInitContainerIndex) } - patchHelper, err := patch.NewHelper(pvr, c.Client) - if err != nil { - log.WithError(err).Error("Unable to new patch helper") - return ctrl.Result{}, err - } - log.Info("Restore starting") + original := pvr.DeepCopy() pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress pvr.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()} - if err = patchHelper.Patch(ctx, pvr); err != nil { + if err = kube.Patch(ctx, original, pvr, c.Client); err != nil { log.WithError(err).Error("Unable to update status to in progress") return ctrl.Result{}, err } + if err = c.processRestore(ctx, pvr, pod, log); err != nil { + original = pvr.DeepCopy() pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed pvr.Status.Message = err.Error() pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if e := patchHelper.Patch(ctx, pvr); e != nil { + if e := kube.Patch(ctx, original, pvr, c.Client); e != nil { log.WithError(err).Error("Unable to update status to failed") } @@ -126,9 +122,10 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } + original = pvr.DeepCopy() pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if err = patchHelper.Patch(ctx, pvr); err != nil { + if err = kube.Patch(ctx, original, pvr, c.Client); err != nil { log.WithError(err).Error("Unable to update status to completed") return ctrl.Result{}, err } @@ -137,11 +134,6 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req } func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logrus.FieldLogger, pvr *velerov1api.PodVolumeRestore) (bool, *corev1api.Pod, error) { - if !isPVRNew(pvr) { - log.Debug("PodVolumeRestore is not new, skip") - return false, nil, nil - } - // we filter the pods during the initialization of cache, if we can get a pod here, the pod must be in the same node with the controller // so we don't need to compare the node anymore pod := &corev1api.Pod{} @@ -154,6 +146,28 @@ func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logr return false, nil, err } + // the status checking logic must be put after getting the PVR's pod because that the getting pod logic + // makes sure the PVR's pod is on the same node with the controller. The controller should only process + // the PVRs on the same node + switch pvr.Status.Phase { + case "", velerov1api.PodVolumeRestorePhaseNew: + case velerov1api.PodVolumeRestorePhaseInProgress: + original := pvr.DeepCopy() + pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed + pvr.Status.Message = fmt.Sprintf("got a PodVolumeRestore with unexpected status %q, this may be due to a restart of the controller during the restoring, mark it as %q", + velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) + pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} + if err := kube.Patch(ctx, original, pvr, c.Client); err != nil { + log.WithError(err).Error("Unable to update status to failed") + return false, nil, err + } + log.Warn(pvr.Status.Message) + return false, nil, nil + default: + log.Debug("PodVolumeRestore is not new or in-progress, skip") + return false, nil, nil + } + if !isResticInitContainerRunning(pod) { log.Debug("Pod is not running restic-wait init container, skip") return false, nil, nil @@ -163,8 +177,6 @@ func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logr } func (c *PodVolumeRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { - mgr.GetConfig() - // The pod may not being scheduled at the point when its PVRs are initially reconciled. // By watching the pods, we can trigger the PVR reconciliation again once the pod is finally scheduled on the node. return ctrl.NewControllerManagedBy(mgr). @@ -197,10 +209,6 @@ func (c *PodVolumeRestoreReconciler) findVolumeRestoresForPod(pod client.Object) return requests } -func isPVRNew(pvr *velerov1api.PodVolumeRestore) bool { - return pvr.Status.Phase == "" || pvr.Status.Phase == velerov1api.PodVolumeRestorePhaseNew -} - func isResticInitContainerRunning(pod *corev1api.Pod) bool { // Restic wait container can be anywhere in the list of init containers, but must be running. i := getResticInitContainerIndex(pod) @@ -337,13 +345,9 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve // the PVR with the new progress func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { return func(progress velerov1api.PodVolumeOperationProgress) { - helper, err := patch.NewHelper(req, c.Client) - if err != nil { - log.WithError(err).Error("Unable to new patch helper") - return - } + original := req.DeepCopy() req.Status.Progress = progress - if err = helper.Patch(context.Background(), req); err != nil { + if err := kube.Patch(context.Background(), original, req, c.Client); err != nil { log.WithError(err).Error("Unable to update PodVolumeRestore progress") } } diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 5af7e41ac0..220e004263 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -21,20 +21,19 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - - "k8s.io/apimachinery/pkg/runtime" - "github.com/sirupsen/logrus" - - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/test" ) func TestShouldProcess(t *testing.T) { @@ -45,37 +44,81 @@ func TestShouldProcess(t *testing.T) { obj *velerov1api.PodVolumeRestore pod *corev1api.Pod shouldProcessed bool + expectedPhase velerov1api.PodVolumeRestorePhase }{ { - name: "InProgress phase pvr should not be processed", + name: "Unable to get pvr's pod should not be processed", obj: &velerov1api.PodVolumeRestore{ + Spec: velerov1api.PodVolumeRestoreSpec{ + Pod: corev1api.ObjectReference{ + Namespace: "ns-1", + Name: "pod-1", + }, + }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseInProgress, + Phase: "", }, }, shouldProcessed: false, }, { - name: "Completed phase pvr should not be processed", + name: "InProgress phase pvr should be marked as failed", obj: &velerov1api.PodVolumeRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "velero", + Name: "pvr-1", + }, + Spec: velerov1api.PodVolumeRestoreSpec{ + Pod: corev1api.ObjectReference{ + Namespace: "ns-1", + Name: "pod-1", + }, + }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseCompleted, + Phase: velerov1api.PodVolumeRestorePhaseInProgress, + }, + }, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "pod-1", }, }, shouldProcessed: false, + expectedPhase: velerov1api.PodVolumeRestorePhaseFailed, }, { - name: "Failed phase pvr should not be processed", + name: "Completed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "velero", + Name: "pvr-1", + }, + Spec: velerov1api.PodVolumeRestoreSpec{ + Pod: corev1api.ObjectReference{ + Namespace: "ns-1", + Name: "pod-1", + }, + }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseFailed, + Phase: velerov1api.PodVolumeRestorePhaseCompleted, + }, + }, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "pod-1", }, }, shouldProcessed: false, }, { - name: "Unable to get pvr's pod should not be processed", + name: "Failed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "velero", + Name: "pvr-1", + }, Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ Namespace: "ns-1", @@ -83,7 +126,13 @@ func TestShouldProcess(t *testing.T) { }, }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: "", + Phase: velerov1api.PodVolumeRestorePhaseFailed, + }, + }, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "pod-1", }, }, shouldProcessed: false, @@ -91,6 +140,10 @@ func TestShouldProcess(t *testing.T) { { name: "Empty phase pvr with pod on node not running init container should not be processed", obj: &velerov1api.PodVolumeRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "velero", + Name: "pvr-1", + }, Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ Namespace: "ns-1", @@ -127,6 +180,10 @@ func TestShouldProcess(t *testing.T) { { name: "Empty phase pvr with pod on node running init container should be enqueued", obj: &velerov1api.PodVolumeRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "velero", + Name: "pvr-1", + }, Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ Namespace: "ns-1", @@ -166,40 +223,37 @@ func TestShouldProcess(t *testing.T) { }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - builder := fake.NewClientBuilder() - if test.pod != nil { - builder.WithObjects(test.pod) + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + + var objs []runtime.Object + if ts.obj != nil { + objs = append(objs, ts.obj) + } + if ts.pod != nil { + objs = append(objs, ts.pod) } + cli := test.NewFakeControllerRuntimeClient(t, objs...) + c := &PodVolumeRestoreReconciler{ logger: logrus.New(), - Client: builder.Build(), + Client: cli, + clock: &clock.RealClock{}, } - shouldProcess, _, _ := c.shouldProcess(context.Background(), c.logger, test.obj) - require.Equal(t, test.shouldProcessed, shouldProcess) + shouldProcess, _, _ := c.shouldProcess(ctx, c.logger, ts.obj) + require.Equal(t, ts.shouldProcessed, shouldProcess) + if len(ts.expectedPhase) > 0 { + pvr := &velerov1api.PodVolumeRestore{} + err := c.Client.Get(ctx, types.NamespacedName{Namespace: ts.obj.Namespace, Name: ts.obj.Name}, pvr) + require.Nil(t, err) + assert.Equal(t, ts.expectedPhase, pvr.Status.Phase) + } }) } } -func TestIsPVRNew(t *testing.T) { - pvr := &velerov1api.PodVolumeRestore{} - - expectationByStatus := map[velerov1api.PodVolumeRestorePhase]bool{ - "": true, - velerov1api.PodVolumeRestorePhaseNew: true, - velerov1api.PodVolumeRestorePhaseInProgress: false, - velerov1api.PodVolumeRestorePhaseCompleted: false, - velerov1api.PodVolumeRestorePhaseFailed: false, - } - - for phase, expected := range expectationByStatus { - pvr.Status.Phase = phase - assert.Equal(t, expected, isPVRNew(pvr)) - } -} - func TestIsResticContainerRunning(t *testing.T) { tests := []struct { name string diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 24b2ef6c70..48ea88e159 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -239,3 +240,12 @@ func IsCRDReady(crd *unstructured.Unstructured) (bool, error) { return false, fmt.Errorf("unable to handle CRD with version %s", ver) } } + +// Patch the given object +func Patch(ctx context.Context, original, updated client.Object, client client.Client) error { + helper, err := patch.NewHelper(original, client) + if err != nil { + return err + } + return helper.Patch(ctx, updated) +} diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index 4a6db60695..afece7f83e 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -34,6 +34,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/vmware-tanzu/velero/pkg/builder" @@ -425,3 +426,25 @@ func TestIsCRDReady(t *testing.T) { _, err = IsCRDReady(obj) assert.NotNil(t, err) } + +func TestPatch(t *testing.T) { + original := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "pod", + }, + } + cli := fake.NewClientBuilder().WithObjects(original).Build() + + updated := original.DeepCopy() + updated.SetLabels(map[string]string{"key": "value"}) + + ctx := context.Background() + err := Patch(ctx, original, updated, cli) + require.Nil(t, err) + + pod := &corev1.Pod{} + err = cli.Get(ctx, types.NamespacedName{Namespace: "default", Name: "pod"}, pod) + require.Nil(t, err) + assert.Equal(t, 1, len(pod.GetLabels())) +} From ef7e5a8388d537576ba8b13357f27c8ddbf66d8c Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 9 May 2022 13:52:43 +0800 Subject: [PATCH 130/366] change func name and repository description Signed-off-by: Ming --- config/crd/v1/bases/velero.io_resticrepositories.yaml | 3 --- config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/restic_repository_types.go | 1 + pkg/cmd/server/server.go | 2 +- pkg/controller/restic_repository_controller.go | 2 +- pkg/controller/restic_repository_controller_test.go | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/config/crd/v1/bases/velero.io_resticrepositories.yaml b/config/crd/v1/bases/velero.io_resticrepositories.yaml index 27c97c7fb7..f9a534cb1e 100644 --- a/config/crd/v1/bases/velero.io_resticrepositories.yaml +++ b/config/crd/v1/bases/velero.io_resticrepositories.yaml @@ -23,9 +23,6 @@ spec: name: v1 schema: openAPIV3Schema: - description: TODO(2.0) After converting all resources to use the runtime-controller - client, the genclient and k8s:deepcopy markers will no longer be needed - and should be removed. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 5cca49a4f8..ebf6350750 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -35,7 +35,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\x0f\xbeϯ \xf2\x1e\xf2\x16\x88=I{h\xe0[\xbai\x81\xa0\xc9f\xb1\x1b\xec\xa5\xe8A#Ѷ\xb2\xb2\xa4J\xd4l\xa7E\xff{Aɞ\x0f\x8f\x93I\x02\xd47\x89\x1f\"\x1f\xf2\xa1\xe4UUU+\xe1\xf5=\x86\xa8\x9dm@x\x8d\u007f\x12Z^\xc5\xfa\xe1e\xac\xb5[o_\xac\x1e\xb4U\r\\\xa5Hn\xb8\xc5\xe8R\x90\xf8\x1a[m5igW\x03\x92P\x82D\xb3\x02\x10\xd6:\x12\xbc\x1dy\t \x9d\xa5\xe0\x8c\xc1Puh뇴\xc1M\xd2Fa\xc8Χ\xa3\xb7\xcf\xeb\x1f\xeb\xe7+\x00\x190\x9b\u007f\xd0\x03F\x12\x83o\xc0&cV\x00V\f\xd8@\xc0HZ\x06\xf4.jrAc\xac\xb7h0\xb8Z\xbbU\xf4(\xf9\xd8.\xb8\xe4\x1b8\b\x8a\xf5\x18RI\xe76;\xba\x9d\x1c\xed\xb2\xc8\xe8H\xbf.\x8a\xdf\xeaHYś\x14\x84Y\n$\x8b\xa3\xb6]2\"\x9c)\xf0\x01Q:\x8f\r\\s,^HT+\x80\x11\x82\x1c[\x05B\xa9\f\xaa07A[\xc2p\xe5L\x1a&0+\xf8\x18\x9d\xbd\x11\xd47PO\xb0\xd7g\x90e\xdd\t\xb0W\x1d\x8ek\xda\xf1\xe1JP\xd9(\xe2\xed\x8b\x12\xb6\xecq\x10ͨ\xe9<\xdaW7o\xee\u007f\xb8;\xd9\x06P\x18eОr\xcd>\xbc\u007f\xfd\xfe\xff\xdf\xd7Ͽ\x83W-a\xe0Ro1\x90\xb6\x1d\bc8\xff\xdc)\x11\xc8A\x8a\b\xd4#\x84dI\x0fX\x1d\xdab\xef\x1b@\x1a\x8d\x96\x9ee\xc5\x0emY\x82\xb0\n\x1e^\xc6F!z\xe9\xfc\x0e\x06\x11\x1e0Dx\xd4ƀu`\x9c\xed0\xc0\x06\xc1\"\xaa\f\xea\xf4\xb1m\xec]2\x8a\xc5\x01\a\xb7EU\xef\x15|p\x9e#\x9e:c4:P\xe2hw\x96\xfcSƧh\x81b.p\x9e=N\xe5D5B\n\xae\x05\xeau\x84\x80>`D[\xd8q\xe2\x18XIXp\x9b\x8f(\xa9\x86;\f\xecf\x8a|\xc4\x15\x02J\xd7Y\xfd\xd7\xdewF\x96\x0f5\x82pl\xcf×\xdb\xc7\n\x03[a\x12>\xcbX\fb\a\x01\xf9\x14H\xf6\xc8_V\x895\xbcs\x01A\xdb\xd65\xd0\x13\xf9جם\xa6i\x14H7\f\xc9jڭs\xf9\xf4&\x91\vq\xadp\x8bf\x1duW\x89 {M()\x05\\\v\xaf\xab\x1c\xba\xcd\xe3\xa0\x1e\xd4\xff\xf6-\xf1\xf4$\xd6Ҙ\x91\x82\xb6ݑ \xf3\xf43\x15`\xa2\x82\x8e FӒ\xc5\x01h\xdebtn\u007f\xbe\xfb\xb0\xef\xc6\\\x8c9\xfa\x19\xf7\x83a<\x94\x80\x01Ӷ\xc5P\x8a\xd8\x067d\x9fh\x95w\xdaR^\x94F\x9d9\x8di3h\xe2\xba\xff\x910\x12ת\x86\xab<\x1f\xb9\x17\x93g\"\xaa\x1a\xdeX\xb8\x12\x03\x9a+\x11\xf1?/\x00#\x1d+\x06\xf6\xcbJp<\xda\xe7\xca\x05\xb5#\xc14}?Q\xaf\xf9D\xbd\xf3(\xb9|\x8c \x9b\xeaV\xcb\xcc\rh]\x00q\xa6_\x9f\xb8^\xa6.\u007f\x1b!\x1f\x92\xbf#\x17D\x87o]\xf19W\x9a\xc5\xf6Ӓ\xcd\x14\x1c\x0f\xcaBc\\V<\xf3\r@\xbd\xa0#\xfe\x92\xd0v?\x06\x16\xf3\xf9L\x11r!\x04\xd3\xd9\n+\xf1\x97\xdcQV\xee.\xe4\xf4n\xc1\x84S\xea\xdd#\xb8\x96\xd0\x1e;\x1dc]\xc8d\x93\x87\xf6W\x05[n\xbe7\x8a\x1b\xaf\xd5\x18.\x04z;S\x9fpoS\xb9EH\xcbJ\xba\xc1\v\xd2\x1b\x83\xcbG\xf2\xc7m\xa3\x8b\x97]\xe1\xfe\xb7\xe3\xbd\xe5k\x17\xf7\x17\xf5\x85\f\xeeO\xb5\x8f\x1b\xa7l\x8c\xa1p*\x10N\x9f\x1c\xa7\xdf\xd8+\x11\xbcSc\x10cCG\xce\xef+r\xe0\x92뀳\tZ-\xd3c\xa6\xb3\xd4m3\x95y\x8dg\xe2\x19~_4>HP\x8a_3@\xb2\xc1\x04\xb6L!\xf0s\xa1\xb8\xc97\xea7\x8f\x10#\"\x1dч\x1fV\x17:\xe0\xed\xb9\xc5\x14\x18;\x03~\xf4\x9c\xf0\xedQ\xcco\xa1\\\xb4%\xa6\xb5.\f\x82\xca˭bGg\x1a\xfcB\x16\x1b\x83\rPH\xe7\xe2\xcf\xcd\x15\x8cQt\x97\xb2{W\xb4\xcae;\x9a\x80ظD\x9f\x80\x9e\xfa\xf3(\xe0B9.D\xea{\x11/\xc5y\xc3:K\r\xb1\x9fߗC@\x9b\x86\xf3c*\xb8\xc6Dž\xdd[\x14\xea\x9c\xc7\x15\\;Z\x16}2\xc3EV\x9cmF~\x97\xa8\xa3:\xc7B\xe4㝴ٿ\xb3\xa6DFn\xc1\xdf\xff\xac\x0e4\x13R\xa2'T\xd7\xf3_\xa3'ON\xfet\xf2R:[\xfeLb\x03\xbf\xfd\xbe*\a\xa3\xba\x9f\xfe^x\xf3\xdf\x00\x00\x00\xff\xffo=\x1agQ\x0e\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85\xa4\x05\xbc\xeb\v\xfa\xd0\xc2o9LJ\x1a\x97\xe6\x82\xd8\xf5K\xd1\a\xae4k\xf1,\x91*I\xad\xb3=\xdc\u007f/f(\xeak\xa5\x15\xb5q\xd0\xeb\xc1|I\xcc%\x87\xc3\xf9\xe6pij\xd5ju&J\xf9\x80\xc6J\xad\xae@\x94\x12\xbf8T\xf4\x97]?\xfdծ\xa5\xbeܽ={\x92*\xbd\x82\xeb\xca:]|F\xab+\x93\xe0{\xdcJ%\x9d\xd4\xea\xac@'R\xe1\xc4\xd5\x19\x80PJ;Aݖ\xfe\x04H\xb4rF\xe79\x9a\xd5#\xaa\xf5S\xb5\xc1M%\xf3\x14\r\x03\x0fK\xef\xbe[\xffe\xfd\xdd\x19@b\x90\xa7\xdf\xcb\x02\xad\x13Ey\x05\xaa\xca\xf33\x00%\n\xbc\x02\x9bd\x98V9\xda\xf5\x0es4z-\xf5\x99-1\xa1\xd5\x1e\x8d\xae\xca+h\u007f\xf0\x93jL\xfc.\xee\xea\xf9ܕK\xeb~\xecu\u007f\x90\xd6\xf1Oe^\x19\x91w\xd6\xe3^+\xd5c\x95\v\xd3\xf6\x9f\x01\xd8D\x97x\x05\x1fi\xa9R$\x98\x9e\x01\xd4\x1b\xe3\xa5W ҔI%\xf2OF*\x87\xe6Z\xe7U\x11H\xb4\x82\x14mbd\xe9\x98\x14wN\xb8ʂނ˰\xbb\x0e\xb5\x9f\xadV\x9f\x84ˮ`myܺ̄\r\xbfz\x12y\x00u\x97\xdb\x13n\xd6\x19\xa9\x1e\xc7V{\a\xd7F+\xc0/\xa5AK(CʜU\x8f\xf0\x9c\xa1\x02\xa7\xc1T\x8aQ\xf9^$OU9\x82H\x89\xc9z\x80g\x8dI\xbfs\x0e\x97\xfb\f!\x17ց\x93\x05\x82\xa8\x17\x84ga\x19\x87\xad6\xe02i\xe7iB@z\xd8zt>\f\xbb=B\xa9pX\xa3\xd3\x01\x15\xa4z} \x91=\x98\xef\x1eq\x1c\x98\xffy\xf7\xd6\xcbM\x92a!\xaeꑺD\xf5\xee\xd3\xedß\xefz\xdd0\x90\x83z\x97 -\bx`\xa1\x06S\xab\x1f\xb8L80H\\C\xe5hDip\x15(\x936 \x01\xb4\x81\x12\x8dԩL\x02Ey\xb2\xcdt\x95\xa7\xb0A\"\uee99P\x1a]\xa2q2\xa8\x8do\x1d3\xd1\xe9\x1d`\xfc\x866\xe5Gy)B˂S+\x03\xa65\x1d\xbclK\xdb\xe2\xcf\x04\xee\x01\x06\x1a$\x14\xe8\xcdϘ\xb85ܡ!0\x01\xebD\xab\x1d\x1a\xa2@\xa2\x1f\x95\xfcO\x03ے\xc4:\x16$\x87\xb5.\xb7\x8d\x95O\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xab@\xa5:\xf0x\x88]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05\xf3\x98袨\x94t\xfbK\xb6trS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+F]\xb1\x89\\\x17\xe9\x1f\x02G\xed\x9b\x1e\xae\a\xba\xe2\x1b\x1b\xb1#\x1c k\xe6\x05\xc6O\xf5\xbbh\tM]D\x9d\xcf7w\xf7]a\x92vH}\xa6{G\xc2Z\x16\x10\xc1\xa4\xdab\xad\x8d[\xa3\v\x86\x89*-\xb5T\x8e\xffHr\x89jH~[m\n\xe9\x88\xef\xff\xae\xd0:\xe2\xd5\x1a\xae\xd9g\x90\x1cV%iO\xba\x86[\x05ע\xc0\xfcZX\xfc\xe6\f J\xdb\x15\x116\x8e\x05]w7\x1c\xec\xa9\xd6\xf9!\xb8\xa6\t~\x05\x1d\xbf+1\xe9\xa9\f͓[\x99\xb0b\xb0\xe5kL\xc0\xc0\xfa\xf96\xae\xb5P\x9b\x1e\x1a>\xec?jmb}\xc2\x01L\xa8M\xcc\xfa\xe0\x97\tj\xf2OX\x94\xa4\xae3(\xde\xd7\xc3\bE\xa2Qڄ \xc1Y\x06\xf3\xa6k\xab\x06\aF\x85\x97ː赓im5\x0e\xa8y\x9c\xa2\x1e\xb1\xad\xa8r\xf7@~\x1c\xed\xbd\xfe\x8c\xd6\xc9dl\xe4`\x13\xefG'\x06~\xa3%\n\xbb\f\r)'\xff\xc0\xf6n\x14.\xb0\xceXL\xd9\xe4\x89'r\x99\x1bO\x01\xb2\x9dy\x0e\xa5Na\xe7W\x82\xcd> }ț\x96?\x1b\xads\x14cT\xc3/I^\xa5\x986a\xce(]\x06\xbb\xbd9\x98\xc4\x01\xa1\x90\x8a\xa4\x8c\xc2/BU5\xbfN\xec\x93\xfd\x950\bd(\xa4\xf20A\xfa\xb0d3!pԤ\xc3b\x02ϣ\x12\xe9\x1b\x05\x9eb\x93\xe3\x158S\x8d\xc9z\x80!\x8c\x11\xfb#4\vA\xf3\x12\x925sjs\x9e\xcb\x04\x89X\x8d\xd1f\xaa1i&\xf6\xf7\u007fH\xb0L\xeb\xa7\x18\"\xfd\x8dƵ\xce\t\x12>\x9b\xc0\x063\xb1\x93\xda\xd8a\x84\x83_0\xa9\x1cN\xe9\x91p\x90\xca\xed\x16\r\xc1›\x89\xbf\x8f\x11븉\xa0f\x8e3\xfe`_-ӉyL\x8d\xa9\xad\xb0)\x9e\x84\n\x8c8Y\xec\xaa\x04\xa9R\xb9\x93i%r\x90\xca:\xa1\x12\xbf?\xd1\xe07\xbe?\x98\x13\x88\x03\xfc\xbd\x01\x0e\xbb .\xf5<\x9bVH\xe1h\xa1\u0378p\x84v\bf\x9a\f\x1bA\x16PO\xb9\xa3\xb6\x19:\xc5ը\xa4\xecR[\xbbs\xd1r\xca\a\x85\xb9\xd8`\x0e\x16sL\x9c6\xd3\xe4\x89\x11\x02\xdfb\xed\xe7\x04eG,i\xeb3HPg\x8dhۜ\x86\xe7L&\x99\x8f\xdfH\xca\x18\x16\xa4\x1a-[\fQ\x96\xf9\xfeئ!F2\xea\xc5\xe6\x8cF\xdb\"\xcc\xc7\x10\xee\x94!i[\xa4\rnی5\xeeS\xbd\x11\x9bW\xa2\xf7\xd0T_%\xec\xb7\a\xd3_^؉ܒ\xcew\xb7[\xc0\xa2t\xfb\v\x90.\xf4\xc6@\xa5\x00\xab\xc5\xe3wƸӴ\xe5v8\xfbŵ\xe5E\xb8֠\xf1;a\x1a;\xab\xbb\xdaW-b؇\xee\xcc\v\x90ۆa\xe9\x05le\xee\x90c\xa99D;\x81\xce,\xe7^\x92@\xb1\xbe\x97Z!\\\x92\xdd4Gڈ\x19\x03Z\r\x01\xf8\xb8<\x9ca\x98\a\x11 \xa1\t*8\v\"\r\x16>\xbbr\xcf\xfa\xd1\xf6p\x04\xf8\xee\xe3{L\xe7H\x06\xf1\x92z\xb0\xa9w\x83H\xa7\x8b\x02o0\ndgS\x1c\xa65g<\x9fC\xbb\x00\x01O\xb8\xf7\x91\xd5\xe8\xe1r\xac\x11kE\x03\xd2 '\xf4،<\xe1\x9eA\xd5\x19\xba(xKDŷ'\xdc\xc7\x0e\x1d\x10\x95\xf0\xabs\x14\x9e\xba\xd4\xc1\xbb\x88Q\xa5\xb65D\xadu\a\x9c\x8e\xdb,,3J\xa1\x05\x8a\x9f\xb8\xed\x86a\xbd\xb4\xf4\x13\xee\xdfX\xcf>ҚL\x96\v(@\x06\x1b,\xb2\x86\x85|\xec\x83\xc8e\xda,\xc6z\xb2\x00⭺\x80\x8f\xda\xd1?7_\xa4%\x14U\n\xef5ڏ\xdaq\xcf7%\xb1\xdfĉ\x04\xf6\x93Y-\x95w\vD\x97E\xeb\xb78\xb0\v%\x11m\xd8&-\xdc*:\x9fy\xfa,aS\x86\x019\x8fVQY\xce\xe8*\xadV\xec\xa6\xc3j\v\x80v\xf1\xaaY\xa5M\x8fS\x17\v!\x8e\xa2X\xa3wO\xde\xca\xffr\x90\v?\xd6\f\x96\xb9H0\x85\xb4\xe2|;'ޅ\xc3G\x99@\x81\xe6\x11\xa1$\xbf\x11/T\v,\xb9o'Ha|h\x11Z\xed\x16\xd28\xc4V\xa4\xf5\x91#\x03\x9b\xa3\x86Odُ\x0f\x8f\xdb%\xbbw\x8e\x87\xa2\xa8߽\xd2]\xe6Y\x16\xf2\xeb0\x06\xf1H\xfa\xf0\xa3\x10\x9c\xec\xfd\x85\xdc+\x8b\xf7\xafq\xdePHc\xd7\xf0\x8e/\xb4s\xec\xce\x0fY\xc2\xceRQ \t\x13i\x81\xe4d'r\n\x1f\xc8x+\xc0\xdc\a\x13z{\x10Ař\x98\xe7L[\xef\xf3\xb7\x12s\xbe\xdd:\u007f\xc2\xfd\xf9Ł\xf5:\xbfU\xe7q0\xc9\xe6\x1f\x18\xad&j\xd1*\xdf\xc39\xffv\u0381\xd9\x12\x159!x[ \xd5\xd1C\xf9\xfay\xc9Q\x80\xce\xda!j\xa1\xc9\xcd%-\x85\xf0s\xbb\x88\x96\xe9R[\xb7\b\xadO\xda:\x9f\x00\xec\x85\xdb#\x19\u0098\xd3_\x9d5\x04\xb1uh\xc0:m\u0085(\x99\xddA\x82\x9c8o\xe7yO\xacn\xb2\x91\x1e0\x1d2\xcf[\v\xe1m\xfa\xb9\xbf)\xa5\xff\xcf\xc3L8Xbإ\xd1\tZ;/J\x91\x9ec&a\xdb$k\x85?\xbcm\xa3LsL*9\xb4e\xa18\x91\xf6\x84\x83\xcd͗Nޙ\xcc\x10\xfd\x1d#ʧ\xe0\b\\\xe8T\x14bx9\x1f\x8d\ued5f\x1d\x14\xb0\x06\xe6\x0fL\xe6\xb1b\xa3\xb2,n\xaeE\xf2\xb7\x16x\x14R\xdd\xf2B\xf0\xf6\x9b\x05+\x10L9\x9ez\x94\xb9\x0e\xf3[\x864\x1d\xb1\xe7W\bW͚\xefj\f\xf68{x\x93\x11\xcf)\xa0`Zi\xd7M\xd6\xd4+\xbd\xb1\xb0\x95ƺ\x16\xe1\x05P\xa5\xe5\xeb\xe4o{\xc6T7Ɯ|\xc4\xfc\xc9\xcf\xee\xa4\x153\xfd\\\x17F,9X\a\xe2gb\x87 \xb7 \x1d\xa0Jt\xa58\xe1E悖Y\x00\xd13\xd1;\x93H\x9fٙ\xac\xaa\"\x9e +\x96N\xa9f\xb3c\xdd)?\b\x19\x97\x9d\x82\xd3\xd8\xead\x81\xba\x9au\xedm\xebWv\xf8ٽ\n\x98B|\x91EU\x80(\x88-K\u038d[_?\x18\xcae<\xaf\x9f\x85tu\x05\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8aM\xf8P\xf3\u007f\xb4\xded\xaa\t\xd8\n\x99Wf\x81\x8d^̙\xa5\xe7\xb6\xda<\xbd\xfca,\x1e\x91\x15\x1332\xe9\xbe h\x9e\xf7\x1f\xa5Y\x162\u007f2\xf8\xf2\xa1ii$I\xa9\x9e\x8bNgar\xf4ڏNk\xe1\x15j?\x15\x9e\xceBeL^\xc3Ӧ\xbd\x86\xa7\xaf\xe1\xe9kx:h\xaf\xe1\xe9kx\xfa\x1a\x9e\x8e\xb7\xd7\xf0\xb4\xd3^\xc3\xd3h\xff\x11\x83\xe1\x8a3\xb7G\x06Da\x15Y\x821\x87\xf6\xccZu\xa5\xd1u^Y\x87fI\x85\xf4\xed\xf8̑\x1a\xfa\xc4\x0fY\xf1\x17zSRӖ\xae\xb4N\xaf)\x99&\x95\f\xca\xe4\xbfň\x88£ˠ\xa7\xab\xedc\v\xe8\xe6\xca\xe6\xfa\xb5\xe3M\xb9\x9a\xff\xdf\x04A\x9c\x0e\xcb\xd7\xdc\xf3\xdf\xf8tk\xae\xfa\xb5o|\x0e\b\x18\xff&\xeb\xca#\xcb\xdaf\x8aَ\x17\xe2Oy\xf8@\xcb\xc1\xe5B\x9f\x98\xa6W\xf8\xfd\x9b\xa6eD\xb5\xd9t\x8dY}k\x89N\xecޮ\xfb\xbf8]W\x9cM\xec\xecY\xba\xcc\u007f\u007fDGW\xf5\xd8-k\x0frZ\u007f\xfb7\xa4\xf1\x04Dm@\xc9\xdc3 @\xe8\x91\x1f~*\xfd\x11\xf9d=\x9f?\xa8\xc5ץ-\xadFk\xea\x87\xe6\xbd\xcaWԠ-\xfb@`\xb6\xde,\x06i\x88\xa92\x1b\xaf\x1f\x9b\x81\xba\xa4\xb6,\xf6\f\x1eQG\x16_=\x16G\x1e\xe0/vckƢ\xa3\xb6\xd8\xfa\xb0oS\x15\x16Y\v֩\xf0\x9a\x05yb\x05X4\xc1⪽\xa2k\xbc:\x95[\xf3\xd4:R\xd95^\xaf5\vr\xac\x9e+\xa6J+\n\xd7\xe8ڬ\xa6\xe2j>\x93\xf8U\x15Y/_\xfb\xfd\x92q\xfe\xf1\xfa\xaa\xa8\xaa\xaa\xa8\xb3\xc0<\xceQuSK\xab\xa5\xa2\xa8\xba\xb42\xaa\xa9z:\xb2pT=\xd4a\xadӱ\xad\xccVAMW8\x1d\x03;V\xfb\x14Q\xd7t\x04d\xb7\xe2iq\x180+M3\x03ƿ\xaa\x0fm\xde\xd7\xe6\xff\v\t\xfc\xdaMk\x93\xa2\x99=\x95,A}\x16\xed~\xber\xb0\xfe\xe0s\x9d\xf019\x8d\xea\x9ex\xa6\xa2(\xdd|>\x92\xc0\x8fR\xa5^\x92HY:1\r\xbfP\xc1\x05fM\x985]q\xdbF\xb4\x83Ӗ\xc5R\x90QOa\xb3\xf7Y!\xbb\x86\x1b\x91d\xcd\xc0\t\x88\xbcr&,\x9d\xec\v\xe1\xe0\xbc9\xc6^\x86\x99\xd4s\xbe\x06\xf8A7\x19\x84\xce\xe7\xa1\x13p\xad,\xca|\x0f\x95E8\xef\x03\xfa\xbaSؤ\xecX%J\x9b\xe9\xf0\x92@\xc4A\xec\xae?c$_\x12\xde\x11Hr]\xa5\xcd\nG\xd8-\xd4\x1e>=p$\xc7_O'\xedW\xe6u\xa4\x16\xceU\x83\x8f\xd0'@N=\x1e\xb1\x88d\xd3Y\x15\xeb\xb4\x11\x8f\xf8A\xfbw5bh֟\xd1{Z\xa5\xb6\xab!\xc7Z\x17MNJ\xb2\xdf\xdb\x10`{\xf5Rk[\x9b\x84\"l\xa7L\ue31e;\x97Gl\xee\xfe\xfe\x83ߐ\x93\x05\xae\xdfW>õ*\x85\xb1H\x94\x0e\x1b\xf5\x936Ӗ0\xd3ϐ\xeb\x9a\x0e\xdf\x0f\xf7a\x90\xafz8\x99v\xd2n\xfc\x13\x16A|\x03\xe9bD\xfea|f\xe7\xb0\xdcaⱜ\x98\xdeN\xc2\x12\xd6\xeaD\xb2-\xe2\x14\x05߰|\xbb7\x16\x8eEdGLFe\xf1\xa7g\x85\xe6sPT{\xab<\xa7f^]\xf9\xc7\xc1\xc4\xc0\xe01\xf3A\xf6o0|\xcc署@ֿ\xf7\xe6\xcd8\x13.\xbcCsH\xba\x19\xfd\x9f\xd6\xfd\xf1\x88z5\xfe\xf4˪y\x8d\xe6,\x82\xb2\xfe\xbd\xb1\x98\a~\xfc\xa3n\x89(]ej\xf7\x9aT\x86\x1f\x9c \xe8\xdfc8퉟\xf6\xb9\xb3\x19^\xb6\x0f\xa0\xb5\x19\x86\xd9\xe7\xd6F\xf8\xd7<\x164\xf9z\x8e\xf7\xae\xfe9\xb4\x15\xc1?\x8d\x9d\xa3z\xc0\x0ft\xcc\xec\xf4\x13\x8din\xc7kB\xf3\xc4\xf0\xb0\xc7\xdd\x14\xea\xe3ם+\xf8\x88\xcf#\xbd7\x8a6qx\x18\xf5w\x9a\x98r\x86b\xecy\xb3\xa3[\xdc5\xb3\xf8By\xc4Z\xf4\xcd\xdc`\xf8 S-\xf2\xbc\x03\xd1_\x1e\x8f\xb1\xf5\x8fr\xeb\xd3F\t\xed\xe9O\a#&\r\xd7Q\xa35e\xb0FU\xea\xa0Ӣ\xd9a\xda\x11\x92ڇw{\xaa\xcd\xc1;+\xb5b\xc2/\xbf\x9e\xb5:*\x92\x04KWߏt\x1f\x859\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), diff --git a/pkg/apis/velero/v1/restic_repository_types.go b/pkg/apis/velero/v1/restic_repository_types.go index 6a44c2a318..e075f5f9eb 100644 --- a/pkg/apis/velero/v1/restic_repository_types.go +++ b/pkg/apis/velero/v1/restic_repository_types.go @@ -73,6 +73,7 @@ type ResticRepositoryStatus struct { // +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + type ResticRepository struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 653a9d12f6..3309391eea 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -792,7 +792,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) } - if err := controller.NewResticRepoConciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.resticManager).SetupWithManager(s.mgr); err != nil { + if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.resticManager).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) } diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 9a1fa51e21..7ac899f40d 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -49,7 +49,7 @@ type ResticRepoReconciler struct { repositoryManager restic.RepositoryManager } -func NewResticRepoConciler(namespace string, logger logrus.FieldLogger, client client.Client, +func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client client.Client, defaultMaintenanceFrequency time.Duration, repositoryManager restic.RepositoryManager) *ResticRepoReconciler { c := &ResticRepoReconciler{ client, diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index ba394a6e35..92dcaae9e5 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -36,7 +36,7 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mo if mockOn != "" { mgr.On(mockOn, arg).Return(ret) } - return NewResticRepoConciler( + return NewResticRepoReconciler( velerov1api.DefaultNamespace, velerotest.NewLogger(), velerotest.NewFakeControllerRuntimeClient(t), From e09c31e5590e63ab128a7dcbab3aba156a986a65 Mon Sep 17 00:00:00 2001 From: Ning Ding <834652870@qq.com> Date: Mon, 9 May 2022 15:55:14 +0800 Subject: [PATCH 131/366] changes according to review comments Signed-off-by: Ning Ding <834652870@qq.com> --- changelogs/unreleased/4872-big-appled | 1 + .../velero-restic-restore-helper.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/4872-big-appled diff --git a/changelogs/unreleased/4872-big-appled b/changelogs/unreleased/4872-big-appled new file mode 100644 index 0000000000..739fb5ab11 --- /dev/null +++ b/changelogs/unreleased/4872-big-appled @@ -0,0 +1 @@ +Cleanup the .velero folder after restic done \ No newline at end of file diff --git a/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go b/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go index d9fdfccad7..6622bee11b 100644 --- a/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go +++ b/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go @@ -41,8 +41,9 @@ func main() { err := removeFolder() if err != nil { fmt.Println(err) + } else { + fmt.Println("Done cleanup .velero folder") } - fmt.Println("Cleanup .velero folder") return } } @@ -85,7 +86,6 @@ func done() bool { func removeFolder() error { children, err := ioutil.ReadDir("/restores") if err != nil { - fmt.Fprintf(os.Stderr, "ERROR reading /restores directory: %s\n", err) return err } From ed43e8de3022ea50c995de4f1a141dafe07e18eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 9 May 2022 10:19:06 +0800 Subject: [PATCH 132/366] Bump up the version of distroless to base-debian11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump up the version of distroless to base-debian11 Fixes #4867 Signed-off-by: Wenkai Yin(尹文开) --- Dockerfile | 2 +- changelogs/unreleased/4898-ywk253100 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4898-ywk253100 diff --git a/Dockerfile b/Dockerfile index 4040e18dc4..8ffe8abff4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,7 @@ RUN mkdir -p /output/usr/bin && \ go build -o /output/${BIN} \ -ldflags "${LDFLAGS}" ${PKG}/cmd/${BIN} -FROM gcr.io/distroless/base-debian10:nonroot +FROM gcr.io/distroless/base-debian11:nonroot LABEL maintainer="Nolan Brubaker " diff --git a/changelogs/unreleased/4898-ywk253100 b/changelogs/unreleased/4898-ywk253100 new file mode 100644 index 0000000000..e0f9c89a90 --- /dev/null +++ b/changelogs/unreleased/4898-ywk253100 @@ -0,0 +1 @@ +Bump up the version of distroless to base-debian11 \ No newline at end of file From 2d69c647d6587da00e123132d27702ef9c686109 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Mon, 9 May 2022 12:59:08 +0300 Subject: [PATCH 133/366] Add Orlin and Abigail in the team on the site Signed-off-by: OrlinVasilev --- .../content/contributors/05-abigil-mccarthy.md | 7 +++++++ site/content/contributors/05-orlin-vasilev.md | 7 +++++++ .../img/contributors/abigail-mccarthy.png | Bin 0 -> 398111 bytes site/static/img/contributors/orlin-vasilev.png | Bin 0 -> 645900 bytes 4 files changed, 14 insertions(+) create mode 100644 site/content/contributors/05-abigil-mccarthy.md create mode 100644 site/content/contributors/05-orlin-vasilev.md create mode 100644 site/static/img/contributors/abigail-mccarthy.png create mode 100644 site/static/img/contributors/orlin-vasilev.png diff --git a/site/content/contributors/05-abigil-mccarthy.md b/site/content/contributors/05-abigil-mccarthy.md new file mode 100644 index 0000000000..578fcbec8c --- /dev/null +++ b/site/content/contributors/05-abigil-mccarthy.md @@ -0,0 +1,7 @@ +--- +first_name: Abigail +last_name: McCarthy +image: /img/contributors/abigail-mccarthy.png +github_handle: a-mccarthy +--- +Technical Writer diff --git a/site/content/contributors/05-orlin-vasilev.md b/site/content/contributors/05-orlin-vasilev.md new file mode 100644 index 0000000000..c57a4c2d22 --- /dev/null +++ b/site/content/contributors/05-orlin-vasilev.md @@ -0,0 +1,7 @@ +--- +first_name: Orlin +last_name: Vasilev +image: /img/contributors/orlin-vasilev.png +github_handle: OrlinVasilev +--- +Community Manager diff --git a/site/static/img/contributors/abigail-mccarthy.png b/site/static/img/contributors/abigail-mccarthy.png new file mode 100644 index 0000000000000000000000000000000000000000..f71c386220e671f943c33a86480096f3beeb4569 GIT binary patch literal 398111 zcmV*HKxn^-P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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∓4wm*K&6;f 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

sxK@ep{up+O}=W{qF7^UO{tC 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%0DaP=BzkQ-Cncic~~N1l=189)ycd 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

    WQ! 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 j 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

    8tdL-|R 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 z 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 z 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 uq@bXK1|#C0NU|#hI0(ypP*k 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

    dtE__kvN?Kh=3#-Mji5xKKp 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 Date: Thu, 10 Feb 2022 02:16:29 -0500 Subject: [PATCH 134/366] add existingResourcePolicy to Restore API add updateall policy option fix updating labels dump updateAll policy option remove updateAll policy refs Signed-off-by: Shubham Pampattiwar add changelog file Signed-off-by: Shubham Pampattiwar update docs Signed-off-by: Shubham Pampattiwar patch labels for sa if policy is update Signed-off-by: Shubham Pampattiwar fix existingResourcePolicy for serviceaccounts modularize changes and add unit tests Signed-off-by: Shubham Pampattiwar fix conflicts and update crds.go Signed-off-by: Shubham Pampattiwar change log level from info to error Signed-off-by: Shubham Pampattiwar update crds.go Signed-off-by: Shubham Pampattiwar --- .../unreleased/4628-shubham-pampattiwar | 1 + config/crd/v1/bases/velero.io_restores.yaml | 5 + config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/restore.go | 16 +++ pkg/builder/restore_builder.go | 6 + pkg/cmd/cli/restore/create.go | 14 ++ pkg/cmd/util/output/restore_describer.go | 7 + pkg/restore/restore.go | 126 +++++++++++++++++- pkg/restore/restore_test.go | 72 +++++++++- site/content/docs/main/api-types/restore.md | 3 + 10 files changed, 247 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/4628-shubham-pampattiwar diff --git a/changelogs/unreleased/4628-shubham-pampattiwar b/changelogs/unreleased/4628-shubham-pampattiwar new file mode 100644 index 0000000000..acb92c9086 --- /dev/null +++ b/changelogs/unreleased/4628-shubham-pampattiwar @@ -0,0 +1 @@ +Add existingResourcePolicy to Restore API \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 09df605e5c..5089888014 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -53,6 +53,11 @@ spec: type: string nullable: true type: array + existingResourcePolicy: + description: ExistingResourcePolicy specifies the restore behaviour + for the kubernetes resource to be restored + nullable: true + type: string hooks: description: Hooks represent custom behaviors that should be executed during or post restore. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index ebf6350750..c3df834a27 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -36,7 +36,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9\xa4\xb12\x97\xf2\xfe)L\xfc\xd5>S\xf30\x92\xa1\x8eC&0\xa7K&\x95_\xbb\x17)\x13 \xf0\bYiP\xcco\xc0-\x91\x9dJE\n\xa9\xcdv,l?\x89\xc4\x1d\x8em[\xb8\x13\x85\x1b+\xf3\x8c#l\xb1]h\x8b\x89H\x01v\xae\v{\x12\xebg\x95,ݳ\xebܾ\x81\xf0n\x8c\x90\tՐ\x13\xe9i\xa0\xe4\xa0\xfd\xb7rdO\xf5);\xdb\n\xbaZ\xbc\x93\xbb\x9cN\x80\x13\r\x1c2#\xd5&&\xf7\xc1\xa7\x1b\xfbp\x8e-x\xec\xe0!\x9e\xf7zN\\/l\aHb\x95\x8e\x879\xcb\xe6N$Z\xdaD8$\x97\xa0\xf1\x18Y\xb5m\xb5m\x91䩽\xf7\x1f\xd9u\x90\xea\xf1đZ\x87\xd7u\xb8\xea\xb1\a\xf3\xa9\xc7\x13l\xa8\x8d\xd9Z_\xed\x94#\xf5\xf8e\"6\xf0\xd5\x04\xa2\xbd\xdax\xf5\xb0D\x8b&\x86U}\xaf\xa6\x04\x16\x85Y\x9d\x11f¯OA\xa4\x9c7\xbe\xff\x19oL<\xc5_\xad\xbfyP\x8a߹+OA\xb4\xbbR}\xfe3\xdc\x14\x14\x16\xb7^V\xec\xbd!\xdf5\xdf:#lZmH~F\xa6\x8c\x1bPk;\xd3\xeb\xbc\x1c\x02\x19\xfb\xc8;;\x16\xd4d\xf3\xcbG\xab\xd9\xe8\xda\x1d\xb3'^\xd6_v\nbИۂ\xf9\t\xb8\x04\x8d9\xa6`\xe1\x8c\xc4;\xc4f\xfd\vj\xd7\x17ׯ!߅\x1e\xb2\x1f\xe5m,\xe4bm\xb2\xcdO{\xadw\xdfexէ\xb2 \x9c\xf9\u007fF(\xb9\x87\x95\xd3X\xa8 vs\xa8\xfd\xd0\x16[b\x139\xe8\x87@\"\xbb\x87\x15\x82\xf1\x8e\x85'\xdfޗ\x14ܸ\x87\xd5>\x8f\xad!\xd0\xceɛ{\x0e\x93\xf6\aD\x04\x9a\xa1\xfb#\x8f\xa0\x93(\xf0\xa2\xa7\x17G\xf6g$a\x04\xdc',\xb3ڶ\x863\r7\xf6X\xbb-\xb2\xa7`Ί=\x17\x8a\xbe4\rxZ\x82\x9b\xe8=\xe5,\xaf>\xe4\xe8\xfeJl׆\xdb\xe3Z\x9a+qF.\x1f\x99\xb6S\x139y-A_K\x83\xbf<\v:\xdd\xc4\x13\x90\xe9^\xc4\xe3%\x1c۶xh\xfa\x9b\xf6 n7\xae\x9c[\xa1\xda\x1e\xa6ɕ\xb0\x86\x8b\xc7\az\x0f\xdd\xe7vˇ\xf6X\x94\x1a\x1dJB\x8a\x11\x8a\xcaqח\x1c\xb2\xf7\x04)UkG6\xa7V}\xd4}pO\xb0wV\x92\xb8\xf7\x9d?\x94\xd3\f\xf2`m\xa2\x17\x8f\x1a\x98\xb1\x8c,@\xcdv\t\x8e\xe6(,\u007f\xdfo\n{r]7\")l?\xd1\x1e\x86g\xdd\xf9ӓ\x19ٓ\xbb\xc7Sa\xb3\x9f|t\x8b\xf3n\xfb\xa3O\xaf\bE,\xea\x1fOb\x97\xe69FZ(\xbf\x89\xe0\xf8\x11{\xb1)\xfb\xddĜ\x84\\\xd0\u009e\xdf\xff\xb1b\x0e\t\xfa\u007fIA\x99\xda\xe3\f_`ЄC\xeb]\xef&j~\xc6~\x81ib\xf7wI\xf9\xa6[\xb8cq\xd2\xf2\x16\xe0N\x90\xcb\xe9\x86\xc6rF\x1e\xe6R;\x99:e\xc0\xbb\\6\xed\xc149\xba\x87\xd5\xd1\xd9\x06\x1f8\xba\x12GN\xc0G\xb3\x9bJ[\x90\x82\xaf\xc8\x11\xbe{\xd4G\tړ\x12\xf7zLt:}\xeb\xd1\"\x8b\xa6\xe3\xb7\xf6\xf8z5w\u05ec\xf7\xa2\xc3Bj\xf3\xd7n\x87ݖ\xf9܄7ںi\x87\xdf\xebI\x9d\xdd\xfb\xb0*\xa6j5\xb9\xa9\x01\xe5\x9dx\x8e\xd1\x06\v\xa0\xa7m\xf4\x94\x93\xaer\xd0\xd1\xcaQn\x11\xfc\x04U\xb8\x00\xc0>S\x8c\xd1\x1a-^\"\xf5\xed\xcbdž\x8fўP\xfb\xef\xe6B\x0e\xad\xd5fr\xb1\xa0\xeb!\xaf\xbd\xa6\xfaʽ\x19h\xda\x03r\xbb\xaff%\x9e\xcb\xfdս@C\x18\xecz`f\xce\x04\xa1\xe1\xf8\x83\xf2\x04EI!\x9f\xe6Dn̩&\x13\x00\x11\xd0\xf7$kp\xe3y\xe5\xf5\x82\x89+\xfc\x00yqp\xf9Njt%mg@u\xb5\xa1\xd5\x0f(q\xf6U\x8ddN\x1e栠E\x15\x9b\x0eo\xab1\xee\tRH\xd3\xf4+X\xb8\x85̏5\x992\xa5Ms\xa2\xfb\x12\\\xa9\xf7%\x87\xc8\x1d\xb6\xab\xbbc\v\x90\xa5I\u0603\xcb\xfa\xedV\xb4rA\x1f٢\\\x10\xba\x90\xe5\x1e\xc2\xdd\r+_آ\n)\xfa\x1dx\xa0\xcc \xbb\xb3p\xd1\xc3b\xa4ݥ\x82\x83\xd9w\x8b'0\xb5\xec(\x93B\xb3\x1cT\by\xbb\x9de\xd2\x1e\xdc)e\xbc\xec\n\xdft\x8dX3U\\*\x95d\xa5\xbeuo6\xbc\x86s\xf9\xd0F\xd0\xde(\x98\xd3%\x106%\xcc\x10\x10\x99\xdd\x17P\x8ee\xe3'<2\x105{\x93\xe5~\f\xde\x0e\x10\xe5b?\x04\x8c\xf0d3\xb1\xd3)\xd6|\xfck\xca\xf8sl\x9b\xa5\xbc\xf4\xa3\xf1}\xfd\xf6\a9\x1a\x15S\xd9_\x84M\x80\xbc\x03\x9a\xaf\xc2\xf9\xa0\xc6XS\x15i@\x12U\x8a&G|\x86\x93\x11c\xdf\xf9Y\x1c\xd2pc\x82\xed\xb1\xb1k\xfe|f\x9aڎ\x05\xf1\xacڎ\xfd@%\xe8R\\3W-\x00VT\x06\xc5\x19\xe7^QM\x84\xe63\x01k\xa0B\xee\x9c^V|z=\xda%\xf2l\t\x83w\xae.FuYs\xf3zC\xb3\x91\xfc\x16}\x04\xbc\x83w%K\xf2@\x85\tD_)s\x85ܓ\xeacw\xd5\r\xaaf\x11Ood\xd6\x05\x955\xa4\xb7\x810j\x85\xe9V\xfbN\xda\rk\x9a\xe62\xbb\xb7\xeaȂ\xce\xe0\xf8X\x93Wo^[R\xb1Z\x87\x15\x19\x11\x12\xc1\r\xe6\"\xb1\x85\x92K\x96[\xd5\xe9=U\x8cN\xb85\x82\xa7\xa0@d\xa0ɗ'\xef/\xde\xfdt}\xf1\xe6\xf24\n\xb85\x9dᱠ\xc2\xd2`\xa9\x834\xafv\xdf.\x00Ē)),\x82\xe2\xb0q5%\x94,\xc3l\xb3*\x13͚Z|鵹(\x88Պ\x83#\x84\x89\xa24\xc1;\xfa\xc08'\x938\x88\xa5\xc8\xe6T\xcc,^_\xcb\xd2\xce\xf3\xcb/\x11+\n\xf22\xf3\a3\n\xa2?L_\x9e\xf9p\x16\xe5\\>h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|ufJֹ\x91Q\x10C\x1e\xe5}\x95\b7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcdߚH\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x97\xbcB\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\xa8\xc0\x19^l?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]/\xe2\xb0\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\x15,\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Qk\xe6\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6\x15~\x89\x19-J\x0f\x05bz\vM_K\xc6\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15\n\x0f}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833\xacPDE^W):q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻DҾ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`\xb1C\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ\x9a\xdcQ\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd;N\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9c\xdfHβ\xa8\x98C\x9b\xf4\xae\x90抒sR \xa81y+ 6,s\xc1\x1f\xe8J\x9f\x91kX\x82:#W\xd3kin\x9c\xb9Y\xdfO\x89;\xbc\xd2\x03%lJ^\xba\xfeh\xc4\xd0\x19\xba.\xaa\xfauqD\xa1Z\x13s\xc2\xe4\x81\xe9\xbevz\xb4\xc0\xdc8\x80\xbf\xc1\xafZ\xd1\xe9\xfe\xfd\xec\xe4\xc3\xd9\x14\xb2U\xc6\xd3y\xd6E\x86ɮu\xf9\xf5\xfa\xdcƹ\x8cV\xda\xc0\"\x94\rC\a\n\xc32\x93\x85\x14\x1a\\A\xbd\x14\x0fW\xb5B\xe70\xd3=\xf78U\xc9+\xa46\xb7\x86\xaa=˴գ}Jo\x02\x18K\xfe\x19\xe5\x1cr\xc2\x16\v\xc8\x195\xc0\xe3\xfd\xe4\xa1\x02h\xb3ܣkw\xe9\x8aI&\xe9\rs*r\x0e\n\xeb\x15z\x1f`\v\xbe\x01\xb5`\x82\xc6\x16\f!Uz\x17\xba,!'4ˤ\xca}-\xb8Pً\xaax\x8d\xbf\xe2x\xa8+5$\xcfz\xbe^4\xe4\t\x97ٽ&\xa50\x8c\xd7\xe5!CmHߨ1\x1aj\x12\x8b\xa9\xfesT\x9d\x89\x11\xf6#;\xffM\xfd'\xfc!V\xf9\xedc\xf9\xecW\xcfws\xacՠ\x04$\rL\xa6\x94\xf1b+\f\xcc\v\x96V}\xb1DU\xd7W\xad\x04M\x9a\x91\x81\x05\x88\xdb]u(\xb2M\xac\x8bF\xef#\xab\x90\xb8\xd17G!\xa1\x16Ps\xec([\x9c\x1ck\f\x85m9\x13Ь_̰&jz\b\xb3y\x82\x1d?\xf2\x16jz\x10\x98)\xec1\xb2jԶts\xef\x93̯\xa44\xe4\xe4\xf8\xfc\xf8t#\xa8u\x9c\x0eu\xca88\xe9\xea\x8a,e\xd5f%\x83\xd4lQ\xf0\x15\xee\xcfq\x8e\x1d\x9d\xfcuXU\xa6ň\t25\xbbˡ \xd4\x19ђ\x18EC\x97\x81\xf4\xb9Zh\x16\xb8Q\xa5\xd7UN\x8e\u007f>>#`\xb2\xd4|`B\x1e\xa486HFcr'I\xa9\xeb\x89'\xc3\\ɒ\bpu\x00\xe0\xb1\xe0,c\x86\xafP\xcc'Ô\xa5q\xc5\x17\xb1A\"\x16ں|d\xc6\xdf\xd3I\a;%_\xe1iw\xaa\x02\xa1\xd6\x18Z\xc2\xf9\x1c(7\xf3\xf4\xfb}\x96.\x85\x14\xa3\xff\x06%\xb1\x8c\x97\xf0\x10S\xbd;\t\xb1\xb3\xe68@\xc6I\x8a\x1ba\xfd\xed\xc4$\x06\xab |\x03\xd1*'\xd9\xe8Hzww\xf3\r\x98\xb6\bKB\x87\x9dQ\xc8\xcfG\x976\xa8\xa9T\x1d-w\x9f\x1e}\xe5\xdf\\\xea$̐\xcd~\xadڸ\xee\x13\xceH\x11i>g7\x8cl\xa7%\xfb\x8cFru\x93\x9e\x88\xf5wYZlM脯\xaa*\xb2\x1a\f9\xb2SOO{f\x02\xf7\xf3\xaf@s\xac\xdb+\xb4\x01\x9a\xa8\"\x1d\xe0\xa85\xe6r\x18\xa5\xc6\xf5ݝ;\x90=v\xb4Y\x02\xcb\xd3\xfe\x18\xcfT:\x9bt\x15^\x14\x14\x8e\xfd\xfa9~$&\xb9\xc1+\xdc.\xf8\xdf'\xbdr\x13ih\u007f\xec\x96\xe8k;'\x16\xef\b\x83\t\x9c&\x1e\x8a\x1e\xb3\xeb\x9f%Lzg\xab\x92\xae\b\x98\xc3U/\x98\xfe\xeee|Z\xda\xfa8\xc8\xfd\x92\xe4\x12^\xcd\xf1\x9ch\xc2\xe9}|<\xf5K\xb5$i\x89\x88\xed\xd7\xfba\xa2\xe7\r\x05\xd2[\xdf\xc2\xcb<\xc9\u05cd7/\x1b\x1bIh\x96\xa5\x15\x8ar\xc3w/G\x86\xa5A-c\x13\x1c\xebћ\xc4\n\x19\xef\xbf\f\xa3ׅ\xb7\xc3\\w;\xc8e\xb7\x8ez\x89\x8a\x88r1\xe9\xc1I\xaa\x02\x18\xca\xd4\x04\xe37\xbe\x873\xa5*\xb6y\x8d\xd3\v!\xdc>\xfa\x1e\xaa0T̀\xbc\xb03\xfd\xe3\x1f\xfe\xf0\xfb?\x8c\x11\r\xc9PC`\x99\nruq}\xf1\xd3\xed\xfbWX\xc4-\x95ʟ\xe5f\x1b\x96mH\x96?\xed\xc8<\x82\xb2\xd8+5\x16:\xeb\xb3\xc3\xd6\xd6\xf0\xfeo\xe7\\֩q\xb6\xe60\x12\xd9\xcdG\xe23}\x84\xd8\b\x0fч\xb6\xb3MV\xdc\xca\xec\xfe\x00\x96\xf6\xf1ݫ\x1b\a\xaa6\xb6\x93v\x81\x8a\xe0bfb)\xf9\xd2\xf5\v\xbc{u\x83\bJ\xdbY\xfb6\xc6\a\xd0շ\xb2s\f7\xe1]jN\x12T\xb6(|\xc7LJ\x14Pδa\x19~\xab\nS$\xda\xf7\xf2>%\x8b\xe7\x93\xf1+\x1c\xbf\r\xe9@\xe8bH>\xcdk\xae\x89\x96\x8b\xa1\x0f\x8bh\xb8&R\xaft\r\x1a\xc9\xc15\x12'\xea\xa5\xea\xa7\xc7\x0f\x1aɧ\xad\x91|n22\xf9\xd5B\xc1\xad\x91EϬ\t\a\xe4@9\x13\xa1\x13ݶ\xa4\x06\x92'l\xa9\xeb\x1d}qsUy\xc7e+\x11\x01\x93W\xa2\xa1\xea2\x9b\x87،\x00\xad\xcf1=\xa2,\x9c\xe7+4\x94\x8c\x8fX\x15\n\xb0\v\x9f\x14gUE\x02D\a\b\xf7#\x98,\xfe\xb4\xa0O\xc6\xe7\x8e\xf8xbخ\xbei\x18\x99\xa2z\x0eة\x02\x1e\x99ѡ\xdb5\xd5R\xb8\x10\xae\xdf>&\xe3\x03\x98L\x93\x82j\xed\x02w\xa6^\x84\xfbȍ̏\x13\xa2\xb7\x8d\t\x91\x99\xa2\x19\x90\x02\x14\x939\xc1\xaa\u007f\xb9|\x88\x9f\xe7\x04fL\xe8@\xbfv\xa2\xe1`X]\t\x92\"\xc2u\xe7\xd9w\xad.Rخ\xbc4\x99L\xe0\xc3\xfeu\x8f\xc5\xf5\x04\xa2諓8M{|J\xca\xf9\xaa>\xa8ᦧ9\xfc&mf\x12\xa5\"\xa1^\xf7z&Q|4\xb0\x95yd\x8fB\x9d\x95ԇ\xfc[\xd4ɴ=UټG\x9b/\x12\x9f\xbe<\xa46=9\x86Ԧ\xbdǐ\xda4\xa46\r\xa9MCjӐڴ\x17\x88!\xb5\xa9\x9aѐڴc\f\xa9MCj\xd3\xd3cHm\x1aR\x9b\xea1\xa46\xed5\x86Ԧ=Ɛ\xda4\xa46m\x1dC qHm\xfa5\x06\x12\x87Ԧ\xb8ׇԦ\x881\xa46\r\xa9Ma\f\xa9M\x91c\xd0H\x86Ԧ_\xa3F\xf2\xb9\xc9\xc8^\xd5\xc5\"_\vy<7JNz\x94\x19\xbb\xc1X=\xcb|\x1a\x90\x9c&\xd7\xd6q\xd3\x19\x93W\xad\xf4\f߄\xdfUi\x89\x82\xe8\x13}\xea\xf4\xa4\xbe\xf5z\xa2k2\x85\xa2`\xfa\xbc\x90\xee\xff\xd59\x05\x8dd\x02\xe7_\x8b\x11\x0e\xa9\xc27%\x8b\xe0\xa9\f\x82$^\xb7;{\x003\x01\xa2a\x1e2s\xa0\x8fv\xd3#c`G\xb6@\x00\x9b\xc4\\\xbb3\x05\xd6\"\xfeɩ >K`3\xda\xdf'\xe1\x02S\xe16\"\xfd\x89\x10\xab\xec\x80mQ\xfe4\x95\\\x1f>\xc2\xff\f\xd1\xfd\xc3G\xf6wD\xf5\xc9J\x96I0\xb7D\xf4}d>\x916;\xa3\xf9!*\x9f\x06\xb3;\x92ߊȧ\x12S\xaf(~\x8f\xe0TO\xe5:ݓ\x9c\xac)\xf9d㻹\x02=\x97<\x9a\u05f6\xf8\xec\x1b&آ\\X6\xa1-{d\xcb*\x9b9\x9eFB\x9e\x93\xd3:\\\x18\xce\x02f9`\x13K\xcaxJ\xa9:,\xad7\xa7\xe8\x9f\xd0e\x96\x01\xe4VN\xbe\xaeC\xe0\xd10\u007f?\xaeV\xee\xbaj0M^\xc4R\x9ek\xa8\x88\xf6\xdd\xef\u007f\x97\xb4\xfb)\x96ab\xc2\xc6\xd3\xc9\x1a\b9\x1a\x93\xfd\x135\xfa\xa8\x1b\xa9\x8e\x94\xe7I\xceؑ\x98A\xfe\x9e(\x1av$e\x10\x96&f\x0f\x94\x90ыs\xf6L\xc4ؑ\x84\xe1q\x94\xa8\x80l&`\xac'R\xa4\xa1<=\xf9\xa2\x87l{\xae\xa4\x8b\xed\t\x17\xa9$Iz'[\xf4O\xb48`\xb7\xc3:s\xa0ww\xfc^.\xba\x03x\x0e{&U<\x17Z\x0e\x91B\xf0\x11\xbb\xcf&\xefj\x9f䉞\x89\x13}\x92&R\x13&v$K\xf4\xf14\xf7L\x94\xe8E>\xa9\xe1\x88\xe4PD\xff0D\xef\x10Ď\x84\x88>=a;C\x0f\xa9\xfd\v\xc3h\x87\x1d\xd6\xc2\a\x89ja3\xe4p\xd0\xd0\xc1\xc1\xc3\x06\xe9I\f\xbb\x13\x18\x1a\x89\b\xa98\xdcL^蓄Ѓ\xa2S\x99\u007fRP%\x99i3\xc1\f\xa3\xfc5p\xba\xba\x85L\x8a\x13\xa2u\x1bsN}\xe7L\xc8Å\xda\x10\r\x89g\xad\xa8>\x12\x8aq\n\xbbzӾ=\xf9q\xe3\x16䣹\fܕ\xd2C\x10\xc1_\xe5\x03\x91S\x03\x82\x9c0\x11\xe8 ޏZ;\vj\u007fQu\xac\xed__|\x15Ϲ\xdcd>_\xc7\x0e\xba\xb6\xb4~>\xbf\x9e\xff\xc0\xe1\x1d{\x1e\xf0\xb4\x8c\xf7ѷ\x9c{\xceA\xd8\xe6\xefћW\xb7\xe1{\x81\xf3\x0e\xdc\x04\xbdԾlC\x02\xccϔ\xa8\x92\xd3ΞL9#\t\x9d\xc7v\xa5\x9bթc\xd1`\xb7\xa4\x9a\xd5ic\xf1\x13ݖf\x96\x942\xf6\xd1=\x9ckib\xe9\xe6\xe7\x96\x141\xaf\x9e%*\xf1\xc9\xe9a\x83\x1d\x169v\xa4\x81\rv\xd8'd\x87}\x1e\x16F\xa3\xd6\xc97\x8afps053\xb0+\x92\x97\x8az\x91\x11\x14\xbc${\xc32\x19\x01\x90;NU\x15\xae\xc1\x8a+Ӓ'\x14\xaf*\v)\xda՟\\NE\xb3\x88K4P\x9f\xedұj\xaf(\xa5\x9c\xd0BI\xa7\xf6\x11U\na\xa5\xae?K\x16)\xd6V\xd2i\x12\xb2Y\xb3G\xb3\x99\xdd.\xabba\x15\x1c\x96 _\x1e\xe6 \x82\x96\xe9'lg7\x95*c\x13\xbe\"s\xcaS\xc2/\x0f\xcc\xcc\t%\xf7\x8cs?\xcd1\xb9\x05C̜\xe9Dg*\x97b\x86\x9bA݄᱀̪\x1d\x19\a*\xca\"m\xfdVY]\xc9R\x85\xf5\xfb\xb6qa\x96)I\x1b\x82\xf1\xb3\xb0\xd5\xc7z\xf7\x81M@\xacKP,5\x84:M\x0fL\xc3Y\x1f̆6\xa3\xee\x1c\xb8u\x17J.Y\x0e9\x99\xac\xd2\xe8_樵\x8e\xc9{\x84\x17\xf8\xbe\x90b$`F\xadm\x14\u007fR\x9d\x10wg\xde\xcd\xd3գ\x109˨I\xb0\xb14\x16֫\xcb\xe9\x91%\xa3\x88\x85\x06\xe5F\x03=\x11\x92HT\x8aK\xc1\xcc\nc\xa3\xf3Ґ\\>\x88\xd31\xb6\x9eMaR\x94L\xc0P\u007f\xafյ\x12D\x81\xa5\t\b:\xe1)\xca\tf\xe2\xdeu\x12(\x99\x025eBw\xbf\x195\xd0\xe9\x0fp\xf4p\xd8\xe3\xc0\xb4\x8f\x80NI)4Dߟi؇\u007f\xfc\xd7\x0fg\x1f\xb2\x05\xc8\xd2|R\x0e\u00879\xcb\xe6M\u007f\x03[\x80&\xb2\xecsm\xcdH\xf2\xc2O\xab\x9b\"\x9e\xb9}\xe4/Ϋ\x98\xa45Ɔ\xd8;\xe2F\xeb\xd5\xfc\xaa\xc4\xe9\xa8uS\xcb\xc3^_\xdf\xfe\xf4\xdd\xc5_.\xbf\x1b\x93K\x9a͛eH\x05\xa1VnD\xc1D\xb92\xa7K \x94\x94\x82\xfd\xb3t\x9d\xc7\xc9I\xf5\x9dӐ\x83\x1f\x057-_?\xc9R\xb4\x82\"\x8a\t\xb46\xe8;\xa6\xb1\xd1+B\xf1\xe9\xacR\x03\x99*\xb9\x88cJ-\xeb\x91\\Z0\xce[\x84\x96\xe6\x1c\x14\x90\x19[F\nY\v\xd57G\xa6yH*\xc6#lO\x8f\xd5b\xe9D\x96\x91&\xd0\x1c\x88\x00cOw\x15\xe1\x92B\xb7jږ\x1at\\~\xf9\xa4\xc4T\xeaB\xb1\x05U\x8c\xaf\x9a\x93\xb4\xea\xeb\xb5\f~\xb8U\xac\xa8m\xa2\xf0\xf5\xdb\xcb[r\xfd\xf6\x8e\x14\n\xcbz\xba\x9c\xe1h\v\xd2n/\x99\x80\xdd \xb7\xe1\xf9\x98\\\x88\x95\xfb\x90\xe3\xe5\x91Z\x06g\xda\x00Z*ޕ\x10\x9a\xd6\x1f}5\xc6\xff;\xb2;\xa8bCDUzy\xb6q\xc9\xc6y.\xd8$\xf2\x1e).\xbdA\x03=\xef\xd8$\xa4z\xad]\x9a\xf0뺱\xa8WP\xb8\xbe\xec\xb1ҒV$\x8d[\x88\xccО?\x9e\xec\xd3Iw\x80f\xcd%\xc5{\xdd:\x1d^\xd3\xcaa\xe5\xe85\xc1\xdf#Eì\xba\xba\t\xe4\xe84j\x94\x04\t@-\x1dZ\x9d\x84\xe5n\x82.A\xe2\x8c|E\xfeD\x1eɟ\x12 \xfe\xf1\x0f\u007f\xf8\xfd\x1f\xe3\xfdY}\xf4\x89>ʨ\xf3v_\xdd\xf4\xdc\xe7\xef-\x1b\xb3\x90\xec\xce\x18I&,鎋\xb3\xec\r(+&<\xc5\xc4㲇\xc7\xd6.\xe1\x93${\x97\x85q5\xad\x95/g\xf4'@\xac\x9c\xb0[\b?\xc5\x17K\xfe\xe4\t\xdfN\xf1\xafR\x9bk\xcfΘn\xcc8\x89\"\xfc\xe1&\vj\xb2y\x9b\xdfZ\x13\"\xe9\xd8\xd7\xe5\x97I.1\x96\xe5\xae\x03\xcdYB\xbe\xf0\xc7;\xbai\xe9\xb3-Jݤ\xa8>\xactͭ\x8f~\n\xaf\x97\xbb\x82\xe5\x89<\xa1\x90\xb97\x18\xec\x92\U000c640c\xb5\x18\xdc\xd8b7\xf8(EZ\xf1\x97\xfab\xbe\xe5\x85\x19\x15\xee&\xf1\x14\x94\u009bf)(]a\xc6$\xcb \x81,{p\xc1BI#3\xc9Sh\v\xb5Ɨ\xe4\xeeU|\x19\xc5v機\x03\xfah]\xb4\xfaM2a\xfe\xed\xf5͙\x9d\xd2\x19\x91\x8aܾ\xba\xbb\xe9w\x95\x89\x90\xa3\xbbW7G\x1fpOҢS\xa3\xb6.\x17\xf9n\xa0\x828\xeb\xacOE\x81\xd8D\xe7V\b\xd0Z0\xa3\x05-F\xf7\xb0\x8a\xd2yӱ\x94\x84\xa3\xcdI\xbb\xc5/\xe8\xfe7\xc1\x14М}B\xc5\x14<\x97\xaa\xe7\xd5]Ua!\x97\x91N#\xb4\xf6\x02t\x10y!\x990\xba\xab\xd4B\x14\xd8M\x93\xf1\x93IY\x1cJ-t\x8c\xa1\xd4\xc2\xd61\x94Z\x18J-\f\xa5\x16\x86R\v\xddc(\xb50\x94Z\xf8\xac\x92\xa7\x87R\v\x8d1\x94ZH\x18C\xa9\x85mc(\xb5\xb0\xd7\x18J-l\x8e\xa1\xd4B\xe7\x18J-t\x8c\xa1\xd4¾c(\xb5P\x8d_\xce\x15\x9f\xa1\xd4§z\xc5g(\xb5\xb0\xcf\xf8<.B\r\xa5\x16\x86R\v\x01/C\xa9\x85\xa81\x94ZX\x1bC\xa9\x85ϕ\xa8\x86R\vC\xa9\x85\xa1\xd4B\xf7w\u007f\xedv\xd8Pj\xe1S\xb5\xc3>\x0f\vc(\xb50\x94Z\x18J-\f\xa5\x16\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xf0+\xf2*&i\x8d\n\xb4,U\x16g\a\xb7\x89\xec\x95\\\x14\xa5\x01\xf2.\x80\xaa\x94娅\xa3,a\xbay\xa3\xff\xc3v\"̤\x98\xb2\x99W\xf4\xce\x17T\xd0\x19\x8c*\xfc\x8c\xea\xfbw\xe7\x1f\"7\x9e\xb3\x05\x8b+\xb2`G]\xb1ই\x87#Ѡ\xeekN\xf74\xa6\vj\f(\xf1\x92\xfc\xc7\xc9?~\xfb\xf3\xe8\xf4\xcf''?|5\xfa\xf7\x1f\u007f{\xf2\x8f1\xfeǿ\x9c\xfe\xf9\xf4\xe7\xf0\x8fߞ\x9e\x9e\x9c\xfc\xf0\xed\x9bo\xeen.\u007fd\xa7?\xff \xcaŽ\xfb\xd7\xcf'?\xc0\xe5\x8f{\x029=\xfd\xf3\x97\xd1S=\xb0q\xda>\x8f\xdf!\xe5\xd4)Eȷ\x17\xf4\xd12\xd8xRX\xc8R\x18w\xcb\xc6\x1d\xf3\xeaD\xb84\xac\xd8CI>\x95\x83I\xfa\x18\xda>\x1fm8\x9f\x11c8\x9f\xee|\xbe\xf3\xb4\xd3>\xa1\xd1s\\x\x95i\xc7\t\x8d\x86\x19\x047\x1a\xba\xd5<\x99&r\xc1\x8c5\xa7S\xae\x197\n\xa9\xe0\x95\x94\xa6\x8b\xda\xf1\xaax-o\xeanS0ݼ\xa0Ѹ\x14.\x83훢\x96RQ\xc7)\x90\xe7\x8cr\x982\x01\xb9SO\u007f}\xfc.\xe95\rY\xa9\x98Y\xbd\x92\xc2\xc0c\x94c\xbf}^nۀ\x88ی\xf8C\x13&Dd\xe1\xae\x1d\xadU\bÛ\u007fq*+\x10U\n\xf4g\xb9:\x17`\x9cs\a\xcdp\xbcٳ6\xf9(\xf0\xc1\xf5\xe2\xa8)\xb6\xe4\xaeO)\xd7ь<]\x1a[E\xe6\x8d5\xdf{\xedb\x05\x85\xe4 \xa4\xf1\x1e\xbf\xa4\xd3 \xa7\b\x8d8\x9fB\xe3\xb2@K\xe2%)\x99\xb50f:\xe0\xfc\xa6\x9a9F\x98\xe2S>4\xac\x9bq^\x18W^\x86\xa4\xa0\xbf\x02\x9acM\x99\x82\x9a\xb9\xcbS]P}\x0f\xb9\xfb!QǮB\xb2v\xc6\xd5\xd2\xefV\x05$\xc7SQ\xb7vٿ\x18\xe7\x8d\xf7\xc6\xf6(\xefE\U000f70af\xdeIi\xbe\xaeʘ\xf4\"\xe4オԎ\x03E#e\x8ee\xbb\xed\xfcF\xb8\x89X\xb6\xa5Yi\xc5S_\x8a\x95\xf0\x81\x19\x84*Ņ\xfeF\xc92Z\x15\xd8Pֿ\xb9z\x8d\xfc\xb2\xf4\x192¨\x15\x96\xa6Ja\x12\xed3W\xd9c\u007f\xf39MI\xd96\x15{\b\xe1z\xf2\x86\xae\b\xe5Zz\xc31!\x18\xdc\xe5!!\xdeU\x93r3z\"\xcd|ݧ\x83\xeca\xf3;\xf1\x05\x8c\xea\x04\x9bʓi\x97\xd0Dž\x84`\xe9=hR(\xc8 \a\x91ES\xef\xc7I\x83@ʿ\x96²\x97^\xb4\u007f\x15\xf2\u007f\x9c˸\x9fU\x8f\x99Jަ\xa7\x98\xaf\x84̥Ԡ\\r\x98*!m\xe3\xbf-'\xc0\xc18G\t\x16\xb9\xa5\xc6y\xfe\u0602\xce\xe2O\x135\x95(4\x92\x80Х\x02\xef47$\x97\tf\x80\xaf#e\x97\xfe\xb7\xab\xd7\xe4+rb\xd7~\x8a\xe4?\xa5\x8c\xa7T}\xc1\xdb\x1fk܄M\xc3\x14-J\xe3u\x02\x81ƾr\xac\xfa\x8c\bIt\x99\xcd\x03NS\xbcC\xc1y\xe5oH\xe1\x15\xb6\x815}\x02\xac\xa9\xa7`\xfd\x9b\x06\xd5[\xae\xfe\xed\x03\xc8\xd5>n0˛ڻ\x86\f\x85,\xc0М\x1a\x9a\x12|+E\xa3:\xe2\xdaQH\xa1\xdd\xddG\x01I;\x1a\xe6\xaf\xec(|\x1c)\xad\xe1;&\xcaGw;\xa0\xbfC\xf9\xf6\x12\xc1\x11\x1fJJ\x91(\x13 \xb4(8s\xe5\xfb\xd6Z\xc4\\\xb5H7m\xef7MM\x14\x0f\x94siՌ\x84\xf0\xb8\xa2\"\x97\x8b\x8d\xc5[C\x14h\x82U\xdcXp\xc7\xe1\xdcv\xd8\xe2ew}8\u007fm\x87\xad\x8f\xeb\x9e\xc3\x12\x12\xaa\x94\xaf7Q\xb2P\xacA\x1a\xa8\x06\xc1&y?9\x9d\x00w\xaa\xa1;9z\xf3\xe4${E\x13\x9d\xaaJ\xf2\xfe%/\xdeI\x0e.7> ɂ\xfd\xc5\xe0\b_\xee\x8b#\xf4>\xb5p\x94\xecE\xff\x14qT&hxd\x1dGVMl\xe3Ȃ\xfd\x85\xe0(9\x04\xa1!\xcb䢸Qr\xca\xe2\x0f\xeb\x86\xe8\xf7\xe0\xea\xe4\x9cx\xd1_j\xe8\xca\"G=\x12\x81\xc7k\xe4~2T5.=Q\xe3d\x9e\xbf\xc5\x15\r\xf4\xffk\xa8\x10ȵ\xcf\xd6\xf4\n\xff\xd5\xf8\xd96\xf3\x85\n\x99\a@\x1fT\xbaɌr\xecH\x94F\x17d\x9d6\xd6\x01\xf6\xb8\xcfE\\c;\x0f'\xe4\xf4aK\x16\xfc%\xc13@Bs?\x99C\xa3v\xbc\xbb\x80w\xe7\xee\xcbX\xd8I\x80õ8\xab\xa7\x84\xe4\xab<Į\xec\x17Ӧ+}\xa9\xec7UO%\x8bp\x10yj%\xa8\x82\x9a\xf9\x19Q\xc0\xf1\xe2^`h\xf7Ρu\x9c\xb6O\x8d\x05\a\xce\x106\x0e\xf5l&E\xda%v\\5\x86\x05\x82F\x947\xe5{\a.\x98Ιew\x86\x89YB\nF\xedQ\xa1\x9c\xb72\xf4\x0e\xe1R\t\x9c\xa0ꓺ\xe9:H\xf6\xd9;bn\xfb\x12{\xfb\x0f\xb6\xb87jwE\xbc3e\x87{ù+\xa2A~\x1c\xf7\xc6l\xa1\xe9+e\xbfk\x18\xe5\xb7E|\x87\x1f\xb2N\xcb\u07fc\xb9\xbdh\x83L\xe3\xec\x98\u05eb\x9czla\x12\x9a/\x98\xd6L\n\xf2\x00\x93\xb9\x94\xf7IpOB\xea\xfc\x8c\x99y9\x19gr\xd1Ȣ\x1fi6\xd3\xe7\xfed\x8f,vҚ\x9c0\xc1í\a\xe7!\x14F\x87\x88\x81]L\x9a\x96Ua\x15\t\xd0w*\xf4\t\xae\x9bh\xbfN-R\x857\x16>\xb8J\xb5I\x8a\u05c9\x05ş \xc7d\xbc\xf8\xea2\x8djO\x8e0\xeb}I\x13\xb7v/]\xe8\xe7÷\x11p\xa6Z\x06\xba\u007f\x1b\x81\xbfְH\x0e\xae8D\xa2\xddǦ\xad\x86\u07b5B\xe2\"ډ\xb6\xe41\xd6n\xf3S\xe5\xa2\x1f\x91\x1a\xb0o?\x8f%ی\f\xf16\x92\xb3\xe9\x14\xc2\r\xe7H\xb1WPE\x17\xd6p\xd0ħ\xfeN`\xc6\xdc5\xd3J\xb5\x8a\x8cPTE\xc2Μ\xba\xc7\fY\xb0\xd9\xdcyi\b\xc5R\x94\xf1\xe5&\x8d$\\Ҝ \x17\x97\x8a=4\x9f\xde{\fͧ\x87\xe6\xd3C\xf3\xe9\xd814\x9f\x1e\x9aO\xef=\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xb4\x1bC\xf3\xe9\x8414\x9f\xde6\x86\xe6\xd3{\x8d\xa1\xf9\xf4\xe6\x18\x9aOw\x8e\xa1\xf9t\xc7\x18\x9aO\xef;\x86\xe6\xd3\xd5\xf8\xe54=\x1b\x9aO\u007f\xaaMφ\xe6\xd3\xfb\x8cϣ5\xdc\xd0|zh>\x1d\xf024\x9f\x8e\x1aC\xf3\xe9\xb514\x9f\xfe\\\x89jh>=4\x9f\x1e\x9aOw\u007f\xf7\xd7n\x87\rͧ?U;\xec\xf3\xb00\x86\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6ӿ\"\xafb\xe2M\x91\x9cE\xb5lۣ[@J\xed\x93P\xbb\xd32\xb2r:\x05\x85\xc2\x17g\x17\x94\xa3\xb4K`\xa13T%\xba}F,6\nT@sW\xf3\"\xce/\xd89\xadP\x84\x14ۗ\xb9{\xa9\x91\xe1\x01r\xf9\xf6\xeb\xda\xe9\x9a\xd2\xea \xedv \xae\xe7\xad\xc8\xd2\xef\nՄ\xd0Q\x9d5\x0e\xb7.\x01?\xe3R\xfb{\xb2\x88\xeclN\x85\x00\xee\x95n\x16\x87\xd99\xd5d\x02 \x88,@8\xb5\x85\x12\xcdČ\x03\xa1\xc6\xd0l>\xb6+\x88\xf3\x9ey\"\xf0]\xeb\xea\x99j\xa3\x80.\x1c1(X\xc4\xf6\x19\xb4S$4SRk\xb2(\xb9aE5I\xa2\x01KeD&-]M\xeb\r\xc6$\xf1\xfa\x02\xeaY\xb5\x8a\xe89\xba2h\x8d3o\xa82g\xd8\x0evQ\x98\x95\xbbK\x15'\xf8\xb0k\xa7҆d\x9c\x810~ծ>#\xce\xf3\x8c\xc4\xe6\xc6\xe3\xf5]\xb7\vڣV\xe4\xe8\x15)\x8cv7}\xd2&꧘3\xed\xbdo\xfa\x8cP\x13\x04e4\xd1\aZB\xb2\x0f\n\x9c\x9b\xb5\xff)q\x9auI\u007f]_5\xab\x99\xe1\x94\xd38\xc500\xa5\xb3V-\x87\xda>\xc4$wd\xabQ`\xb1\xec\x90\xc3\x02\x1e\x1c\x01K\xcb? \x03\xb6D\u007f\x90\xe5\x8cQ\x10\u05f9\xe8\xb33ц\xee\xfa\x06\xb4\xa63\xb8\x89L\xb1\xd9\xe6 \xc6,\x9b\x9a\xb8\"\r.\xacFfdC\x87\xab/\xa0\xb4-\xd0(\xb0\v\xb7\xc6\xca\xe6|P\xcc\x18@\"\xc6\xceU\x98y\x18\x99\a\xbe1\xb9\xe6\xf5\x987\xe1\x83\xeeC\xb1Tk\xf5)\x91\xbbK\x1d\x13 \x13\xc5`J\xa6LP\xee\xefa\xc4]8\xc2~\x16T[ҤZ\x83B\x9f\x8bw;\x05\xdc\xc4\x11\xec\xf7\x1e\x91F\x95\"\xa3\x8d.\x97X\xfd\x8eM\xc9\f\xefzD\x1a\x13s*ȿ~\xf5\xef\u007f$\x93\x95Ղ\xd186\xd2P^m \a1\x8b\xac\xed\xef\xc5S\xbb\x0eYE\t\x9c-X\xac[\xc8\x1a\x03\xbf\xbb\x9f\xb4\x03\x8d\xe79,\xcf\x1b\xf49\xe2r\x16\x87\xd3W\xe1~eug2F\x91Or\xedw\xb0\x01\xc9Y\xb6Jf\x04\xa1y\x0e\x99\xcb\a\xe7\xca\xebyb\xeb+\x8e\x85,J\xeeR2\xbe\x0e\x95%\xa3@\x96\x1a6\xabau\xf2\xc1Xj\bS[\xef\x10\xee\xaeL\xf9\xa5\xc4)-\xbe\xf0\x9c\x0f\x8dW=s\xd0O\xfc5\xe5|B\xb3\xfb;\xf9\x9d\x9c\xe9\xb7\xe2R\xa9\xc86\xfbH\xfd\x01\x1f\x9cZ-f^\x8a{ly[\xd7\x1a\x96q\xd2V\x96\xa6(M\xb8\xe4\xddt\xef\x86͌\xae\aY)h\xc13\\\xcf\x0e\x1e\xed\xb9E\xf7l\x1c;\xf0\xc5w\x1cw\xe1rV\xcd[\af\x10{#\xe8w_\xfd\xeb\xbf9\x96E\xa4\"\xff\xf6\x15^\x19\xd5gN\x88\xa1n`\x15\xd9\x05\xe5<6\x98\xd5d0\x96\xe8\xc7\x1dL\xe2\xd9y\x84Ig\a\xcfbr\xdf\xdd\xfd\x1d\xedmf4\xf0陫=\x12<\x88Q@\x8fQ\x89;\xf6R\x16\x8b\xdc|\x04\x83v)y\xb9\x80װdY\\\x84\xbf\x85\xea\x16\x94\x10\t\xe2L\x1b\"\xe3\xaa@L\xb8\xcc\xeeI\xee\x015\xeef\xac\xf7\xb1\x8e\xc1L\xc2-\x94\xad\xab\xab\uf7d0\xd8fD\vZ\x14U-\aE\x1fZ\x8bE^\x12}\x01\x85\xa6\x06\xaaӳ:\xdctc\x15\xf6\xf0n\x03\xab5\xa0@0E\xac\xf4s\xc3\u07fc\xdehH\x15:\xe8%\xe5\x1a\xf8=qz\x9a\xdd9\xe4\xcc\xf1\xe1\xf5\x1eI\x0fiwzZ8\x16U\xae\xc0\x82\x1ao\xd3$\xe6\xcf \xd5\x16\xa04\xd3V\x81y\x8fg\xe2\x15\xa7l\x91~\xc3?\xa5!A\x8f\x16\xb0)y\t\xa3\x06\x9dF\xbe\x18\x8d\xe8^u\x8f\xe2\xee\xb68\x96\x86-}ӹ\xfe\x8d\xcc= dծ\r\xb35e\xa3\xc9a[\xa1\x87^\nG_\xb6\xff\xbe\xc6Q\x93\xeb\xbbu\xc6\x1fg<@\x0e\xa6g\xf6\x1f\x83}\xe3\xe4\x0f\xc0\xbd\x91o\xfbe\xf4\xad;\xd7t\xd8x\x82j\x98^\xdeG2v\xb9\xb1\t\xe0-\x05\xf9\xe9\x91\xe3\x97\xc7\x1f\x94\x87;t+Y\xd0\x19Z#=\xb1\xbe\x0e\xae_\xa1Yk&#Īg\f\u0085\xbc\xaam\x9e\x04\xd4]\xa8\xaf\xe5p0\x9f\xb0\xf2X\x02\xc4\a\xba\"T\xc9R\xe4.\xf6P\a\xa5ެ\xa1\xe3Z\x8a\x94)\xfb路4Uմ\xc54\x01&ȋ\xf1\x8b\xaf>7\xc1\x8f+Y\x13\xfc\x89\x85\x9f\x1b|\xeb\x83b!\xb4l\uf2497\xde\xc5ZwXO*;\xe9b@\b\xe4A1\xe3\xa9\xf9\x81i '\xb1^\xf30\xa4jֲ\x0e\xfc{\xd8\xe9\xfbj\xa1\xe3\x1eVU\xd8\x1d\xf1b\u007f\b\x01\xd0\x1a\x15\xbe\x8d\xfbnٿ3ʹ\x97\xbe\x11\xb0\xb6\xf7\xf4+4+\xb0\xc4\xe7HŮ\xe1X\xfbV\xcdR\xe89+\x9eJ\x8e\xa1\x98\xb2-\xa7\x01\xfbU\xf3^\a\xde\xd1ߕ8#\xd7\xd2\xd8\xff\xb9|d\xfa\x89\v9v/_K\xd0\xd7\xd2\xe0ӽ\x91㦶7j\xdc\xe3H\xd2\xc2\xf1H\xbc\xa4\x84ߨ\x96y\xf5\xf4\xfd\xf7\n\xc5L\x93+a\x19\x95\xc7AuYQ{\xf0\xcd;\x86\xc8\xd5vk\xa2\xee\xdb-\xf8\x0e\xad\xf6\x1bM\xcc5?\xb5\x1b\xe5\xadi\xb8)8\x8f\xb6\xfb\v&h\x17\x9cf\x90\xfb>\x13v㍢\x06flw\xfb\x81\x05\xa8\x19&\x1ad\xf3]\xab\xda#t\xb8\xa7\xe2}\b\x15y;\xab\x19Uh\u007f\x0e\x15\xda\xcb\x10\x14\x9f[\xb0\x11:\x89Q~\xf3$G{\x12c\x9b\xb2\xc8}\xda\vsZX\xca\xff\x1f˞\x91\x88\xfe\x97\x14\x94)=&\x17\xfe\x86ʖ\xef6\xdf\xf0\xbaN\x13\xb8\x85\xcb4\xb1\xbb\xb0\xa4\x1c\\\xa1b*\b\xec,\xbf\"\xa7\x1b\xd2\xf2\x8c<̥F\xc9P\a\x91\x8e\xeeaut\xd6:![ ڇ\xaf\xc4\xd1Y\x15/k\x1d\xcaJNa\b\xe3\b\xffv4\xde\x10\xb0[`?!vwRɎ?VZ\xf7\x1b\x97ڴ\xb9\xf3\xfb\xd2\xc7N\xdaب\xc0\xd8\xfcf\x8b8\x9a\xcaqˬ\xe8\xfa$U30]&\x88ט1\x95aL.\xc4j\x03.^\x8c\xebT\xb9\xbd\x11W\xd1Y\xd1j_\x84--1C\xa2\x01\xca'.\xe9nC\xd8>\xb8\xb9k;6\x05\x05\xa8Zµ\xcc\xe1F\xaa\xae\xfc\x8ev\xbcf\xfd\xf9\x0e\x8b\xb6\x81\x14\xc9s̳\xc7G;拺\xb1\u05cb\x0fi|\xfa\xef\u07fc\u007fj=\xef\xaa\aw/\x84bo\r\xb7_\x1d\xeb\xb0ﻻ6\x82\x16z.\r9\t\x97\xda3.\xcb\xdc\xdf\xecW\x1d\xf1\x9e\x1e\xab\xd4\xd9\x1c\xf2\x92Cw\xd3\xc1\x8d2\x95\xe1Ѡ\xfb\x95\x82\xfd\xb3l\xb7\xe8\r\x1e*\xfft\xd7A\xa8qR\x99\xd6\r\x8f\xaaeG\u007f\xc1\xfd\f_\xf2V\xa4\x87\xbc%\x15\xbe\tҝ\x05\xa9\r\xdeS\x12\xa6Qt-\x98\x9d\x99o\xcf\xe1\x1f\xef\xbcf\x17ְ\xed8t\xb0\x8fn\xe1:\xf2_݈\x88o9V.\x97\xbe\r\xa3\x8b\xe6n]\xce}F\vS\xaa\xd0\xfe\xbaT\xd8\x0f\xacnaB\x03\xe6<\x8aZ`\xb7\x1b\x06\xde/Ȥ\xb8c\vІ.\x8a'(\xe4\xd5\xe6\x1bv\x03\xa4\xcauU\xe9\xa4\xe9\"\xa8\x9bnuYŴn\xf5\x96\x8f\x1b\xb0\x1d\x18\xd4\xc9,h\xc8\t,A\x10\u007f\xc9\a#\xef\xce\x01\xd1\x01\xf4\x0e\x8d\x13\xb5\xc4X@\x80\x83\xf9\xbeS\xa9\bvի\xa6\xbeI\x11\xe1\xeaxN\r\x8c:o\x12\xeeu\x12;\xa5\x8e\xeb\x91\xfb\x04\x82/]#]\x94@\x19\xe6\x89\xd9\xed\xe5ܽ\x1dn\x1e\xf8\xab~\x0f\xa0\x80\xcc@X\x14wr\x1c\xafʺVO\x16\xb1\xfe\x04W\x0e\x9c;\xd7\x1d\xab\xa4<\xb4\xf0\xc5\n8A\xaalu\x99\xe1#\x9d\xb7\xacv]\xa0\xf77>\xde\x01\xd5]yK-D|\xdd|\xd6\xdb*\x0e\a\xb8\xf4\x8c\xba>y\xaec)S\xd0I\xfa~J\x12\xbf\x1cq\xc6\t)\xe6T?\xc5.o\xec3UK\xaeơ\xac8\xe5\xbb-s\x02Q.6\x81\x8f\xc85\xccr\v\x1e;\xd8f\x9b\xfe\xeb\x85\xed\x00I,\x99?\xccY6wZ\x80\xa5M\x84Cr\t\x1a9\x87\xd5TW\xdb\x16I\x9e\xda{\xff\x91]\xbc\xa3\x1eO\x9c\xa9ux]\xfc\xa4\x1e{\xf0\xdbz<\xc1y78\x8b\xff\xbdSt\xd6㗉\xd8 J\x12\x88\xf6j\xe3\xd5\xc3\x12-ZUVۿ\x9a\x12X\x14fuF\x98\t\xbf>\x05\x91r\xde\xf8\xfeg\xbc1\xf1\x14\u007f\xb5\xfe\xe6A)~\xe7\xae<\x05\xd1\xeeJ\xf5\xf9\xcfpSPX\xdczY\xb1\xf7\x86|\xd7|댰i\xb5!\xf9\x19\x992n@\xad\xedL\xaf\xf3r\bd\xec#\xef\xecXP\x93\xcd/\x1f\xadf\xa3k\x0fԞxY\u007f\xd9\xe9\xc4\xc1Hh\v\xe6'\xe0\x12\xb4_\x99\x82\x85\xb3\x8b\xef\x10\x9b\xf5/hP\\\\\xbf\x86|\x17z\xc8~\x94\xb7\xb1\x90\x8b\xb5\xc96?\xed\x15\xfd}\x97\xe1U\x9f\xcahr\x1e\x8f3B\xc9=\xac\x9c\xc6B\x05\xb1\x9bC\r껝\xe6\xd3&r\xd0\xf5\xe2\xd4cX!\x18\xefKy\xf2\xed}I\xc1\x8d{\xe8\xd0\xf7\xbbF\v\x81vN\xde\xc2u\x98\xb4? \"\xd0\xf2\xde\x1fy\x04\xfdb\x81\x17=\xbd8\xb2?#\t#\xe0>a\x99ն5\xfc\x87\xb8\xb1\xc7\xdam\x91=\x05sV\xec\xb9Pt\x1fj\xc0\xd3\x12h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|u&\x87\xd6\xe9\xa0Q\x10C\xeah\x9d\xb15f\xf2<\x97\x99>7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcd_\x14I\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x17\xb0B\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\x8e\xc2\x19\xde\xe5?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]\xaf[\xb1\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\xd5h\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Q^\xe7\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6պ\x89\x19-J\x0f5qz\vM_>\xc7\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15j-}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833,\xcaDE^\x17f:q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻*Ծ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`}G\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ2\xe4Q\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd.O\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9cok\x9e\xb3}\xb4I\xef\ni\xae(9'\x05\x82\x1a\x93\xb7\x02b\xc32\x17\xfc\x81\xae\xf4\x19\xb9\x86%\xa83r5\xbd\x96\xe6ƙ\x9b\xf5\xfd\x94\xb8\xc3+=P¦\xe4\xa5k\tG\f\x9d\xa1뢪_\x17G\x14\xaa51'L\x1e\x98\xeek\xa7G\v̍\x03\xf8\x1b\xfc\xaa\x15\x9d\xee\xdf\xcfN>\x9cM![e<\x9dg]d\x98\xecZ\x97_\xaf\xcfm\x9c\xcbh\xa5\r,B\xd90t\xa00,3YH\xa1\xc1\x15\xd4K\xf1pU+t\x0e3\xdds\x8fS\x95\xbcBjsk\xa8ڳL[=ڧ\xf4&\x80\xb1\xe4\x9fQ\xce!'l\xb1\x80\x9cQ\x03<\xdeO\x1e*\x806\xcb=\xba\x0e\x9f\xae\x98d\x92\xde0\xa7\"砰^\xa1\xf7\x01\xb6\xe0\x1bP\v&hl\xc1\x10R\xa5w\xa1\xcb\x12rB\xb3L\xaa\xdcׂ\v\x95\xbd\xa8\x8a\xd7\xf8+\x8e\x87\xbaRC\xf2\xac\xe7\xebEC\x9ep\x99\xddkR\n\xc3x]\x1e2Ԇ\xf4\xbd)\xa3\xa1&\xb1\x98\xea?Gՙ\x18a?\xb2\xf3\xdf\xd4\u007f\xc2\x1fb\x95\xdf>\x96\xcf~\xf5|7\xc7Z\rJ@\xd2\xc0dJ\x19/\xb6\xc2\xc0\xbc`i\xd5\x17KTu}\xd5JФ\x19\x19X\x80\xb8\xddU\x87\"\xdbĺh\xf4>\xb2\n\x89\x1b}s\x14\x12j\x015ǎ\xb2\xc5ɱ\xc6Pؖ3\x01\xcd\xfa\xc5\fk\xa2\xa6\x870\x9b'\xd8\xf1#o\xa1\xa6\a\x81\x99\xc2\x1e#\xabFmK7\xf7>\xc9\xfcJJCN\x8eϏO7\x82Z\xc7\xe9P\xa7\x8c\x83\x93\xae\xae\xc8RVmV2H\xcd\x16\x05_\xe1\xfe\x1c\xe7\xd8\xd1\xc9_\x87UeZ\x8c\x98 S\xb3\xbb\x1c\nB\x9d\x11-\x89Q4t\x19H\x9f\xab\x85f\x81\x1bUz]\xe5\xe4\xf8\xe7\xe33\x02&K\xcd\a&\xe4A\x8ac\x83d4&w\x92\x94\xba\x9ex2̕,\x89\x00W\a\x00\x1e\v\xce2f\xf8\n\xc5|2LY\x1aW|\x11\x1b$b\xa1\xad\xcbGf\xfc=\x9dt\xb0S\xf2\x15\x9ev\xa7*\x10j\x8d\xa1%\x9cρr3O\xbf\xdfg\xe9RH1\xfaoP\x12\xcbx\t\x0f1ջ\x93\x10;k\x8e\x03d\x9c\xa4\xb8\x11\xd6\xdfNLb\xb0\n\xc27\x10\xadr\x92\x8d\x8e\xa4ww7߀i\x8b\xb0$t\xd8\x19\x85\xfc|ti\x83\x9aJ\xd5\xd1e\xf8\xe9\xd1W\xfeͥN\xc2\f\xd9\xecת\x8d\xeb>\xe1\x8c\x14\x91\xe6sv\xc3\xc8vZ\xb2\xcfh$W7\xe9\x89X\u007f\x97\xa5\xc5քN\xf8\xaa\xaa\"\xab\xc1\x90#;\xf5\xf4\xb4g&p?\xff\n4Ǻ\xbdB\x1b\xa0\x89*\xd2\x01\x8eZc.\x87Qj\\\xdfݹ\x03\xd9cG\x9b%\xb0<\xed\x8f\xf1L\xa5\xb3IW\xe1EA\xe1د\x9f\xe3Gb\x92\x1b\xbc\xc2\xed\x82\xff}\xd2+7\x91\x86\xf6\xc7n\x89\xbe\xb6sb\xf1\x8e0\x98\xc0i\xe2\xa1\xe81\xbb\xfeY¤w\xb6*銀9\\\xf5\x82\xe9\xef^Ƨ\xa5\xad\x8f\x83\xdc/I.\xe1\xd5\x1cω&\x9c\xde\xc7\xc7S\xbfTK\x92\x96\x88\xd8~\xbd\x1f&z\xdeP \xbd\xf5-\xbc̓|\xddx\U000f2c51\x84fYZ\xa1(7|\xf7rdX\x1a\xd426\xc1\xb1\x1e\xbdI\xac\x90\xf1\xfe\xcb0z]x;\xccu\xb7\x83\\v먗\xa8\x88(\x17\x93\x1e\x9c\xa4*\x80\xa1LM0~\xe3{8S\xaab\x9b\xd78\xbd\x10\xc2\xed\xa3\xef\xa1\nC\xc5\f\xc8\v;\xd3?\xfe\xe1\x0f\xbf\xff\xc3\x18ѐ\f5\x04\x96\xa9 W\x17\xd7\x17?ݾ\u007f\x85E\xdcR\xa9\xfcYn\xb6aنd\xf9ӎ\xcc#(\x8b\xbdRc\xa1\xb3>;lm\r\xef\xffv\xcee\x9d\x1agk\x0e#\x91\xdd|$>\xd3G\x88\x8d\xf0\x10}h;\xdbdŭ\xcc\xee\x0f`i\x1f߽\xbaq\xa0jc;i\x17\xa8\b.f&\x96\x92/]\xbf\xc0\xbbW7\x88\xa0\xb4\x9d\xb5oc|\x00]}+;\xc7p\x13ޥ\xe6$Ae\x8b\xc2w̤D\x01\xe5L\x1b\x96ᷪ0E\xa2}/\xefS\xb2x>\x19\xbf\xc2\xf1ې\x0e\x84.\x86\xe4Ӽ\xe6\x9ah\xb9\x18\xfa\xb0\x88\x86k\"\xf5Jנ\x91\x1c\\#q\xa2^\xaa~z\xfc\xa0\x91|\xda\x1a\xc9\xe7&#\x93_-\x14\xdc\x1aY\xf4̚p@\x0e\x943\x11:\xd1mKj y\u0096\xba\xde\xd1\x177W\x95w\\\xb6\x12\x110y%\x1a\xaa.\xb3y\x88\xcd\b\xd0\xfa\x1c\xd3#\xca\xc2y\xbeBC\xc9\xf8\x88U\xa1\x00\xbb\xf0IqVU$@t\x80p?\x82\xc9\xe2O\v\xfad|\ue20f'\x86\xedꛆ\x91)\xaa瀝*\xe0\x91\x19\x1d\xba]S-\x85\v\xe1\xfa\xedc2>\x80\xc94)\xa8\xd6.pg\xeaE\xb8\x8f\xdc\xc8\xfc8!zۘ\x10\x99)\x9a\x01)@1\x99\x13\xac\xfa\x97ˇ\xf8yN`Ƅ\x0e\xf4k'\x1a\x0e\x86Օ )\"\\w\x9e}\xd7\xea\"\x85\xed\xcaK\x93\xc9\x04>\xec_\xf7X\\O \x8a\xbe:\x89Ӵǧ\xa4\x9c\xaf\xea\x83\x1anz\x9a\xc3o\xd2f&Q*\x12\xeau\xafg\x12\xc5G\x03[\x99G\xf6(\xd4YI}ȿE\x9dL\xdbS\x95\xcd{\xb4\xf9\"\xf1\xe9\xcbCjӓcHm\xda{\f\xa9MCjӐ\xda4\xa46\r\xa9M{\x81\x18R\x9b\xaa\x19\r\xa9M;Ɛ\xda4\xa46==\x86Ԧ!\xb5\xa9\x1eCj\xd3^cHm\xdac\f\xa9MCj\xd3\xd61\x04\x12\x87Ԧ_c qHm\x8a{}Hm\x8a\x18CjӐ\xda\x14Ɛ\xda\x149\x06\x8ddHm\xfa5j$\x9f\x9b\x8c\xecU],\xf2\xb5\x90\xc7s\xa3\xe4\xa4G\x99\xb1\x1b\x8cճ̧\x01\xc9irm\x1d7\x9d1y\xd5J\xcf\xf0M\xf8]\x95\x96(\x88>ѧNO\xea[\xaf'\xba&S(\n\xa6\xcf\v\xe9\xfe_\x9dS\xd0H&p\xfe\xb5\x18\xe1\x90*|S\xb2\b\x9e\xca H\xe2u\xbb\xb3\a0\x13 \x1a\xe6!3\a\xfah7=2\x06vd\v\x04\xb0I̵;S`-⟜\n\xe2\xb3\x046\xa3\xfd}\x12.0\x15n#ҟ\b\xb1\xca\x0e\xd8\x16\xe5OS\xc9\xf5\xe1#\xfc\xcf\x10\xdd?|d\u007fGT\x9f\xacd\x99\x04sKD\xdfG\xe6\x13i\xb33\x9a\x1f\xa2\xf2i0\xbb#\xf9\xad\x88|*1\xf5\x8a\xe2\xf7\bN\xf5T\xae\xd3=\xc9ɚ\x92O6\xbe\x9b+\xd0sɣym\x8bϾa\x82-ʅe\x13ڲG\xb6\xac\xb2\x99\xe3i$\xe499\xadÅ\xe1,`\x96\x036\xb1\xa4\x8c\xa7\x94\xaa\xc3\xd2zs\x8a\xfe\t]f\x19@n\xe5\xe4\xeb:\x04\x1e\r\xf3\xf7\xe3j宫\x06\xd3\xe4E,幆\x8ah\xdf\xfd\xfewI\xbb\x9fb\x19&&l<\x9d\xac\x81\x90\xa31\xd9?Q\xa3\x8f\xba\x91\xeaHy\x9e\xe4\x8c\x1d\x89\x19\xe4\uf262aGR\x06aib\xf6@\t\x19\xbd8g\xcfD\x8c\x1dI\x18\x1eG\x89\n\xc8f\x02\xc6z\"E\x1a\xcaӓ/zȶ\xe7J\xba؞p\x91J\x92\xa4w\xb2E\xffD\x8b\x03v;\xac3\azw\xc7\xef\xe5\xa2;\x80\xe7\xb0gR\xc5s\xa1\xe5\x10)\x04\x1f\xb1\xfbl\xf2\xae\xf6I\x9e\xe8\x998\xd1'i\"5abG\xb2D\x1fOs\xcfD\x89^\xe4\x93\x1a\x8eH\x0eE\xf4\x0fC\xf4\x0eA\xecH\x88\xe8\xd3\x13\xb63\xf4\x90ڿ0\x8cv\xd8a-|\x90\xa8\x166C\x0e\a\r\x1d\x1cI\b=(:\x95\xf9'\x05U\x92\x996\x13\xcc0\xca_\x03\xa7\xab[Ȥȣ5\xa3\xb5\x86H\xd5y\xd5\x0e\x9c\xb3\xcc\xe33!Z\xb71\xe7\xd4w΄<\\\xa8\rѐx֊\xea#\xa1\x18\xa7\xb0\xab7\xedۓ\x1f7nA>\x9a\xcb\xc0])=\x04\x11\xfcU>\x1095 \xc8\t\x13\x81\x0e\xe2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfe\xf5\xc5W\xf1\x9c\xcbM\xe6\xf3u\xec\xa0kK\xeb\xe7\xf3\xeb\xf9\x0f\x1cޱ\xe7\x01O\xcbx\x1f}˹\xe7\x1c\x84m\xfe\x1e\xbdyu\x1b\xbe\x178\xef\xc0M\xd0K\xed\xcb6$\xc0\xfcL\x89*9\xed\xecɔ3\x92\xd0ylW\xbaY\x9d:\x16\rvK\xaaY\x9d6\x16?\xd1mifI)c\x1f\xddù\x96&\x96n~nI\x11\xf3\xeaY\xa2\x12\x9f\x9c\x1e6\xd8a\x91cG\x1a\xd8`\x87}Bv\xd8\xe7aa4j\x9d|\xa3h\x067\aS3\x03\xbb\"y\xa9\xa8\x17\x19A\xc1K\xb27,\x93\x11\x00\xb9\xe3TU\xe1\x1a\xac\xb82-yB\U0006ac90\xa2]\xfd\xc9\xe5T4\x8b\xb8D\x03\xf5\xd9.\x1d\xab\xf6\x8aR\xca\t-\x94tj\x1fQ\xa5\x10V\xea\xfa\xb3d\x91bm%\x9d&!\x9b5{4\x9b\xd9\xed\xb2*\x16V\xc1a\t\xf2\xe5a\x0e\"h\x99~\xc2vvS\xa926\xe1+2\xa7<%\xfc\xf2\xc0̜Pr\xcf8\xf7\xd3\x1c\x93[0\xc4̙Nt\xa6r)f\xb8\x19\xd4M\x18\x1e\vȬڑq\xa0\xa2,\xd2\xd6o\x95Օ,UX\xbfo\x1b\x17f\x99\x92\xb4!\x18?\v[}\xacw\x1f\xd8\x04ĺ\x04\xc5RC\xa8\xd3\xf4\xc04\x9c\xf5\xc1lh3\xea\u0381[w\xa1\xe4\x92吓\xc9*\x8d\xfee\x8eZ똼Gx\x81\xef\v)F\x02f\xd4\xdaF\xf1'\xd5\tqw\xe6\xdd<]=\n\x91\xb3\x8c\x9a\x04\x1bKca\xbd\xba\x9c\x1eY2\x8aXhPn4\xd0\x13!\x89D\xa5\xb8\x14̬06:/\r\xc9\xe5\x838\x1dc\xeb\xd9\x14&E\xc9\x04\f\xf5\xf7Z]+A\x14X\x9a\x80\xa0\x13\x9e\xa2\x9c`&\xee]'\x81\x92)PS&t\xf7\x9bQ\x03\x9d\xfe\x00G\x0f\x87=\x0eL\xfb\b蔔BC\xf4\xfd\x99\x86}\xf8\xc7\u007f\xfdp\xf6![\x80,\xcd'\xe5 |\x98\xb3l\xde\xf47\xb0\x05h\"\xcb>\xd7\u058c$/\xfc\xb4\xba)\xe2\x99\xdbG\xfe⼊IZcl\x88\xbd#n\xb4^ͯJ\x9c\x8eZ7\xb5<\xec\xf5\xf5\xedO\xdf]\xfc\xe5\xf2\xbb1\xb9\xa4ټY\x86T\x10j\xe5F\x14L\x94+s\xba\x04BI)\xd8?K\xd7y\x9c\x9cT\xdf9\r9\xf8Qp\xd3\xf2\xf5\x93,E+(\xa2\x98@k\x83\xbec\x1a\x1b\xbd\"\x14\x9f\xce*5\x90\xa9\x92\x8b8\xa6Բ\x1eɥ\x05\xe3\xbcEhi\xceA\x01\x99\xb1e\xa4\x90\xb5P}sd\x9a\x87\xa4b<\xc2\xf6\xf4X-\x96Nd\x19i\x02́\b0\xf6tW\x11.)t\xab\xa6m\xa9A\xc7\xe5\x97OJL\xa5.\x14[P\xc5\xf8\xaa9I\xab\xbe^\xcb\xe0\x87[Ŋ\xda&\n_\xbf\xbd\xbc%\xd7o\xefH\xa1\xb0\xac\xa7\xcb\x19\x8e\xb6 \xed\xf6\x92\t\xd8\rr\x1b\x9e\x8fɅX\xb9\x0f9^\x1e\xa9ep\xa6\r\xa0\xa5\xe2]\t\xa1i\xfd\xd1Wc\xfc\xbf#\xbb\x83*6DT\xa5\x97g\x1b\x97l\x9c\xe7\x82M\"\xef\x91\xe2\xd2\x1b4\xd0\xf3\x8eMB\xaa\xd7ڥ\t\xbf\xae\x1b\x8bz\x05\x85\xeb\xcb\x1e+-iEҸ\x85\xc8\f\xed\xf9\xe3\xc9>\x9dt\ah\xd6\\R\xbc\u05ed\xd3\xe15\xad\x1cV\x8e^\x13\xfc=R4̪\xab\x9b@\x8eN\xa3FI\x90\x00\xd4ҡ\xd5IX\xee&\xe8\x12$\xce\xc8W\xe4O\xe4\x91\xfc)\x01\xe2\x1f\xff\xf0\x87\xdf\xff1ޟ\xd5G\x9f裌:o\xf7\xd5M\xcf}\xfe\u07b21\v\xc9\ue311d\u0092\xee\xb88\xcbހ\xb2b\xc2SL<.{xl\xed\x12>I\xb2wY\x18W\xd3Z\xf9rF\u007f\x02\xc4\xca\t\xbb\x85\xf0S|\xb1\xe4O\x9e\xf0\xed\x14\xff*\xb5\xb9\xf6\xec\x8c\xe9ƌ\x93(\xc2\x1fn\xb2\xa0&\x9b\xb7\xf9\xad5!\x92\x8e}]~\x99\xe4\x12cY\xee:М%\xe4\v\u007f\xbc\xa3\x9b\x96>ۢ\xd4M\x8a\xea\xc3J\xd7\xdc\xfa\xe8\xa7\xf0z\xb9+X\x9e\xc8\x13\n\x99{\x83\xc1.9o\b\xc9X\x8b\xc1\x8d-v\x83\x8fR\xa4\x15\u007f\xa9/\xe6[^\x98Q\xe1n\x12OA)\xbci\x96\x82\xd2\x15fL\xb2\f\x12Ȳ\a\x17,\x9442\x93<\x85\xb6Pk|I\xee^ŗQlg\x9e\xfa9\xa0\x8f\xd6E\xab\xdf$\x13\xe6\xdf^ߜ\xd9)\x9d\x11\xa9\xc8\xed\xab\xbb\x9b~W\x99\b9\xba{us\xf4\x01\xf7$-:5j\xebr\x91\xef\x06*\x88\xb3\xce\xfaT\x14\x88Mtn\x85\x00\xad\x053Z\xd0bt\x0f\xab(\x9d7\x1dKI8ڜ\xb4[\xfc\x82\xee\u007f\x13L\x01\xcd\xd9'TL\xc1s\xa9z^\xddU\x15\x16r\x19\xe94Bk/@\a\x91\x17\x92\t\xa3\xbbJ-D\x81\xdd4\x19?\x99\x94š\xd4B\xc7\x18J-l\x1dC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xd0=\x86R\vC\xa9\x85\xcf*yz(\xb5\xd0\x18C\xa9\x85\x841\x94Z\xd86\x86R\v{\x8d\xa1\xd4\xc2\xe6\x18J-t\x8e\xa1\xd4B\xc7\x18J-\xec;\x86R\v\xd5\xf8\xe5\\\xf1\x19J-|\xaaW|\x86R\v\xfb\x8c\xcf\xe3\"\xd4Pja(\xb5\x10\xf02\x94Z\x88\x1aC\xa9\x85\xb51\x94Z\xf8\\\x89j(\xb50\x94Z\x18J-t\u007f\xf7\xd7n\x87\r\xa5\x16>U;\xec\xf3\xb00\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb50\x94Z\x18J-\f\xa5\x16\x86R\v\xbf\"\xafb\x92֨@\xcbReqvp\x9b\xc8^\xc9EQ\x1a \xef\x02\xa8JY\x8eZ8\xca\x12\xa6\x9b7\xfa?l'\xc2L\x8a)\x9byE\xef|A\x05\x9d\xc1\xa8\xc2Ϩ\xbe\u007fw\xfe!r\xe39[\xb0\xb8\"\vv\xd4\x15\vnzx8\x12\r\xea\xbe\xe6tOc\xba\xa0ƀ\x12/\xc9\u007f\x9c\xfc\xe3\xb7?\x8fN\xff|r\xf2\xc3W\xa3\u007f\xff\xf1\xb7'\xff\x18\xe3\u007f\xfc\xcb\xe9\x9fO\u007f\x0e\xff\xf8\xed\xe9\xe9\xc9\xc9\x0f߾\xf9\xe6\xee\xe6\xf2Gv\xfa\xf3\x0f\xa2\\ܻ\u007f\xfd|\xf2\x03\\\xfe\xb8'\x90\xd3\xd3?\u007f\x19=\xd5\x03\x1b\xa7\xed\xf3\xf8\x1dRN\x9dR\x84|{A\x1f-\x83\x8d'\x85\x85,\x85q\xb7l\xdc1\xafN\x84KÊ=\x94\xe4S9\x98\xa4\x8f\xa1\xed\xf3ц\xf3\x191\x86\xf3\xe9\xce\xe7;O;\xed\x13\x1a=DžW\x99v\x9c\xd0h\x98Ap\xa3\xa1[͓i\"\x17\xccXs:\xe5\x9aq\xa3\x90\n^Ii\xba\xa8\x1d\xaf\x8a\xd7\xf2\xa6\xee6\x05\xd3\xcd\v\x1a\x8dK\xe12ؾ)j)\x15u\x9c\x02y\xce(\x87)\x13\x90;\xf5\xf4\xd7\xc7\xef\x92^Ӑ\x95\x8a\x99\xd5+)\f\xe5Cú\x19\xe7\x85q\xe5eH\n\xfa+\xa09֔)\xa8\x99\xbb<\xd5\x05\xd5\xf7\x90\xbb\x1f\x12u\xec*$kg\\-\xfdnU@r<\x15uk\x97\xfd\x8bq\xdexol\x8f\xf2^4\u007f+\xf8Ꝕ\xe6몌I/B\xfe\xde[K\xed8P4R\xe6X\xb6\xdb\xceo\x84\x9b\x88e[\x9a\x95V<\xf5\xa5X\t\x1f\x98A\xa8R\\\xe8o\x94,\xa3U\x81\re\xfd\x9b\xab\xd7\xc8/K\x9f!#\x8cZai\xaa\x14&\xd1>s\x95=\xf67\x9fӔ\x94mS\xb1\x87\x10\xae'o\xe8\x8aP\xae\xa57\x1c\x13\x82\xc1]\x1e\x12\xe2]5)7\xa3'\xd2\xcc\xd7}:\xc8\x1e6\xbf\x13_\xc0\xa8N\xb0\xa9<\x99v\t}\\H\b\x96ރ&\x85\x82\fr\x10Y4\xf5~\x9c4\b\xa4\xfck),{\xe9E\xfbW!\xffǹ\x8c\xfbY\xf5\x98\xa9\xe4mz\x8a\xf9J\xc8\\J\r\xca%\x87\xa9\x12\xd26\xfe\xdbr\x02\x1c\x8cs\x94`\x91[j\x9c\xe7\x8f-\xe8,\xfe4QS\x89B#\t\b]*\xf0NsCr\x99`\x06\xf8:Rv\xe9\u007f\xbbzM\xbe\"'v\xed\xa7H\xfeS\xcaxJ\xd5\x17\xbc\xfd\xb1\xc6M\xd84LѢ4^'\x10h\xec+Ǫψ\x90D\x97\xd9<\xe04\xc5;\x14\x9cW\xfe\x86\x14^a\x1bX\xd3'\xc0\x9az\nֿiP\xbd\xe5\xea\xdf>\x80\\\xed\xe3\x06\xb3\xbc\xa9\xbdk\xc8P\xc8\x02\fͩ\xa1)\xc1\xb7R4\xaa#\xae\x1d\x85\x14\xda\xdd}\x14\x90\xb4\xa3a\xfeʎ\xc2Ǒ\xd2\x1a\xbec\xa2|t\xb7\x03\xfa;\x94o/\x11\x1c\xf1\xa1\xa4\x14\x892\x01B\x8b\x823W\xbeo\xadE\xccU\x8bt\xd3\xf6~\xd3\xd4D\xf1@9\x97V\xcdH\b\x8f+*r\xb9\xd8X\xbc5D\x81&Xō\x05w\x1c\xcem\x87-^vׇ\xf3\xd7v\xd8\xfa\xb8\xee9,!\xa1J\xf9z\x13%\v\xc5\x1a\xa4\x81j\x10l\x92\xf7\x93\xd3\tp\xa7\x1a\xba\x93\xa37ON\xb2W4ѩ\xaa$\xef_\xf2\xe2\x9d\xe4\xe0r\xe3\x03\x92,\xd8_\f\x8e\xf0\xe5\xbe8B\xefS\vG\xc9^\xf4O\x11Ge\x82\x86G\xd6qd\xd5\xc46\x8e,\xd8_\b\x8e\x92C\x10\x1a\xb2L.\x8a\x1b%\xa7,\xfe\xb0n\x88~\x0f\xaeNΉ\x17\xfd\xa5\x86\xae,r\xd4#\x11x\xbcF\xee'CU\xe3\xd2\x135N\xe6\xf9[\\\xd1@\xff\xbf\x86\n\x81\\\xfblM\xaf\xf0_\x8d\x9fm3_\xa8\x90y\x00\xf4A\xa5\x9b\xcc(ǎDitA\xd6ic\x1d`\x8f\xfb\\\xc45\xb6\xf3pBN\x1f\xb6d\xc1_\x12<\x03$4\xf7\x9394jǻ\vxw\uef8c\x85\x9d\x048\\\x8b\xb3zJH\xbe\xcaC\xec\xca~1m\xbaҗ\xca~S\xf5T\xb2\b\a\x91\xa7V\x82*\xa8\x99\x9f\x11\x05\x1c/\xee\x05\x86v\xef\x1cZ\xc7i\xfb\xd4Xp\xe0\fa\xe3P\xcffR\xa4]b\xc7UcX h\xc4S䁖\xc1\x1f}\x17\x88-\xa1Oʧ ͏\xdcJ*\x8fgZT͎{&r\u007fo\xac\x85|\xef\nK;{\xce.\xc3[\x9f,o2ח\xe4\x1fig\xaf\xda02\xda<\xdaI\x10\x9b\xec\xa0\xe3h'\xc1t\xec\xe0\x9d3\x17CM\xa5QOW4q^\xacf\xb0\xb3B@B.k\x18\x15\xf7\xfa\x9b\xc03hY\xe4\x88\bً\x93՜1\xd0\xc0ч=_\xe9\xe5\x1b\x12\xd4\xe1d\x95ꁉ\\>\xe8CyS\xbew\xe0\x82\xe9\x9cYvg\x98\x98%\xa4`\xd4\x1e\x15\xcay+C\xef\x10.\x95\xc0\t\xaa>\xa9\x9b\xae\x83d\x9f\xbd#\xe6\xb6/\xb1\xb7\xff`\x8b{\xa3vW\xc4;Sv\xb87\x9c\xbb\"\x1a\xe4\xc7qo\xcc\x16\x9a\xbeR\xf6\xbb\x86Q~[\xc4w\xf8!\xeb\xb4\xfc͛ۋ6\xc84Ύy\xbdʩ\xc7\x16&\xa1\xf9\x82iͤ \x0f0\x99Ky\x9f\x04\xf7$\xa4\xceϘ\x99\x97\x93q&\x17\x8d,\xfa\x91f3}\xeeO\xf6\xc8b'\xad\xc9\t\x13<\xdczp\x1eBat\x88\x18\xd8ŤiY\x15V\x91\x00}\xa7B\x9fຉ\xf6\xeb\xd4\"UxcძT\x9b\xa4x\x9dXP\xfc\trLƋ\xaf.Ө\xf6\xe4\b\xb3ޗ4qk\xf7҅~>|\x1b\x01g\xaae\xa0\xfb\xb7\x11\xf8k\r\x8b\xe4\xe0\x8aC$\xda}l\xdaj\xe8]+$.\xa2\x9dhK\x1ec\xed6?\xc5\xe3\xa6\xd3!\xa9\x8e\a\tG\x05\xbdM\xbc\x98\xd3\x11:\bP\xdeX\x81\x96\x041\x18;s)\xa4r\xc7۪\xf4R$\xf4\xfcv\x03\xfdW.\xdf\fi\xd6+\x1a\x8d\xedz\x95\x9e\xeei\x87O\x87\xc3\xf46\xac\rd\xd5\x16\xe7\xd8I/S\xff\xc0\xcc\x1c\xdb7͡\xf5\x81t\xcc*И\xb0$\b(%\x95\xbf7\x12\x12\rR\xeb*;\xfd\a/\xb7X\xa6@\xed\xbf\x8eu\x9f\fZ\xd2j\x1fk?\xa0-ǁ\xe9\x1424\xd9\x1b;\x97\x04܅hN\xea~c\xfej\xb8eB\xf6\x9b\x89\xc7k\xc1\x1e-\x06\x9al\xa0'\x16B_\xacn\x90\xa7cB\xae\xd2\f\xd1p\xb1\xfb\xccr\x9a&t\u007f\xb3(\x95\x14D\xab35n\xa2\x0f\xe7\xa5I\x06\xc0\xac^;\xa3tɐ\x92oA\x9a9\x17\a\x11Ø{\xe1\x81\xd9#\xe8\x99P\xaaOl3\u007fc#\x1f#\xcd9\xb6\x9e\xc3\x11\xfcc\xc91\x84\x1d\xb9\x1c\x84Ňq\x89˙:h>\aْӑ\x9e\xddD\x9e;É<_\xb4\x99\x1c \xe2L>J\x94'\xedⷫ\xe8ܳ\xcd\xefm\x03JãiU\x8f\xa8\xb5{q\xea\xaa\xdaWU\xb1\xf9*T\xe3g\xff\x1d\x9b3\xdfn?/\xa4+6\xd0,u\xef\xfb\x9aƩ)\xa50\x8c\x87\xe0բ\xe0\xd6xl\xcd8:\x1b\x12a5\xfa\r\x9fUȨ\xdb\x1b\xfbB\xffq\xe7\xe5\xbfP\fU-\x8dC=\xef\x9b\xeaS.\xfa\x11\xa9\x01\xfb\xf6\xf3X\xb2\xcd\xc8\x10o#9\x9bN!\xdcp\x8e\x14{\x05Uta\r\aM|\xea\xef\x04f\xcc]3\xadT\xab\xc8\bEU$\xec̩{̐\x05\x9b͝\x97\x86P,E\x19_n\xd2H\xc2%\xcd\trq\xa9\xc8\x03U\vk\xb1\xd0l\x8e\xf5\x1b\xa9 y\x19}\xf0\xb1\x93\xdcj\xa4\r5@d\x01\xae\xa4\x84\xdb\x1b\x8boW]+\x8eL\x87\xe6\xd3C\xf3\xe9\xbd\xc7\xd0|zh>=4\x9f\x8e\x1dC\xf3\xe9\xa1\xf9\xf4\xdech>=4\x9f\x1e\x9aO\xbb14\x9fN\x18C\xf3\xe9mch>\xbd\xd7\x18\x9aOo\x8e\xa1\xf9t\xe7\x18\x9aOw\x8c\xa1\xf9\xf4\xbech>]\x8d_Nӳ\xa1\xf9\xf4\xa7\xda\xf4lh>\xbd\xcf\xf8\x0f\vch>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>\xfd+\xf2*&\xde\x14\xc9YT˶=\xba\x05\xa4\xd4>\t\xb5;-#+\xa7SP(|qvA9J\xbb\x04\x16:CU\xa2\xdbg\xc4b\xa3@\x054w5/\xe2\xfc\x82\x9d\xd3\nEH\xb1}\x99\xbb\x97\x1a\x19\x1e \x97o\xbf\xae\x9d\xae)\xad\x0e\xd2n\a\xe2zފ,\xfd\xaePM\b\x1d\xd5Y\xe3p\xeb\x12\xf03.\xb5\xbf'\x8b\xc8\xce\xe6T\b\xe0^\xe9fq\x98\x9dSM&\x00\x82\xc8\x02\x84S[(\xd1L\xcc8\x10j\f\xcd\xe6c\xbb\x828\xef\x99'\x02ߵ\xae\x9e\xa96\n\xe8\xc2\x11\x83\x82El\x9fA;EB3%\xb5&\x8b\x92\x1bVT\x93$\x1a\xb0TFd\xd2\xd2մ\xde`L\x12\xaf/\xa0\x9eU\xab\x88\x9e\xa3+\x83\xd68\xf3\x86*s\x86\xed`\x17\x85Y\xb9\xbbTq\x82\x0f\xbbv*mH\xc6\x19\b\xe3W\xed\xea3\xe2<\xcfHln<^\xdfu\xbb\xa0=jE\x8e^\x91\xc2hw\xd3'm\xa2~\x8a9\xd3\xde\xfb\xa6\xcf\b5APF\x13}\xa0%$\xfb\xa0\xc0\xb9Y\xfb\x9f\x12\xa7Y\x97\xf4\xd7\xf5U\xb3\x9a\x19N9\x8dS\f\x03S:k\xd5r\xa8\xedCLrG\xb6\x1a\x05\x16\xcb\x0e9,\xe0\xc1\x11\xb0\xb4\xfc\x032`K\xf4\aY\xce\x18\x05q\x9d\x8b>;\x13m\xe8\xaeo@k:\x83\x9b\xc8\x14\x9bm\x0eb̲\xa9\x89+\xd2\xe0\xc2jdF6t\xb8\xfa\x02J\xdb\x02\x8d\x02\xbbpk\xacl\xce\aŌ\x01$b\xec\\\x85\x99\x87\x91y\xe0\x1b\x93k^\x8fy\x13>\xe8>\x14K\xb5V\x9f\x12\xb9\xbb\xd41\x012Q\f\xa6d\xca\x04\xe5\xfe\x1eF܅#\xecgA\xb5%M\xaa5(\xf4\xb9x\xb7S\xc0M\x1c\xc1~\xef\x11iT)2\xda\xe8r\x89\xd5\xefؔ\xcc\xf0\xaeG\xa411\xa7\x82\xfc\xebW\xff\xfeG2YY-\x18\x8dc#\r\xe5\xd5\x06r\x10\xb3\xc8\xda\xfe^<\xb5\xeb\x90U\x94\xc0قź\x85\xac1\xf0\xbb\xfbI;\xd0x\x9e\xc3\xf2\xbcA\x9f#.gq8}\x15\xeeWVw&c\x14\xf9$\xd7~\a\x1b\x90\x9ce\xabdF\x10\x9a琹|p\xae\xbc\x9e'\xb6\xbe\xe2XȢ\xe4.%\xe3\xebPY2\nd\xa9a\xb3\x1aV'\x1f\x8c\xa5\x860\xb5\xf5\x0e\xe1\xeeʔ_J\x9c\xd2\xe2\v\xcf\xf9\xd0x\xd53\a\xfd\xc4_S\xce'4\xbb\xbf\x93\xdfə~+.\x95\x8al\xb3\x8f\xd4\x1f\xf0\xc1\xa9\xd5b楸ǖ\xb7u\xada\x19'mei\x8a҄K\xdeM\xf7n\xd8\xcc\xe8z\x90\x95\x82\x16<\xc3\xf5\xec\xe0ў[t\xcfƱ\x03_|\xc7q\x17.gռu`\x06\xb17\x82~\xf7տ\xfe\x9bcYD*\xf2o_\xe1\x95Q}\xe6\x84\x18\xea\x06V\x91]P\xcec\x83YM\x06c\x89~\xdc\xc1$\x9e\x9dG\x98tv\xf0,&\xf7\xdd\xdd\xdf\xd1\xdefF\x03\x9f\x9e\xb9\xda#\xc1\x83\x18\x05\xf4\x18\x95\xb8c/e\xb1\xc8\xcdG0h\x97\x92\x97\vx\rK\x96\xc5E\xf8[\xa8nA\t\x91 δ!2\xae\nĄ\xcb\xec\x9e\xe4\x1eP\xe3n\xc6z\x1f\xeb\x18\xcc$\xdcBٺ\xba\xfa\xfe\t\x89mF\xb4\xa0EQ\xd5rP\xf4\xa1\xb5X\xe4%\xd1\x17Phj\xa0:=\xab\xc3M7Va\x0f\xef6\xb0Z\x03\n\x04S\xc4J?7\xfc\xcd덆T\xa1\x83^R\xae\x81\xdf\x13\xa7\xa7ٝC\xce\x1c\x1f^\xef\x91\xf4\x90v\xa7\xa7\x85cQ\xe5\n,\xa8\xf16Mb\xfe\fRm\x01J3m\x15\x98\xf7x&^q\xca\x16\xe97\xfcS\x1a\x12\xf4h\x01\x9b\x92\x970j\xd0i\xe4\x8bш\xeeU\xf7(\xeen\x8bci\xd8\xd27\x9d\xeb\xdf\xc8\xdc\x03BV\xed\xda0[S6\x9a\x1c\xb6\x15z\xe8\xa5p\xf4e\xfb\xefk\x1c5\xb9\xbe[g\xfcq\xc6\x03\xe4`zf\xff1\xd87N\xfe\x00\xdc\x1b\xf9\xb6_FߺsM\x87\x8d'\xa8\x86\xe9\xe5}$c\x97\x1b\x9b\x00\xdeR\x90\x9f\x1e9~y\xfcAy\xb8C\xb7\x92\x05\x9d\xa15\xd2\x13\xeb\xeb\xe0\xfa\x15\x9a\xb5f2B\xacz\xc6 \\ȫ\xda\xe6I@݅\xfaZ\x0e\a\xf3\t+\x8f%@|\xa0+B\x95,E\xeeb\x0fuP\xea\xcd\x1a:\xae\xa5H\x99\xb2\x8f~\xfbJSUM[L\x13`\x82\xbc\x18\xbf\xf8\xeas\x13\xfc\xb8\x925\xc1\x9fX\xf8\xb9\xc1\xb7>(\x16B\xcb\xf6\x9e\x98x\xe3]\xacu\x87\xf5\xa4\xb2\x93.\x06\x84@\x1e\x143\x9e\x9a\x1f\x98\x06r\x12\xeb5\x0fC\xaaf-\xcbӶK/\xa9w{\xbf\xa2\xa7\xba\x9c<\x83dp\f=\x01=Ȅ\xba|\xf1:\x1df\x87Xi\"\xfd(\xa5\xd3lj\x9bͱ\xabzu\xfaA\x0f\x89߲\xcb\xc7\"\xa1s\\k\xdb.\x1f\v\x8a^\xff\xa2\u07bf\x94sR\x8b\xf0\xed\xfb\x97\x00w\xbbZ\xf0\x17\x98\xd3e\x92\xfc\xd3l\xc18U\x1cS\xcbn\x1d&ɤ4\x04Ē))\x92n_\x10\xb2\xa4\x8aa\xb5q\x05X\v2\x03M\xbe\xf8\x80S\xe6\x1e\x19\xe9L\x16\x9d\xecA\xd5/W\xfa\x8c\x9fT\x1e\nN\xe0\xfd\xe4\xea&\x8d\xab쥍T\xb0\xa5\xb2\xb6(9_\xbb\xd4\xd8\xd97\xc1>g\xb5\x93-w\xbbv\xd9\x0fa\x8a\u0590\xd4\x05\xdd\x1be\x8d\x17\\\x02\xbe\xe6,C\x87\xbd\b\u007fp\xffeg\xed?ұB\xb7\x97.\t\x15\xf3\xb20:{\x86\xc9\x15\xa2\xfe\x82\xab\xa0\xe0>\xbc\xb9\xfc\xadN\xc1\x9d\ai/\xa4u\xd1a\x98H$\x91\xd5ϯ!,P\xce>\xf8\xda$\x9b&\xc6j\x1a\xf4\xcfMhv_\x16\x9f\x16\xfa\xb0\x03\xf5-p\xd4\r\x9e@\xddw\xcdg\x1d\xda\x16`\xe8\xf2Ÿ\xfd\x17k[3n0\v\xb9S5{p\x99\x90\x16k\xee\xc2YΖ,/)oQ`\x03g5j\xf1B*\xe3]\tRX\x12\u05ff\xdf\xc2qua0\xfa\xac\xee\xf6\x02\xa3\xe3Ǫ\xdf>\x15\xb6\x9b\x05\xb7\u074bk\xaf8,\xfa8\xaeo\a\xae\x03\x1e=k\xb7\xf6\xc3\xd64\xdb;_\x900<\x87+\xbf\xb8~\xbdM\xbd\xd9\xe9\xb2oM\xf5b\xc7t\xfc\x99\xa96|W\x17\x06\xaf\x88\xf9;_\xfa\x8cPr\x0f\xab3\x97\xfc*|\x19z\x0f\xc4u\r\xf6j\xc3=lWU\xec\xcb\x0e\xde6\xc4\xec\xe3\xc0\xbf\x87\x9d\xbe\xaf\x16:\xeeaU\x85\xdd\x11/\xf6\x87\x10\x00\xadQ\xe1۸\xef\x96\xfd;\xa3\x9c{\xe9\x1b\x01k{O\xbfB\xb3\x02K|\x8eT\xec\x1a\x8e\xb5o\xd5,\x85\x9e\xb3\xe2\xa9\xe4\x18\x8a)\xdbr\x1a\xb0_5\xefu\xe0\x1d\xfd]\x893r-\x8d\xfd\x9f\xcbG\xa6\x9f\xb8\x90c\xf7\xf2\xb5\x04}-\r>\xdd\x1b9nj{\xa3\xc6=\x8e$-\x1c\x8f\xc4KJ\xf8\x8dj\x99WO\xdf\u007f\xafP\xcc4\xb9\x12\x96Qy\x1cT\x97\x15\xb5\a\u07fcc\x88\\m\xb7&\xea\xbe݂\xef\xd0j\xbf\xd1\xc4\\\xf3S\xbbQޚ\x86\x9b\x82\xf3h\xbb\xbf`\x82v\xc1i\x06\xb9\xef3a7\xde(j`\xc6v\xb7\x1fX\x80\x9aa\xa2A6ߵ\xaa=B\x87{*އP\x91\xb7\xb3\x9aQ\x85\xf6\xe7P\xa1\xbd\fA\xf1\xb9\x05\x1b\xa1\x93\x18\xe57Or\xb4'1\xb6)\x8bܧ\xbd0\xa7\x85\xa5\xfc\xff\xb1\xec\x19\x89\xe8\u007fIA\x99\xd2cr\xe1o\xa8l\xf9n\xf3\r\xaf\xeb4\x81[\xb8L\x13\xbb\vK\xca\xc1\x15*\xa6\x82\xc0\xce\xf2+r\xba!-\xcf\xc8\xc3\\j\x94\fu\x10\xe9\xe8\x1eVGg\xad\x13\xb2\x05\xa2}\xf8J\x1c\x9dU\xf1\xb2֡\xac\xe4\x14\x860\x8e\xf0oG\xe3\r\x01\xbb\x05\xf6\x13bw'\x95\xec\xf8c\xa5u\xbfq\xa9M\x9b;\xbf/}줍\x8d\n\x8c\xcdo\xb6\x88\xa3\xa9\x1c\xb7̊\xaeOR5\x03\xd3e\x82x\x8d\x19S\x19\xc6\xe4B\xac6\xe0\xe2ŸN\x95\xdb\x1bq\x15\x9d\x15\xad\xf6E\xd8\xd2\x123$\x1a\xa0|\xe2\x92\xee6\x84탛\xbb\xb6cSP\x80\xaa%\\\xcb\x1cn\xa4\xea\xca\xefh\xc7k֟\xef\xb0h\x1bH\x91<\xc7<{|\xb4c\xbe\xa8\x1b{\xbd\xf8\x90Ƨ\xff\xfe\xcd\xfb\xa7\xd6\xf3\xaezp\xf7B(\xf6\xd6p\xfbձ\x0e\xfb\xbe\xbbk#h\xa1\xe7Ґ\x93p\xa9=\xe3\xb2\xcc\xfd\xcd~\xd5\x11\xef\xe9\xb1J\x9d\xcd!/9t7\x1d\xdc(S\x19\x1e\r\xba_)\xd8?\xcbv\x8b\xde\xe0\xa1\xf2Ow\x1d\x84\x1a'\x95i\xdd\xf0\xa8Zv\xf4\x17\xdc\xcf\xf0%oEz\xc8[R\xe1\x9b \xddY\x90\xda\xe0=%a\x1aEׂٙ\xf9\xf6\x1c\xfe\xf1\xcekva\rێC\a\xfb\xe8\x16\xae#\xffՍ\x88\xf8\x96c\xe5r\xe9\xdb0\xbah\xee\xd6\xe5\xdcg\xb40\xa5\n\xed\xafK\x85\xfd\xc0\xea\x16&4`Σ\xa8\x05v\xbba\xe0\xfd\x82L\x8a;\xb6\x00m\xe8\xa2x\x82B^m\xbea7@\xaa\\W\x95N\x9a.\x82\xba\xe9V\x97UL\xebVo\xf9\xb8\x01ہA\x9d̂\x86\x9c\xc0\x12\x04\xf1\x97|0\xf2\xee\x1c\x10\x1d@\xef\xd08QK\x8c\x05\x048\x98\xef;\x95\x8a`W\xbdj\xea\x9b\x14\x11\xae\x8e\xe7\xd4\xc0\xa8\xf3&\xe1^'\xb1S\xea\xb8\x1e\xb9O \xf8\xd25\xd2E\t\x94a\x9e\x98\xdd^\xce\xdd\xdb\xe1恿\xea\xf7\x00\n\xc8\f\x84Eq'\xc7\xf1\xaa\xack\xf5d\x11\xebOp\xe5\xc0\xb9sݱJ\xcaC\v_\xac\x80\x13\xa4\xcaV\x97\x19>\xd2y\xcbj\xd7\x05z\u007f\xe3\xe3\x1dPݕ\xb7\xd4B\xc4\xd7\xcdg\xbd\xad\xe2p\x80KϨ\xeb\x93\xe7:\x962\x05\x9d\xa4\xef\xa7$\xf1\xcb\x11g\x9c\x90bN\xf5S\xec\xf2\xc6>S\xb5\xe4j\x1cʊS\xbe\xdb2'\x10\xe5b\x13\xf8\x88\\\xc3Cǯ_#ѿ\xf7m\x95;\x8e҈\\\x89\x1b%g\xaa\xabJ\xec(\x1c\xac\x0e\n\x19\x91\x1b\xaa\f\xa3\x9c\xaf\xbe\xee\xeeF\x13\xbe\x1e\x85;?\x95'\xb5\x04\xf7X\xad\x882\xe1Ο\xa5T:\t\xbd\xaa\xfd\xc6\x1e뚎;69|tl\x8dP\b\x8e\n\xd6\x06\x8a)Xڌ`:\x95\xca8\xc5w4\"l\xea\xf9g\x97\xf2A\x19G\x15\xce\x05\xd1\b3\xb5\x81X\t$\xa9\b\x15+\xa2\x90X\xb1\xf5ׂ\xae\x9c\v\x9efYi\x8f\xe7\xb96\xb4K\xce\xf4rȡQ\xe9\x89l\x8b\xb5\xd7\xf6\x057\x9f\xaf\xba\xefWuJ\x10\x9cC\x1dv\xcav\x9cak50\xbc\xd5_\xe9\xa0ڞ\xb2nK䩢\x1ax'\xf2j\xbb\x81\xdc\x0e\xb8U\x0f\x87\x05\xb8+\x95\x1bːM\x15y\xbb;\x91\xe9\xf0\xaaݳl\x8e\x15\xf5\xcc\\\xc9rV\xb5K\xdf\xc6@\xb7\xb9\x15K\xbc\xf4[\xf0rf\xc9\xda;\xfaL\xa9D\xc3z\U0006effc\x9e\xee.\xa0\xbbQ\xb8Cw\xd7-\x89\xf7\x94\x02\xd8z\xb8\x9fd\xaf\xae\xbb\u007f\xba\x12yY\xb1\xd4\xcb}d\xf3\xfb\xb5\xc7\xd7\x02)VJ\xd7\x10\xbd<\xed@\xce\t\x9b:\xafifg\xbd\x99\xbc\xf3\x81\x03\"\x0fT\t&fO-\xfe{\xffX\x87j\xe2!t('\x1d\x8b\xa8ԕ(\xe5$LrK\xaeO\xa5\xb0\xf4PO:\xcf\xd0ƏH\xc8y\x03\xc9\xfeK\xfe\x97Z\xadwe.|\xa0\xd2\xe1\xf6\x9e\x89\xfceH\b,x\xa9(\xf7\xff̤plA\xbf$?\xfc\xf8EX\xd0{P\xba\xfa\xf1\xff\x05\x00\x00\xff\xff\x14\x93\x06\u0098\xed\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c]oܸ\xf1ݿb\xe0>\xa4\x05\xbc\xeb\v\xfa\xd0\xc2o9LJ\x1a\x97\xe6\x82\xd8\xf5K\xd1\a\xae4k\xf1,\x91*I\xad\xb3=\xdc\u007f/f(\xeak\xa5\x15\xb5q\xd0\xeb\xc1|I\xcc%\x87\xc3\xf9\xe6pij\xd5ju&J\xf9\x80\xc6J\xad\xae@\x94\x12\xbf8T\xf4\x97]?\xfdծ\xa5\xbeܽ={\x92*\xbd\x82\xeb\xca:]|F\xab+\x93\xe0{\xdcJ%\x9d\xd4\xea\xac@'R\xe1\xc4\xd5\x19\x80PJ;Aݖ\xfe\x04H\xb4rF\xe79\x9a\xd5#\xaa\xf5S\xb5\xc1M%\xf3\x14\r\x03\x0fK\xef\xbe[\xffe\xfd\xdd\x19@b\x90\xa7\xdf\xcb\x02\xad\x13Ey\x05\xaa\xca\xf33\x00%\n\xbc\x02\x9bd\x98V9\xda\xf5\x0es4z-\xf5\x99-1\xa1\xd5\x1e\x8d\xae\xca+h\u007f\xf0\x93jL\xfc.\xee\xea\xf9ܕK\xeb~\xecu\u007f\x90\xd6\xf1Oe^\x19\x91w\xd6\xe3^+\xd5c\x95\v\xd3\xf6\x9f\x01\xd8D\x97x\x05\x1fi\xa9R$\x98\x9e\x01\xd4\x1b\xe3\xa5W ҔI%\xf2OF*\x87\xe6Z\xe7U\x11H\xb4\x82\x14mbd\xe9\x98\x14wN\xb8ʂނ˰\xbb\x0e\xb5\x9f\xadV\x9f\x84ˮ`myܺ̄\r\xbfz\x12y\x00u\x97\xdb\x13n\xd6\x19\xa9\x1e\xc7V{\a\xd7F+\xc0/\xa5AK(CʜU\x8f\xf0\x9c\xa1\x02\xa7\xc1T\x8aQ\xf9^$OU9\x82H\x89\xc9z\x80g\x8dI\xbfs\x0e\x97\xfb\f!\x17ց\x93\x05\x82\xa8\x17\x84ga\x19\x87\xad6\xe02i\xe7iB@z\xd8zt>\f\xbb=B\xa9pX\xa3\xd3\x01\x15\xa4z} \x91=\x98\xef\x1eq\x1c\x98\xffy\xf7\xd6\xcbM\x92a!\xaeꑺD\xf5\xee\xd3\xedß\xefz\xdd0\x90\x83z\x97 -\bx`\xa1\x06S\xab\x1f\xb8L80H\\C\xe5hDip\x15(\x936 \x01\xb4\x81\x12\x8dԩL\x02Ey\xb2\xcdt\x95\xa7\xb0A\"\uee99P\x1a]\xa2q2\xa8\x8do\x1d3\xd1\xe9\x1d`\xfc\x866\xe5Gy)B˂S+\x03\xa65\x1d\xbclK\xdb\xe2\xcf\x04\xee\x01\x06\x1a$\x14\xe8\xcdϘ\xb85ܡ!0\x01\xebD\xab\x1d\x1a\xa2@\xa2\x1f\x95\xfcO\x03ے\xc4:\x16$\x87\xb5.\xb7\x8d\x95O\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xab@\xa5:\xf0x\x88]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05\xf3\x98袨\x94t\xfbK\xb6trS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+F]\xb1\x89\\\x17\xe9\x1f\x02G\xed\x9b\x1e\xae\a\xba\xe2\x1b\x1b\xb1#\x1c k\xe6\x05\xc6O\xf5\xbbh\tM]D\x9d\xcf7w\xf7]a\x92vH}\xa6{G\xc2Z\x16\x10\xc1\xa4\xdab\xad\x8d[\xa3\v\x86\x89*-\xb5T\x8e\xffHr\x89jH~[m\n\xe9\x88\xef\xff\xae\xd0:\xe2\xd5\x1a\xae\xd9g\x90\x1cV%iO\xba\x86[\x05ע\xc0\xfcZX\xfc\xe6\f J\xdb\x15\x116\x8e\x05]w7\x1c\xec\xa9\xd6\xf9!\xb8\xa6\t~\x05\x1d\xbf+1\xe9\xa9\f͓[\x99\xb0b\xb0\xe5kL\xc0\xc0\xfa\xf96\xae\xb5P\x9b\x1e\x1a>\xec?jmb}\xc2\x01L\xa8M\xcc\xfa\xe0\x97\tj\xf2OX\x94\xa4\xae3(\xde\xd7\xc3\bE\xa2Qڄ \xc1Y\x06\xf3\xa6k\xab\x06\aF\x85\x97ː赓im5\x0e\xa8y\x9c\xa2\x1e\xb1\xad\xa8r\xf7@~\x1c\xed\xbd\xfe\x8c\xd6\xc9dl\xe4`\x13\xefG'\x06~\xa3%\n\xbb\f\r)'\xff\xc0\xf6n\x14.\xb0\xceXL\xd9\xe4\x89'r\x99\x1bO\x01\xb2\x9dy\x0e\xa5Na\xe7W\x82\xcd> }ț\x96?\x1b\xads\x14cT\xc3/I^\xa5\x986a\xce(]\x06\xbb\xbd9\x98\xc4\x01\xa1\x90\x8a\xa4\x8c\xc2/BU5\xbfN\xec\x93\xfd\x950\bd(\xa4\xf20A\xfa\xb0d3!pԤ\xc3b\x02ϣ\x12\xe9\x1b\x05\x9eb\x93\xe3\x158S\x8d\xc9z\x80!\x8c\x11\xfb#4\vA\xf3\x12\x925sjs\x9e\xcb\x04\x89X\x8d\xd1f\xaa1i&\xf6\xf7\u007fH\xb0L\xeb\xa7\x18\"\xfd\x8dƵ\xce\t\x12>\x9b\xc0\x063\xb1\x93\xda\xd8a\x84\x83_0\xa9\x1cN\xe9\x91p\x90\xca\xed\x16\r\xc1›\x89\xbf\x8f\x11븉\xa0f\x8e3\xfe`_-ӉyL\x8d\xa9\xad\xb0)\x9e\x84\n\x8c8Y\xec\xaa\x04\xa9R\xb9\x93i%r\x90\xca:\xa1\x12\xbf?\xd1\xe07\xbe?\x98\x13\x88\x03\xfc\xbd\x01\x0e\xbb .\xf5<\x9bVH\xe1h\xa1\u0378p\x84v\bf\x9a\f\x1bA\x16PO\xb9\xa3\xb6\x19:\xc5ը\xa4\xecR[\xbbs\xd1r\xca\a\x85\xb9\xd8`\x0e\x16sL\x9c6\xd3\xe4\x89\x11\x02\xdfb\xed\xe7\x04eG,i\xeb3HPg\x8dhۜ\x86\xe7L&\x99\x8f\xdfH\xca\x18\x16\xa4\x1a-[\fQ\x96\xf9\xfeئ!F2\xea\xc5\xe6\x8cF\xdb\"\xcc\xc7\x10\xee\x94!i[\xa4\rnی5\xeeS\xbd\x11\x9bW\xa2\xf7\xd0T_%\xec\xb7\a\xd3_^؉ܒ\xcew\xb7[\xc0\xa2t\xfb\v\x90.\xf4\xc6@\xa5\x00\xab\xc5\xe3wƸӴ\xe5v8\xfbŵ\xe5E\xb8֠\xf1;a\x1a;\xab\xbb\xdaW-b؇\xee\xcc\v\x90ۆa\xe9\x05le\xee\x90c\xa99D;\x81\xce,\xe7^\x92@\xb1\xbe\x97Z!\\\x92\xdd4Gڈ\x19\x03Z\r\x01\xf8\xb8<\x9ca\x98\a\x11 \xa1\t*8\v\"\r\x16>\xbbr\xcf\xfa\xd1\xf6p\x04\xf8\xee\xe3{L\xe7H\x06\xf1\x92z\xb0\xa9w\x83H\xa7\x8b\x02o0\ndgS\x1c\xa65g<\x9fC\xbb\x00\x01O\xb8\xf7\x91\xd5\xe8\xe1r\xac\x11kE\x03\xd2 '\xf4،<\xe1\x9eA\xd5\x19\xba(xKDŷ'\xdc\xc7\x0e\x1d\x10\x95\xf0\xabs\x14\x9e\xba\xd4\xc1\xbb\x88Q\xa5\xb65D\xadu\a\x9c\x8e\xdb,,3J\xa1\x05\x8a\x9f\xb8\xed\x86a\xbd\xb4\xf4\x13\xee\xdfX\xcf>ҚL\x96\v(@\x06\x1b,\xb2\x86\x85|\xec\x83\xc8e\xda,\xc6z\xb2\x00⭺\x80\x8f\xda\xd1?7_\xa4%\x14U\n\xef5ڏ\xdaq\xcf7%\xb1\xdfĉ\x04\xf6\x93Y-\x95w\vD\x97E\xeb\xb78\xb0\v%\x11m\xd8&-\xdc*:\x9fy\xfa,aS\x86\x019\x8fVQY\xce\xe8*\xadV\xec\xa6\xc3j\v\x80v\xf1\xaaY\xa5M\x8fS\x17\v!\x8e\xa2X\xa3wO\xde\xca\xffr\x90\v?\xd6\f\x96\xb9H0\x85\xb4\xe2|;'ޅ\xc3G\x99@\x81\xe6\x11\xa1$\xbf\x11/T\v,\xb9o'Ha|h\x11Z\xed\x16\xd28\xc4V\xa4\xf5\x91#\x03\x9b\xa3\x86Odُ\x0f\x8f\xdb%\xbbw\x8e\x87\xa2\xa8߽\xd2]\xe6Y\x16\xf2\xeb0\x06\xf1H\xfa\xf0\xa3\x10\x9c\xec\xfd\x85\xdc+\x8b\xf7\xafq\xdePHc\xd7\xf0\x8e/\xb4s\xec\xce\x0fY\xc2\xceRQ \t\x13i\x81\xe4d'r\n\x1f\xc8x+\xc0\xdc\a\x13z{\x10Ař\x98\xe7L[\xef\xf3\xb7\x12s\xbe\xdd:\u007f\xc2\xfd\xf9Ł\xf5:\xbfU\xe7q0\xc9\xe6\x1f\x18\xad&j\xd1*\xdf\xc39\xffv\u0381\xd9\x12\x159!x[ \xd5\xd1C\xf9\xfay\xc9Q\x80\xce\xda!j\xa1\xc9\xcd%-\x85\xf0s\xbb\x88\x96\xe9R[\xb7\b\xadO\xda:\x9f\x00\xec\x85\xdb#\x19\u0098\xd3_\x9d5\x04\xb1uh\xc0:m\u0085(\x99\xddA\x82\x9c8o\xe7yO\xacn\xb2\x91\x1e0\x1d2\xcf[\v\xe1m\xfa\xb9\xbf)\xa5\xff\xcf\xc3L8Xbإ\xd1\tZ;/J\x91\x9ec&a\xdb$k\x85?\xbcm\xa3LsL*9\xb4e\xa18\x91\xf6\x84\x83\xcd͗Nޙ\xcc\x10\xfd\x1d#ʧ\xe0\b\\\xe8T\x14bx9\x1f\x8d\ued5f\x1d\x14\xb0\x06\xe6\x0fL\xe6\xb1b\xa3\xb2,n\xaeE\xf2\xb7\x16x\x14R\xdd\xf2B\xf0\xf6\x9b\x05+\x10L9\x9ez\x94\xb9\x0e\xf3[\x864\x1d\xb1\xe7W\bW͚\xefj\f\xf68{x\x93\x11\xcf)\xa0`Zi\xd7M\xd6\xd4+\xbd\xb1\xb0\x95ƺ\x16\xe1\x05P\xa5\xe5\xeb\xe4o{\xc6T7Ɯ|\xc4\xfc\xc9\xcf\xee\xa4\x153\xfd\\\x17F,9X\a\xe2gb\x87 \xb7 \x1d\xa0Jt\xa58\xe1E悖Y\x00\xd13\xd1;\x93H\x9fٙ\xac\xaa\"\x9e +\x96N\xa9f\xb3c\xdd)?\b\x19\x97\x9d\x82\xd3\xd8\xead\x81\xba\x9au\xedm\xebWv\xf8ٽ\n\x98B|\x91EU\x80(\x88-K\u038d[_?\x18\xcae<\xaf\x9f\x85tu\x05\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8aM\xf8P\xf3\u007f\xb4\xded\xaa\t\xd8\n\x99Wf\x81\x8d^̙\xa5\xe7\xb6\xda<\xbd\xfca,\x1e\x91\x15\x1332\xe9\xbe h\x9e\xf7\x1f\xa5Y\x162\u007f2\xf8\xf2\xa1ii$I\xa9\x9e\x8bNgar\xf4ڏNk\xe1\x15j?\x15\x9e\xceBeL^\xc3Ӧ\xbd\x86\xa7\xaf\xe1\xe9kx:h\xaf\xe1\xe9kx\xfa\x1a\x9e\x8e\xb7\xd7\xf0\xb4\xd3^\xc3\xd3h\xff\x11\x83\xe1\x8a3\xb7G\x06Da\x15Y\x821\x87\xf6\xccZu\xa5\xd1u^Y\x87fI\x85\xf4\xed\xf8̑\x1a\xfa\xc4\x0fY\xf1\x17zSRӖ\xae\xb4N\xaf)\x99&\x95\f\xca\xe4\xbfň\x88£ˠ\xa7\xab\xedc\v\xe8\xe6\xca\xe6\xfa\xb5\xe3M\xb9\x9a\xff\xdf\x04A\x9c\x0e\xcb\xd7\xdc\xf3\xdf\xf8tk\xae\xfa\xb5o|\x0e\b\x18\xff&\xeb\xca#\xcb\xdaf\x8aَ\x17\xe2Oy\xf8@\xcb\xc1\xe5B\x9f\x98\xa6W\xf8\xfd\x9b\xa6eD\xb5\xd9t\x8dY}k\x89N\xecޮ\xfb\xbf8]W\x9cM\xec\xecY\xba\xcc\u007f\u007fDGW\xf5\xd8-k\x0frZ\u007f\xfb7\xa4\xf1\x04Dm@\xc9\xdc3 @\xe8\x91\x1f~*\xfd\x11\xf9d=\x9f?\xa8\xc5ץ-\xadFk\xea\x87\xe6\xbd\xcaWԠ-\xfb@`\xb6\xde,\x06i\x88\xa92\x1b\xaf\x1f\x9b\x81\xba\xa4\xb6,\xf6\f\x1eQG\x16_=\x16G\x1e\xe0/vckƢ\xa3\xb6\xd8\xfa\xb0oS\x15\x16Y\v֩\xf0\x9a\x05yb\x05X4\xc1⪽\xa2k\xbc:\x95[\xf3\xd4:R\xd95^\xaf5\vr\xac\x9e+\xa6J+\n\xd7\xe8ڬ\xa6\xe2j>\x93\xf8U\x15Y/_\xfb\xfd\x92q\xfe\xf1\xfa\xaa\xa8\xaa\xaa\xa8\xb3\xc0<\xceQuSK\xab\xa5\xa2\xa8\xba\xb42\xaa\xa9z:\xb2pT=\xd4a\xadӱ\xad\xccVAMW8\x1d\x03;V\xfb\x14Q\xd7t\x04d\xb7\xe2iq\x180+M3\x03ƿ\xaa\x0fm\xde\xd7\xe6\xff\v\t\xfc\xdaMk\x93\xa2\x99=\x95,A}\x16\xed~\xber\xb0\xfe\xe0s\x9d\xf019\x8d\xea\x9ex\xa6\xa2(\xdd|>\x92\xc0\x8fR\xa5^\x92HY:1\r\xbfP\xc1\x05fM\x985]q\xdbF\xb4\x83Ӗ\xc5R\x90QOa\xb3\xf7Y!\xbb\x86\x1b\x91d\xcd\xc0\t\x88\xbcr&,\x9d\xec\v\xe1\xe0\xbc9\xc6^\x86\x99\xd4s\xbe\x06\xf8A7\x19\x84\xce\xe7\xa1\x13p\xad,\xca|\x0f\x95E8\xef\x03\xfa\xbaSؤ\xecX%J\x9b\xe9\xf0\x92@\xc4A\xec\xae?c$_\x12\xde\x11Hr]\xa5\xcd\nG\xd8-\xd4\x1e>=p$\xc7_O'\xedW\xe6u\xa4\x16\xceU\x83\x8f\xd0'@N=\x1e\xb1\x88d\xd3Y\x15\xeb\xb4\x11\x8f\xf8A\xfbw5bh֟\xd1{Z\xa5\xb6\xab!\xc7Z\x17MNJ\xb2\xdf\xdb\x10`{\xf5Rk[\x9b\x84\"l\xa7L\ue31e;\x97Gl\xee\xfe\xfe\x83ߐ\x93\x05\xae\xdfW>õ*\x85\xb1H\x94\x0e\x1b\xf5\x936Ӗ0\xd3ϐ\xeb\x9a\x0e\xdf\x0f\xf7a\x90\xafz8\x99v\xd2n\xfc\x13\x16A|\x03\xe9bD\xfea|f\xe7\xb0\xdcaⱜ\x98\xdeN\xc2\x12\xd6\xeaD\xb2-\xe2\x14\x05߰|\xbb7\x16\x8eEdGLFe\xf1\xa7g\x85\xe6sPT{\xab<\xa7f^]\xf9\xc7\xc1\xc4\xc0\xe01\xf3A\xf6o0|\xcc署@ֿ\xf7\xe6\xcd8\x13.\xbcCsH\xba\x19\xfd\x9f\xd6\xfd\xf1\x88z5\xfe\xf4˪y\x8d\xe6,\x82\xb2\xfe\xbd\xb1\x98\a~\xfc\xa3n\x89(]ej\xf7\x9aT\x86\x1f\x9c \xe8\xdfc8퉟\xf6\xb9\xb3\x19^\xb6\x0f\xa0\xb5\x19\x86\xd9\xe7\xd6F\xf8\xd7<\x164\xf9z\x8e\xf7\xae\xfe9\xb4\x15\xc1?\x8d\x9d\xa3z\xc0\x0ft\xcc\xec\xf4\x13\x8din\xc7kB\xf3\xc4\xf0\xb0\xc7\xdd\x14\xea\xe3ם+\xf8\x88\xcf#\xbd7\x8a6qx\x18\xf5w\x9a\x98r\x86b\xecy\xb3\xa3[\xdc5\xb3\xf8By\xc4Z\xf4\xcd\xdc`\xf8 S-\xf2\xbc\x03\xd1_\x1e\x8f\xb1\xf5\x8fr\xeb\xd3F\t\xed\xe9O\a#&\r\xd7Q\xa35e\xb0FU\xea\xa0Ӣ\xd9a\xda\x11\x92ڇw{\xaa\xcd\xc1;+\xb5b\xc2/\xbf\x9e\xb5:*\x92\x04KWߏt\x1f\x859\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index 73e2872f35..1b1b8c05a8 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -92,6 +92,11 @@ type RestoreSpec struct { // Hooks represent custom behaviors that should be executed during or post restore. // +optional Hooks RestoreHooks `json:"hooks,omitempty"` + + // ExistingResourcePolicy specifies the restore behaviour for the kubernetes resource to be restored + // +optional + // +nullable + ExistingResourcePolicy PolicyType `json:"existingResourcePolicy,omitempty"` } // RestoreHooks contains custom behaviors that should be executed during or post restore. @@ -212,6 +217,14 @@ const ( // RestorePhaseFailed means the restore was unable to execute. // The failing error is recorded in status.FailureReason. RestorePhaseFailed RestorePhase = "Failed" + + // PolicyTypeNone means velero will not overwrite the resource + // in cluster with the one in backup whether changed/unchanged. + PolicyTypeNone PolicyType = "none" + + // PolicyTypeUpdate means velero will try to attempt a patch on + // the changed resources. + PolicyTypeUpdate PolicyType = "update" ) // RestoreStatus captures the current status of a Velero restore @@ -302,3 +315,6 @@ type RestoreList struct { Items []Restore `json:"items"` } + +// PolicyType helps specify the ExistingResourcePolicy +type PolicyType string diff --git a/pkg/builder/restore_builder.go b/pkg/builder/restore_builder.go index 7ea43e73e4..4fa24ac35f 100644 --- a/pkg/builder/restore_builder.go +++ b/pkg/builder/restore_builder.go @@ -95,6 +95,12 @@ func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder return b } +// ExistingResourcePolicy sets the Restore's resource policy. +func (b *RestoreBuilder) ExistingResourcePolicy(policy string) *RestoreBuilder { + b.object.Spec.ExistingResourcePolicy = velerov1api.PolicyType(policy) + return b +} + // IncludeClusterResources sets the Restore's "include cluster resources" flag. func (b *RestoreBuilder) IncludeClusterResources(val bool) *RestoreBuilder { b.object.Spec.IncludeClusterResources = &val diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index 95f733209c..caf94dc51f 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -82,6 +82,7 @@ type CreateOptions struct { Labels flag.Map IncludeNamespaces flag.StringArray ExcludeNamespaces flag.StringArray + ExistingResourcePolicy string IncludeResources flag.StringArray ExcludeResources flag.StringArray NamespaceMappings flag.Map @@ -113,6 +114,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.Var(&o.Labels, "labels", "Labels to apply to the restore.") flags.Var(&o.IncludeResources, "include-resources", "Resources to include in the restore, formatted as resource.group, such as storageclasses.storage.k8s.io (use '*' for all resources).") flags.Var(&o.ExcludeResources, "exclude-resources", "Resources to exclude from the restore, formatted as resource.group, such as storageclasses.storage.k8s.io.") + flags.StringVar(&o.ExistingResourcePolicy, "existing-resource-policy", "", "Restore Policy to be used during the restore workflow, can be - none or update") flags.VarP(&o.Selector, "selector", "l", "Only restore resources matching this label selector.") f := flags.VarPF(&o.RestoreVolumes, "restore-volumes", "", "Whether to restore volumes from snapshots.") // this allows the user to just specify "--restore-volumes" as shorthand for "--restore-volumes=true" @@ -172,6 +174,10 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return errors.New("Velero client is not set; unable to proceed") } + if len(o.ExistingResourcePolicy) > 0 && !isResourcePolicyValid(o.ExistingResourcePolicy) { + return errors.New("existing-resource-policy has invalid value, it accepts only none, update as value") + } + switch { case o.BackupName != "": if _, err := o.client.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), o.BackupName, metav1.GetOptions{}); err != nil { @@ -264,6 +270,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { ExcludedNamespaces: o.ExcludeNamespaces, IncludedResources: o.IncludeResources, ExcludedResources: o.ExcludeResources, + ExistingResourcePolicy: api.PolicyType(o.ExistingResourcePolicy), NamespaceMapping: o.NamespaceMappings.Data(), LabelSelector: o.Selector.LabelSelector, RestorePVs: o.RestoreVolumes.Value, @@ -351,3 +358,10 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return nil } + +func isResourcePolicyValid(resourcePolicy string) bool { + if resourcePolicy == string(api.PolicyTypeNone) || resourcePolicy == string(api.PolicyTypeUpdate) { + return true + } + return false +} diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index fac53f5cc7..286467d038 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -148,6 +148,13 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel describePodVolumeRestores(d, podVolumeRestores, details) } + d.Println() + s = "" + if restore.Spec.ExistingResourcePolicy != "" { + s = string(restore.Spec.ExistingResourcePolicy) + } + d.Printf("Existing Resource Policy: \t%s\n", s) + d.Println() d.Printf("Preserve Service NodePorts:\t%s\n", BoolPointerString(restore.Spec.PreserveNodePorts, "false", "true", "auto")) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 496b6cc570..63bb402551 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1240,6 +1240,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // labels, so copy them from the object we attempted to restore. labels := obj.GetLabels() addRestoreLabels(fromCluster, labels[velerov1api.RestoreNameLabel], labels[velerov1api.BackupNameLabel]) + fromClusterWithLabels := fromCluster.DeepCopy() // saving the in-cluster object so that we can create label patch if overall patch fails if !equality.Semantic.DeepEqual(fromCluster, obj) { switch groupResource { @@ -1267,17 +1268,58 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso _, err = resourceClient.Patch(name, patchBytes) if err != nil { warnings.Add(namespace, err) + // check if there is existingResourcePolicy and if it is set to update policy + if len(ctx.restore.Spec.ExistingResourcePolicy) > 0 && ctx.restore.Spec.ExistingResourcePolicy == velerov1api.PolicyTypeUpdate { + // remove restore labels so that we apply the latest backup/restore names on the object via patch + removeRestoreLabels(fromCluster) + //try patching just the backup/restore labels + warningsFromUpdate, errsFromUpdate := ctx.updateBackupRestoreLabels(fromCluster, fromClusterWithLabels, namespace, resourceClient) + warnings.Merge(&warningsFromUpdate) + errs.Merge(&errsFromUpdate) + } } else { ctx.log.Infof("ServiceAccount %s successfully updated", kube.NamespaceAndName(obj)) } default: - e := errors.Errorf("could not restore, %s %q already exists. Warning: the in-cluster version is different than the backed-up version.", - obj.GetKind(), obj.GetName()) - warnings.Add(namespace, e) + // check for the presence of existingResourcePolicy + if len(ctx.restore.Spec.ExistingResourcePolicy) > 0 { + resourcePolicy := ctx.restore.Spec.ExistingResourcePolicy + ctx.log.Infof("restore API has resource policy defined %s , executing restore workflow accordingly for changed resource %s %s", resourcePolicy, fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + + // existingResourcePolicy is set as none, add warning + if resourcePolicy == velerov1api.PolicyTypeNone { + e := errors.Errorf("could not restore, %s %q already exists. Warning: the in-cluster version is different than the backed-up version.", + obj.GetKind(), obj.GetName()) + warnings.Add(namespace, e) + // existingResourcePolicy is set as update, attempt patch on the resource and add warning if it fails + } else if resourcePolicy == velerov1api.PolicyTypeUpdate { + // processing update as existingResourcePolicy + warningsFromUpdateRP, errsFromUpdateRP := ctx.processUpdateResourcePolicy(fromCluster, fromClusterWithLabels, obj, namespace, resourceClient) + warnings.Merge(&warningsFromUpdateRP) + errs.Merge(&errsFromUpdateRP) + } + } else { + // Preserved Velero behavior when existingResourcePolicy is not specified by the user + e := errors.Errorf("could not restore, %s %q already exists. Warning: the in-cluster version is different than the backed-up version.", + obj.GetKind(), obj.GetName()) + warnings.Add(namespace, e) + } } return warnings, errs } + //update backup/restore labels on the unchanged resources if existingResourcePolicy is set as update + if ctx.restore.Spec.ExistingResourcePolicy == velerov1api.PolicyTypeUpdate { + resourcePolicy := ctx.restore.Spec.ExistingResourcePolicy + ctx.log.Infof("restore API has resource policy defined %s , executing restore workflow accordingly for unchanged resource %s %s ", resourcePolicy, obj.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + // remove restore labels so that we apply the latest backup/restore names on the object via patch + removeRestoreLabels(fromCluster) + // try updating the backup/restore labels for the in-cluster object + warningsFromUpdate, errsFromUpdate := ctx.updateBackupRestoreLabels(fromCluster, obj, namespace, resourceClient) + warnings.Merge(&warningsFromUpdate) + errs.Merge(&errsFromUpdate) + } + ctx.log.Infof("Restore of %s, %v skipped: it already exists in the cluster and is the same as the backed up version", obj.GroupVersionKind().Kind, name) return warnings, errs } @@ -1831,3 +1873,81 @@ func (ctx *restoreContext) getSelectedRestoreableItems(resource, targetNamespace } return restorable, warnings, errs } + +// removeRestoreLabels removes the restore name and the +// restored backup's name. +func removeRestoreLabels(obj metav1.Object) { + labels := obj.GetLabels() + + if labels == nil { + labels = make(map[string]string) + } + + labels[velerov1api.BackupNameLabel] = "" + labels[velerov1api.RestoreNameLabel] = "" + + obj.SetLabels(labels) +} + +// updates the backup/restore labels +func (ctx *restoreContext) updateBackupRestoreLabels(fromCluster, fromClusterWithLabels *unstructured.Unstructured, namespace string, resourceClient client.Dynamic) (warnings, errs Result) { + patchBytes, err := generatePatch(fromCluster, fromClusterWithLabels) + if err != nil { + ctx.log.Errorf("error generating patch for %s %s: %v", fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster), err) + errs.Add(namespace, err) + return warnings, errs + } + + if patchBytes == nil { + // In-cluster and desired state are the same, so move on to + // the next items + ctx.log.Errorf("skipped updating backup/restore labels for %s %s: in-cluster and desired state are the same along-with the labels", fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + return warnings, errs + } + + // try patching the in-cluster resource (with only latest backup/restore labels) + _, err = resourceClient.Patch(fromCluster.GetName(), patchBytes) + if err != nil { + ctx.log.Errorf("backup/restore label patch attempt failed for %s %s: %v", fromCluster.GroupVersionKind(), kube.NamespaceAndName(fromCluster), err) + errs.Add(namespace, err) + } else { + ctx.log.Infof("backup/restore labels successfully updated for %s %s", fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + } + return warnings, errs +} + +// function to process existingResourcePolicy as update, tries to patch the diff between in-cluster and restore obj first +// if the patch fails then tries to update the backup/restore labels for the in-cluster version +func (ctx *restoreContext) processUpdateResourcePolicy(fromCluster, fromClusterWithLabels, obj *unstructured.Unstructured, namespace string, resourceClient client.Dynamic) (warnings, errs Result) { + ctx.log.Infof("restore API has existingResourcePolicy defined as update , executing restore workflow accordingly for changed resource %s %s ", obj.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + ctx.log.Infof("attempting patch on %s %q", fromCluster.GetKind(), fromCluster.GetName()) + // remove restore labels so that we apply the latest backup/restore names on the object via patch + removeRestoreLabels(fromCluster) + patchBytes, err := generatePatch(fromCluster, obj) + if err != nil { + ctx.log.Errorf("error generating patch for %s %s: %v", obj.GroupVersionKind().Kind, kube.NamespaceAndName(obj), err) + errs.Add(namespace, err) + return warnings, errs + } + + if patchBytes == nil { + // In-cluster and desired state are the same, so move on to + // the next items + ctx.log.Errorf("skipped updating %s %s: in-cluster and desired state are the same", fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster)) + return warnings, errs + } + + // try patching the in-cluster resource (resource diff plus latest backup/restore labels) + _, err = resourceClient.Patch(obj.GetName(), patchBytes) + if err != nil { + ctx.log.Errorf("patch attempt failed for %s %s: %v", fromCluster.GroupVersionKind(), kube.NamespaceAndName(fromCluster), err) + warnings.Add(namespace, err) + // try just patching the labels + warningsFromUpdate, errsFromUpdate := ctx.updateBackupRestoreLabels(fromCluster, fromClusterWithLabels, namespace, resourceClient) + warnings.Merge(&warningsFromUpdate) + errs.Merge(&errsFromUpdate) + } else { + ctx.log.Infof("%s %s successfully updated", obj.GroupVersionKind().Kind, kube.NamespaceAndName(obj)) + } + return warnings, errs +} diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index dadf6bea5a..5c19611630 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -967,6 +967,76 @@ func TestRestoreItems(t *testing.T) { test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").Result()), }, }, + { + name: "update secret data and labels when secret exists in cluster and is not identical to the backed up one, existing resource policy is update", + restore: defaultRestore().ExistingResourcePolicy("update").Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("secrets", builder.ForSecret("ns-1", "sa-1").Data(map[string][]byte{"key-1": []byte("value-1")}).Result()). + Done(), + apiResources: []*test.APIResource{ + test.Secrets(builder.ForSecret("ns-1", "sa-1").Data(map[string][]byte{"foo": []byte("bar")}).Result()), + }, + want: []*test.APIResource{ + test.Secrets(builder.ForSecret("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")).Data(map[string][]byte{"key-1": []byte("value-1")}).Result()), + }, + }, + { + name: "update service account labels when service account exists in cluster and is identical to the backed up one, existing resource policy is update", + restore: defaultRestore().ExistingResourcePolicy("update").Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("serviceaccounts", builder.ForServiceAccount("ns-1", "sa-1").Result()). + Done(), + apiResources: []*test.APIResource{ + test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + want: []*test.APIResource{ + test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")).Result()), + }, + }, + { + name: "update pod labels when pod exists in cluster and is identical to the backed up one, existing resource policy is update", + restore: defaultRestore().ExistingResourcePolicy("update").Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("pods", builder.ForPod("ns-1", "sa-1").Result()). + Done(), + apiResources: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + want: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")).Result()), + }, + }, + { + name: "do not update pod labels when pod exists in cluster and is identical to the backed up one, existing resource policy is none", + restore: defaultRestore().ExistingResourcePolicy("none").Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("pods", builder.ForPod("ns-1", "sa-1").Result()). + Done(), + apiResources: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + want: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + }, + { + name: "do not update pod labels when pod exists in cluster and is identical to the backed up one, existing resource policy is not specified, velero behavior is preserved", + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("pods", builder.ForPod("ns-1", "sa-1").Result()). + Done(), + apiResources: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + want: []*test.APIResource{ + test.Pods(builder.ForPod("ns-1", "sa-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "foo", "velero.io/restore-name", "bar")).Result()), + }, + }, { name: "service account secrets and image pull secrets are restored when service account already exists in cluster", restore: defaultRestore().Result(), @@ -2168,7 +2238,7 @@ func TestRestorePersistentVolumes(t *testing.T) { ObjectMeta( builder.WithAnnotations("velero.io/original-pv-name", "source-pv"), builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - // the namespace for this PV's claimRef should be the one that the PVC was remapped into. + // the namespace for this PV's claimRef should be the one that the PVC was remapped into. ).ClaimRef("target-ns", "pvc-1"). AWSEBSVolumeID("new-volume"). Result(), diff --git a/site/content/docs/main/api-types/restore.md b/site/content/docs/main/api-types/restore.md index 9663125f68..daa293e923 100644 --- a/site/content/docs/main/api-types/restore.md +++ b/site/content/docs/main/api-types/restore.md @@ -73,6 +73,9 @@ spec: # to restore from. If specified, and BackupName is empty, Velero will # restore from the most recent successful backup created from this schedule. scheduleName: my-scheduled-backup-name + # ExistingResourcePolicy specifies the restore behaviour + # for the kubernetes resource to be restored. Optional + existingResourcePolicy: none # Actions to perform during or post restore. The only hooks currently supported are # adding an init container to a pod before it can be restored and executing a command in a # restored pod's container. Optional. From ec9a797c63867f8c04ea7e96db553da27396e1ab Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 9 May 2022 14:07:27 -0400 Subject: [PATCH 135/366] Add supported versions statment to support process page Signed-off-by: Abigail McCarthy --- site/content/docs/main/support-process.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/content/docs/main/support-process.md b/site/content/docs/main/support-process.md index d07268c528..547ed9c0ff 100644 --- a/site/content/docs/main/support-process.md +++ b/site/content/docs/main/support-process.md @@ -3,6 +3,12 @@ title: "Support Process" layout: docs --- + +Velero provides best effort support through the process on this page for the current version of Velero and n-1 Velero version, including all patch release in the supported minor releases. For example, if the current version is 1.9, the Velero maintainers would offer best effort support for v1.9 and v1.8. If you have a question about a previous Velero version (for example, 1.7), please note that maintainers may ask you to upgrade to a supported version before doing any investigation into your issue. + +For more information about Velero testing and supported Kubernetes versions, see Velero's [compatibility matrix](https://github.com/vmware-tanzu/velero/blob/main/README.md#velero-compatibility-matrix). + + ## Weekly Rotation The Velero maintainers use a weekly rotation to manage community support. Each week, a different maintainer is the point person for responding to incoming support issues via Slack, GitHub, and the Google group. The point person is *not* expected to be on-call 24x7. Instead, they choose one or more hour(s) per day to be available/responding to incoming issues. They will communicate to the community what that time slot will be each week. From 35ac28741f2a858d328f8285c1c69966457ec3fb Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 9 May 2022 14:18:56 -0400 Subject: [PATCH 136/366] fix typo Signed-off-by: Abigail McCarthy --- site/content/docs/main/support-process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/main/support-process.md b/site/content/docs/main/support-process.md index 547ed9c0ff..96492c6220 100644 --- a/site/content/docs/main/support-process.md +++ b/site/content/docs/main/support-process.md @@ -4,7 +4,7 @@ layout: docs --- -Velero provides best effort support through the process on this page for the current version of Velero and n-1 Velero version, including all patch release in the supported minor releases. For example, if the current version is 1.9, the Velero maintainers would offer best effort support for v1.9 and v1.8. If you have a question about a previous Velero version (for example, 1.7), please note that maintainers may ask you to upgrade to a supported version before doing any investigation into your issue. +Velero provides best effort support through the process on this page for the current version of Velero and n-1 Velero version, including all patch releases in the supported minor releases. For example, if the current version is 1.9, the Velero maintainers would offer best effort support for v1.9 and v1.8. If you have a question about a previous Velero version (for example, 1.7), please note that maintainers may ask you to upgrade to a supported version before doing any investigation into your issue. For more information about Velero testing and supported Kubernetes versions, see Velero's [compatibility matrix](https://github.com/vmware-tanzu/velero/blob/main/README.md#velero-compatibility-matrix). From 4013c92afb9593d288ee056d88c5e75079cfd216 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 10 May 2022 13:46:52 -0400 Subject: [PATCH 137/366] Update compatibility matrix for 1.9 Signed-off-by: Abigail McCarthy --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 36cc3baff9..da18b502a1 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,22 @@ See [the list of releases][6] to find out about feature changes. The following is a list of the supported Kubernetes versions for each Velero version. -| Velero version | Kubernetes versions| -|----------------|--------------------| -| 1.8 | 1.16-latest | -| 1.6.3-1.7.1 | 1.12-latest | -| 1.60-1.6.2 | 1.12-1.21 | -| 1.5 | 1.12-1.21 | -| 1.4 | 1.10-1.21 | +| Velero version | Expected Kubernetes version compatibility| Tested on Kubernetes version| +|----------------|--------------------|--------------------| +| 1.9 | 1.16-latest | 1.20.5, 1.21.2, 1.22.5, 1.23, and 1.24 | +| 1.8 | 1.16-latest | | +| 1.6.3-1.7.1 | 1.12-latest || +| 1.60-1.6.2 | 1.12-1.21 || +| 1.5 | 1.12-1.21 || +| 1.4 | 1.10-1.21 | | + +The Velero maintainers are continuously working to expand testing coverage, but are not able to test every combination of Velero and supported Kubernetes for each Velero release. The table above is meant to track the current testing coverage and the expected supported Kubernetes versions and configurations. + +Velero also supports IPv4, IPv6, and dual stack environments. + +If you are interested in using a different version of Kubernetes with a given Velero version, we'd recommend that you perform testing before installing or upgrading your environment. + +For full information around capabilities within a release, also see the Velero [release notes](https://github.com/vmware-tanzu/velero/releases) and Kubernetes [release notes](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG). [1]: https://github.com/vmware-tanzu/velero/workflows/Main%20CI/badge.svg [2]: https://github.com/vmware-tanzu/velero/actions?query=workflow%3A"Main+CI" From 6a551e546e19680857208489bee4839ce99848fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 9 May 2022 16:25:52 +0800 Subject: [PATCH 138/366] Make garbage collection for expired backups configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make garbage collection for expired backups configurable Fixes #4875 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4897-ywk253100 | 1 + pkg/cmd/cli/install/install.go | 7 +++++++ pkg/cmd/server/server.go | 3 +++ pkg/controller/gc_controller.go | 10 ++++++++-- pkg/controller/gc_controller_test.go | 3 +++ pkg/install/deployment.go | 11 +++++++++++ pkg/install/deployment_test.go | 4 ++++ pkg/install/resources.go | 2 ++ 8 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/4897-ywk253100 diff --git a/changelogs/unreleased/4897-ywk253100 b/changelogs/unreleased/4897-ywk253100 new file mode 100644 index 0000000000..a5f5a6809f --- /dev/null +++ b/changelogs/unreleased/4897-ywk253100 @@ -0,0 +1 @@ +Make garbage collection for expired backups configurable \ No newline at end of file diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index dd7b9a914c..0f1819fa86 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -67,6 +67,7 @@ type InstallOptions struct { Wait bool UseVolumeSnapshots bool DefaultResticMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration Plugins flag.StringArray NoDefaultBackupLocation bool CRDsOnly bool @@ -103,6 +104,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "Create restic daemonset. Optional.") flags.BoolVar(&o.Wait, "wait", o.Wait, "Wait for Velero deployment to be ready. Optional.") flags.DurationVar(&o.DefaultResticMaintenanceFrequency, "default-restic-prune-frequency", o.DefaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default. Optional.") + flags.DurationVar(&o.GarbageCollectionFrequency, "garbage-collection-frequency", o.GarbageCollectionFrequency, "How often the garbage collection runs for expired backups.(default 1h)") flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment") flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "Only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.") flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "File containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") @@ -187,6 +189,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { BSLConfig: o.BackupStorageConfig.Data(), VSLConfig: o.VolumeSnapshotConfig.Data(), DefaultResticMaintenanceFrequency: o.DefaultResticMaintenanceFrequency, + GarbageCollectionFrequency: o.GarbageCollectionFrequency, Plugins: o.Plugins, NoDefaultBackupLocation: o.NoDefaultBackupLocation, CACertData: caCertData, @@ -395,5 +398,9 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact return errors.New("--default-restic-prune-frequency must be non-negative") } + if o.GarbageCollectionFrequency < 0 { + return errors.New("--garbage-collection-frequency must be non-negative") + } + return nil } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index b9d4f18dbb..d230e08b0b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -121,6 +121,7 @@ type serverConfig struct { profilerAddress string formatFlag *logging.FormatFlag defaultResticMaintenanceFrequency time.Duration + garbageCollectionFrequency time.Duration defaultVolumesToRestic bool } @@ -214,6 +215,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "How long to wait on persistent volumes and namespaces to terminate during a restore before timing out.") command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "How long to wait by default before backups can be garbage collected.") command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default.") + command.Flags().DurationVar(&config.garbageCollectionFrequency, "garbage-collection-frequency", config.garbageCollectionFrequency, "How often garbage collection is run for expired backups.") command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "Backup all volumes with restic by default.") return command @@ -669,6 +671,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.sharedInformerFactory.Velero().V1().DeleteBackupRequests().Lister(), s.veleroClient.VeleroV1(), s.mgr.GetClient(), + s.config.garbageCollectionFrequency, ) return controllerRunInfo{ diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index d365dcf411..05c28ad30f 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -39,7 +39,7 @@ import ( ) const ( - GCSyncPeriod = 60 * time.Minute + defaultGCFrequency = 60 * time.Minute garbageCollectionFailure = "velero.io/gc-failure" gcFailureBSLNotFound = "BSLNotFound" gcFailureBSLCannotGet = "BSLCannotGet" @@ -54,6 +54,7 @@ type gcController struct { deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter kbClient client.Client + frequency time.Duration clock clock.Clock } @@ -65,6 +66,7 @@ func NewGCController( deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, kbClient client.Client, + frequency time.Duration, ) Interface { c := &gcController{ genericController: newGenericController(GarbageCollection, logger), @@ -76,7 +78,11 @@ func NewGCController( } c.syncHandler = c.processQueueItem - c.resyncPeriod = GCSyncPeriod + c.resyncPeriod = frequency + if c.resyncPeriod < 0 { + c.resyncPeriod = defaultGCFrequency + } + logger.Infof("Garbage collection frequency: %s", c.resyncPeriod.String()) c.resyncFunc = c.enqueueAllBackups backupInformer.Informer().AddEventHandler( diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 3597a3c8c2..c373064e48 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -53,6 +53,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) { sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), nil, + defaultGCFrequency, ).(*gcController) ) @@ -114,6 +115,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), nil, + defaultGCFrequency, ).(*gcController) keys := make(chan string) @@ -262,6 +264,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), fakeClient, + defaultGCFrequency, ).(*gcController) controller.clock = fakeClock diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 191835c22c..ebeb936f61 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -40,6 +40,7 @@ type podTemplateConfig struct { resources corev1.ResourceRequirements withSecret bool defaultResticMaintenanceFrequency time.Duration + garbageCollectionFrequency time.Duration plugins []string features []string defaultVolumesToRestic bool @@ -104,6 +105,12 @@ func WithDefaultResticMaintenanceFrequency(val time.Duration) podTemplateOption } } +func WithGarbageCollectionFrequency(val time.Duration) podTemplateOption { + return func(c *podTemplateConfig) { + c.garbageCollectionFrequency = val + } +} + func WithPlugins(plugins []string) podTemplateOption { return func(c *podTemplateConfig) { c.plugins = plugins @@ -275,6 +282,10 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-restic-prune-frequency=%v", c.defaultResticMaintenanceFrequency)) } + if c.garbageCollectionFrequency > 0 { + deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--garbage-collection-frequency=%v", c.garbageCollectionFrequency)) + } + if len(c.plugins) > 0 { for _, image := range c.plugins { container := *builder.ForPluginContainer(image, pullPolicy).Result() diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index 85f067488c..616db03246 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -50,6 +50,10 @@ func TestDeployment(t *testing.T) { assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--default-restic-prune-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) + deploy = Deployment("velero", WithGarbageCollectionFrequency(24*time.Hour)) + assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) + assert.Equal(t, "--garbage-collection-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) + deploy = Deployment("velero", WithFeatures([]string{"EnableCSI", "foo", "bar", "baz"})) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--features=EnableCSI,foo,bar,baz", deploy.Spec.Template.Spec.Containers[0].Args[1]) diff --git a/pkg/install/resources.go b/pkg/install/resources.go index f4c4d268a3..78a9ed6891 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -226,6 +226,7 @@ type VeleroOptions struct { BSLConfig map[string]string VSLConfig map[string]string DefaultResticMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration Plugins []string NoDefaultBackupLocation bool CACertData []byte @@ -285,6 +286,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { WithResources(o.VeleroPodResources), WithSecret(secretPresent), WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency), + WithGarbageCollectionFrequency(o.GarbageCollectionFrequency), } if len(o.Features) > 0 { From 989a1e3ebb0bf6213d26395511b0ca1aa65da4e4 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 6 May 2022 16:24:15 +0800 Subject: [PATCH 139/366] Refactor BSL controller with periodical enqueue source. Add filter functions for PeriodicalEnqueueSource. Move BSL's valication frequency check test case to PeriodicalEnqueueSource's test. Signed-off-by: Xun Jiang --- changelogs/unreleased/4894-jxun | 1 + .../backup_storage_location_controller.go | 166 +++++++++++------- ...backup_storage_location_controller_test.go | 101 ++--------- pkg/util/kube/periodical_enqueue_source.go | 26 ++- .../kube/periodical_enqueue_source_test.go | 50 +++++- 5 files changed, 177 insertions(+), 167 deletions(-) create mode 100644 changelogs/unreleased/4894-jxun diff --git a/changelogs/unreleased/4894-jxun b/changelogs/unreleased/4894-jxun new file mode 100644 index 0000000000..dcac05202d --- /dev/null +++ b/changelogs/unreleased/4894-jxun @@ -0,0 +1 @@ +Refactor BSL controller with periodical enqueue source \ No newline at end of file diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index e6abd9b356..627247be10 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -29,11 +29,18 @@ import ( "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + "github.com/vmware-tanzu/velero/pkg/util/kube" +) + +const ( + backupStorageLocationSyncPeriod = 1 * time.Minute ) // BackupStorageLocationReconciler reconciles a BackupStorageLocation object @@ -53,96 +60,92 @@ type BackupStorageLocationReconciler struct { // +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations/status,verbs=get;update;patch func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithField("controller", BackupStorageLocation) + var unavailableErrors []string + var location velerov1api.BackupStorageLocation - log.Debug("Validating availability of backup storage locations.") + log := r.Log.WithField("controller", BackupStorageLocation).WithField(BackupStorageLocation, req.NamespacedName.String()) + log.Debug("Validating availability of BackupStorageLocation") locationList, err := storage.ListBackupStorageLocations(r.Ctx, r.Client, req.Namespace) if err != nil { - log.WithError(err).Error("No backup storage locations found, at least one is required") - return ctrl.Result{}, err + log.WithError(err).Error("No BackupStorageLocations found, at least one is required") + return ctrl.Result{}, nil } pluginManager := r.NewPluginManager(log) defer pluginManager.CleanupClients() var defaultFound bool - for _, location := range locationList.Items { - if location.Spec.Default { + for _, bsl := range locationList.Items { + if bsl.Spec.Default { defaultFound = true - break + } + if bsl.Name == req.Name && bsl.Namespace == req.Namespace { + location = bsl } } - var unavailableErrors []string - var anyVerified bool - for i := range locationList.Items { - location := &locationList.Items[i] - isDefault := location.Spec.Default - log := r.Log.WithField("controller", BackupStorageLocation).WithField(BackupStorageLocation, location.Name) - - // TODO(2.0) remove this check since the server default will be deprecated - if !defaultFound && location.Name == r.DefaultBackupLocationInfo.StorageLocation { - // For backward-compatible, to configure the backup storage location as the default if - // none of the BSLs be marked as the default and the BSL name matches against the - // "velero server --default-backup-storage-location". - isDefault = true - defaultFound = true - } + if location.Name == "" || location.Namespace == "" { + log.WithError(err).Error("BackupStorageLocation is not found") + return ctrl.Result{}, nil + } - if !storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.DefaultBackupLocationInfo.ServerValidationFrequency, log) { - log.Debug("Validation not required, skipping...") - continue - } + isDefault := location.Spec.Default - anyVerified = true + // TODO(2.0) remove this check since the server default will be deprecated + if !defaultFound && location.Name == r.DefaultBackupLocationInfo.StorageLocation { + // For backward-compatible, to configure the backup storage location as the default if + // none of the BSLs be marked as the default and the BSL name matches against the + // "velero server --default-backup-storage-location". + isDefault = true + defaultFound = true + } - func() { - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(location, r.Client) + func() { + // Initialize the patch helper. + patchHelper, err := patch.NewHelper(&location, r.Client) + if err != nil { + log.WithError(err).Error("Error getting a patch helper to update BackupStorageLocation") + return + } + defer func() { + location.Status.LastValidationTime = &metav1.Time{Time: time.Now().UTC()} if err != nil { - log.WithError(err).Error("Error getting a patch helper to update this resource") - return + log.Info("BackupStorageLocation is invalid, marking as unavailable") + err = errors.Wrapf(err, "BackupStorageLocation %q is unavailable", location.Name) + unavailableErrors = append(unavailableErrors, err.Error()) + location.Status.Phase = velerov1api.BackupStorageLocationPhaseUnavailable + location.Status.Message = err.Error() + } else { + log.Info("BackupStorageLocations is valid, marking as available") + location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable + location.Status.Message = "" } - defer func() { - location.Status.LastValidationTime = &metav1.Time{Time: time.Now().UTC()} - if err != nil { - log.Info("Backup storage location is invalid, marking as unavailable") - err = errors.Wrapf(err, "Backup storage location %q is unavailable", location.Name) - unavailableErrors = append(unavailableErrors, err.Error()) - location.Status.Phase = velerov1api.BackupStorageLocationPhaseUnavailable - location.Status.Message = err.Error() - } else { - log.Info("Backup storage location valid, marking as available") - location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable - location.Status.Message = "" - } - if err := patchHelper.Patch(r.Ctx, location); err != nil { - log.WithError(err).Error("Error updating backup storage location phase") - } - }() - - backupStore, err := r.BackupStoreGetter.Get(location, pluginManager, log) - if err != nil { - err = errors.Wrapf(err, "Error getting a backup store") - return + if err := patchHelper.Patch(r.Ctx, &location); err != nil { + log.WithError(err).Error("Error updating BackupStorageLocation phase") } + }() - // updates the default backup location - location.Spec.Default = isDefault + backupStore, err := r.BackupStoreGetter.Get(&location, pluginManager, log) + if err != nil { + log.WithError(err).Error("Error getting a backup store") + return + } - log.Info("Validating backup storage location") - err = backupStore.IsValid() - }() - } + log.Info("Validating BackupStorageLocation") + err = backupStore.IsValid() + if err != nil { + log.WithError(err).Error("fail to validate backup store") + return + } - if !anyVerified { - log.Debug("No backup storage locations needed to be validated") - } + // updates the default backup location + location.Spec.Default = isDefault + }() r.logReconciledPhase(defaultFound, locationList, unavailableErrors) - return ctrl.Result{Requeue: true}, nil + return ctrl.Result{}, nil } func (r *BackupStorageLocationReconciler) logReconciledPhase(defaultFound bool, locationList velerov1api.BackupStorageLocationList, errs []string) { @@ -169,21 +172,48 @@ func (r *BackupStorageLocationReconciler) logReconciledPhase(defaultFound bool, if numUnavailable+numUnknown == len(locationList.Items) { // no available BSL if len(errs) > 0 { - log.Errorf("Current backup storage locations available/unavailable/unknown: %v/%v/%v, %s)", numAvailable, numUnavailable, numUnknown, strings.Join(errs, "; ")) + log.Errorf("Current BackupStorageLocations available/unavailable/unknown: %v/%v/%v, %s)", numAvailable, numUnavailable, numUnknown, strings.Join(errs, "; ")) } else { - log.Errorf("Current backup storage locations available/unavailable/unknown: %v/%v/%v)", numAvailable, numUnavailable, numUnknown) + log.Errorf("Current BackupStorageLocations available/unavailable/unknown: %v/%v/%v)", numAvailable, numUnavailable, numUnknown) } } else if numUnavailable > 0 { // some but not all BSL unavailable - log.Warnf("Unavailable backup storage locations detected: available/unavailable/unknown: %v/%v/%v, %s)", numAvailable, numUnavailable, numUnknown, strings.Join(errs, "; ")) + log.Warnf("Unavailable BackupStorageLocations detected: available/unavailable/unknown: %v/%v/%v, %s)", numAvailable, numUnavailable, numUnknown, strings.Join(errs, "; ")) } if !defaultFound { - log.Warn("There is no existing backup storage location set as default. Please see `velero backup-location -h` for options.") + log.Warn("There is no existing BackupStorageLocation set as default. Please see `velero backup-location -h` for options.") } } func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) error { + g := kube.NewPeriodicalEnqueueSource( + r.Log, + mgr.GetClient(), + &velerov1api.BackupStorageLocationList{}, + backupStorageLocationSyncPeriod, + // Add filter function to enqueue BSL per ValidationFrequency setting. + func(object client.Object) bool { + location := object.(*velerov1api.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.DefaultBackupLocationInfo.ServerValidationFrequency, r.Log.WithField("controller", BackupStorageLocation)) + }, + ) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.BackupStorageLocation{}). + // Handle BSL's creation event and spec update event to let changed BSL got validation immediately. + WithEventFilter(predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return true + }, + UpdateFunc: func(ue event.UpdateEvent) bool { + return ue.ObjectNew.GetGeneration() != ue.ObjectOld.GetGeneration() + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + GenericFunc: func(ge event.GenericEvent) bool { + return false + }, + }). + Watches(g, nil). Complete(r) } diff --git a/pkg/controller/backup_storage_location_controller_test.go b/pkg/controller/backup_storage_location_controller_test.go index 82a6cc2415..75ad691a25 100644 --- a/pkg/controller/backup_storage_location_controller_test.go +++ b/pkg/controller/backup_storage_location_controller_test.go @@ -81,7 +81,7 @@ var _ = Describe("Backup Storage Location Reconciler", func() { Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) r := BackupStorageLocationReconciler{ Ctx: ctx, - Client: fake.NewFakeClientWithScheme(scheme.Scheme, locations), + Client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ StorageLocation: "location-1", ServerValidationFrequency: 0, @@ -91,18 +91,17 @@ var _ = Describe("Backup Storage Location Reconciler", func() { Log: velerotest.NewLogger(), } - actualResult, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: types.NamespacedName{Namespace: "ns-1"}, - }) - - Expect(actualResult).To(BeEquivalentTo(ctrl.Result{Requeue: true})) - Expect(err).To(BeNil()) - // Assertions for i, location := range locations.Items { + actualResult, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: location.Namespace, Name: location.Name}, + }) + Expect(actualResult).To(BeEquivalentTo(ctrl.Result{})) + Expect(err).To(BeNil()) + key := client.ObjectKey{Name: location.Name, Namespace: location.Namespace} instance := &velerov1api.BackupStorageLocation{} - err := r.Client.Get(ctx, key, instance) + err = r.Client.Get(ctx, key, instance) Expect(err).To(BeNil()) Expect(instance.Spec.Default).To(BeIdenticalTo(tests[i].expectedIsDefault)) Expect(instance.Status.Phase).To(BeIdenticalTo(tests[i].expectedPhase)) @@ -147,7 +146,7 @@ var _ = Describe("Backup Storage Location Reconciler", func() { Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) r := BackupStorageLocationReconciler{ Ctx: ctx, - Client: fake.NewFakeClientWithScheme(scheme.Scheme, locations), + Client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ StorageLocation: "default", ServerValidationFrequency: 0, @@ -157,91 +156,19 @@ var _ = Describe("Backup Storage Location Reconciler", func() { Log: velerotest.NewLogger(), } - actualResult, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: types.NamespacedName{Namespace: "ns-1"}, - }) - - Expect(actualResult).To(BeEquivalentTo(ctrl.Result{Requeue: true})) - Expect(err).To(BeNil()) - // Assertions for i, location := range locations.Items { - key := client.ObjectKey{Name: location.Name, Namespace: location.Namespace} - instance := &velerov1api.BackupStorageLocation{} - err := r.Client.Get(ctx, key, instance) + actualResult, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: location.Namespace, Name: location.Name}, + }) + Expect(actualResult).To(BeEquivalentTo(ctrl.Result{})) Expect(err).To(BeNil()) - Expect(instance.Spec.Default).To(BeIdenticalTo(tests[i].expectedIsDefault)) - } - }) - - It("Should not patch a backup storage location object status phase if the location's validation frequency is specifically set to zero", func() { - tests := []struct { - backupLocation *velerov1api.BackupStorageLocation - isValidError error - expectedIsDefault bool - expectedPhase velerov1api.BackupStorageLocationPhase - wantErr bool - }{ - { - backupLocation: builder.ForBackupStorageLocation("ns-1", "location-1").ValidationFrequency(0).LastValidationTime(time.Now()).Result(), - isValidError: nil, - expectedIsDefault: false, - expectedPhase: "", - wantErr: false, - }, - { - backupLocation: builder.ForBackupStorageLocation("ns-1", "location-2").ValidationFrequency(0).LastValidationTime(time.Now()).Result(), - isValidError: nil, - expectedIsDefault: false, - expectedPhase: "", - wantErr: false, - }, - } - // Setup - var ( - pluginManager = &pluginmocks.Manager{} - backupStores = make(map[string]*persistencemocks.BackupStore) - ) - pluginManager.On("CleanupClients").Return(nil) - - locations := new(velerov1api.BackupStorageLocationList) - for i, test := range tests { - location := test.backupLocation - locations.Items = append(locations.Items, *location) - backupStores[location.Name] = &persistencemocks.BackupStore{} - backupStores[location.Name].On("IsValid").Return(tests[i].isValidError) - } - - // Setup reconciler - Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - r := BackupStorageLocationReconciler{ - Ctx: ctx, - Client: fake.NewFakeClientWithScheme(scheme.Scheme, locations), - DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ - StorageLocation: "default", - ServerValidationFrequency: 0, - }, - NewPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - BackupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), - Log: velerotest.NewLogger(), - } - - actualResult, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: types.NamespacedName{Namespace: "ns-1"}, - }) - - Expect(actualResult).To(BeEquivalentTo(ctrl.Result{Requeue: true})) - Expect(err).To(BeNil()) - - // Assertions - for i, location := range locations.Items { key := client.ObjectKey{Name: location.Name, Namespace: location.Namespace} instance := &velerov1api.BackupStorageLocation{} - err := r.Client.Get(ctx, key, instance) + err = r.Client.Get(ctx, key, instance) Expect(err).To(BeNil()) Expect(instance.Spec.Default).To(BeIdenticalTo(tests[i].expectedIsDefault)) - Expect(instance.Status.Phase).To(BeIdenticalTo(tests[i].expectedPhase)) } }) }) diff --git a/pkg/util/kube/periodical_enqueue_source.go b/pkg/util/kube/periodical_enqueue_source.go index bb89295b47..20b658c61a 100644 --- a/pkg/util/kube/periodical_enqueue_source.go +++ b/pkg/util/kube/periodical_enqueue_source.go @@ -34,12 +34,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" ) -func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, objList client.ObjectList, period time.Duration) *PeriodicalEnqueueSource { +func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, objList client.ObjectList, period time.Duration, filters ...func(object client.Object) bool) *PeriodicalEnqueueSource { return &PeriodicalEnqueueSource{ - logger: logger.WithField("resource", reflect.TypeOf(objList).String()), - Client: client, - objList: objList, - period: period, + logger: logger.WithField("resource", reflect.TypeOf(objList).String()), + Client: client, + objList: objList, + period: period, + filterFuncs: filters, } } @@ -48,9 +49,10 @@ func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, // the reconcile logic periodically type PeriodicalEnqueueSource struct { client.Client - logger logrus.FieldLogger - objList client.ObjectList - period time.Duration + logger logrus.FieldLogger + objList client.ObjectList + period time.Duration + filterFuncs []func(object client.Object) bool } func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHandler, q workqueue.RateLimitingInterface, pre ...predicate.Predicate) error { @@ -70,6 +72,14 @@ func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHand p.logger.Error("%s's type isn't metav1.Object", object.GetObjectKind().GroupVersionKind().String()) return nil } + for _, filter := range p.filterFuncs { + if filter != nil { + if enqueueObj := filter(object.(client.Object)); !enqueueObj { + p.logger.Debugf("skip enqueue object %s/%s due to filter function.", obj.GetNamespace(), obj.GetName()) + return nil + } + } + } q.Add(ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: obj.GetNamespace(), diff --git a/pkg/util/kube/periodical_enqueue_source_test.go b/pkg/util/kube/periodical_enqueue_source_test.go index 2264729f7f..3621533477 100644 --- a/pkg/util/kube/periodical_enqueue_source_test.go +++ b/pkg/util/kube/periodical_enqueue_source_test.go @@ -26,8 +26,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/util/workqueue" + crclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/vmware-tanzu/velero/internal/storage" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -43,7 +45,7 @@ func TestStart(t *testing.T) { // no resources time.Sleep(1 * time.Second) - require.Equal(t, queue.Len(), 0) + require.Equal(t, 0, queue.Len()) // contain one resource require.Nil(t, client.Create(ctx, &velerov1.Schedule{ @@ -52,13 +54,53 @@ func TestStart(t *testing.T) { }, })) time.Sleep(2 * time.Second) - require.Equal(t, queue.Len(), 1) + require.Equal(t, 1, queue.Len()) // context canceled, the enqueue source shouldn't run anymore item, _ := queue.Get() queue.Forget(item) - require.Equal(t, queue.Len(), 0) + require.Equal(t, 0, queue.Len()) cancelFunc() time.Sleep(2 * time.Second) - require.Equal(t, queue.Len(), 0) + require.Equal(t, 0, queue.Len()) +} + +func TestFilter(t *testing.T) { + require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) + + ctx, cancelFunc := context.WithCancel(context.TODO()) + client := (&fake.ClientBuilder{}).Build() + queue := workqueue.NewRateLimitingQueue(workqueue.DefaultItemBasedRateLimiter()) + source := NewPeriodicalEnqueueSource( + logrus.WithContext(ctx), + client, + &velerov1.BackupStorageLocationList{}, + 1*time.Second, + func(object crclient.Object) bool { + location := object.(*velerov1.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, 1*time.Minute, logrus.WithContext(ctx).WithField("BackupStorageLocation", location.Name)) + }, + ) + + require.Nil(t, source.Start(ctx, nil, queue)) + + // Should not patch a backup storage location object status phase + // if the location's validation frequency is specifically set to zero + require.Nil(t, client.Create(ctx, &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "location1", + Namespace: "default", + }, + Spec: velerov1.BackupStorageLocationSpec{ + ValidationFrequency: &metav1.Duration{Duration: 0}, + }, + Status: velerov1.BackupStorageLocationStatus{ + LastValidationTime: &metav1.Time{Time: time.Now()}, + }, + })) + time.Sleep(2 * time.Second) + + require.Equal(t, 0, queue.Len()) + + cancelFunc() } From bfdb68a35a51e86ca8839567d15a63de8a673201 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Tue, 15 Feb 2022 09:52:47 -0500 Subject: [PATCH 140/366] add multiple label selector support to backup API Signed-off-by: Shubham Pampattiwar remove backup CLI bits Signed-off-by: Shubham Pampattiwar labelselectors spec option for velero restore Signed-off-by: Shubham Pampattiwar add changelog file Signed-off-by: Shubham Pampattiwar update spec name to OrLabelSelectors Signed-off-by: Shubham Pampattiwar minor fixes Signed-off-by: Shubham Pampattiwar add validations for labelSelector and orLabelSelectors Signed-off-by: Shubham Pampattiwar update crds.gp after fixing conflicts Signed-off-by: Shubham Pampattiwar fix CI and add unit tests Signed-off-by: Shubham Pampattiwar updated OrLabelSelector spec description and added validation failure unit tests Signed-off-by: Shubham Pampattiwar add comments and change log level Signed-off-by: Shubham Pampattiwar update site docs Signed-off-by: Shubham Pampattiwar wrap pager client calls in a function Signed-off-by: Shubham Pampattiwar resolve confilcts and update crds.go Signed-off-by: Shubham Pampattiwar rebase and update crds.go Signed-off-by: Shubham Pampattiwar combine listing items for a given label Signed-off-by: Shubham Pampattiwar --- .../unreleased/4650-shubham-pampattiwar | 1 + config/crd/v1/bases/velero.io_backups.yaml | 55 ++++++++ config/crd/v1/bases/velero.io_restores.yaml | 55 ++++++++ config/crd/v1/bases/velero.io_schedules.yaml | 55 ++++++++ config/crd/v1/crds/crds.go | 6 +- pkg/apis/velero/v1/backup.go | 9 ++ pkg/apis/velero/v1/restore.go | 9 ++ pkg/apis/velero/v1/zz_generated.deepcopy.go | 22 ++++ pkg/backup/backup_test.go | 29 +++++ pkg/backup/item_collector.go | 122 ++++++++++++------ pkg/builder/backup_builder.go | 6 + pkg/builder/restore_builder.go | 6 + pkg/controller/backup_controller.go | 5 + pkg/controller/backup_controller_test.go | 7 + pkg/controller/restore_controller.go | 5 + pkg/controller/restore_controller_test.go | 9 ++ pkg/restore/restore.go | 34 +++++ pkg/restore/restore_test.go | 30 +++++ site/content/docs/main/api-types/backup.md | 7 + site/content/docs/main/api-types/restore.md | 7 + 20 files changed, 439 insertions(+), 40 deletions(-) create mode 100644 changelogs/unreleased/4650-shubham-pampattiwar diff --git a/changelogs/unreleased/4650-shubham-pampattiwar b/changelogs/unreleased/4650-shubham-pampattiwar new file mode 100644 index 0000000000..59a7fd4365 --- /dev/null +++ b/changelogs/unreleased/4650-shubham-pampattiwar @@ -0,0 +1 @@ +Add multiple label selector support to Velero Backup and Restore APIs diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index b0d3f0d81c..03bfed1ffb 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -314,6 +314,61 @@ spec: type: string type: object type: object + orLabelSelectors: + description: OrLabelSelectors is list of metav1.LabelSelector to filter + with when adding individual objects to the backup. If multiple provided + they will be joined by the OR operator. LabelSelector as well as + OrLabelSelectors cannot co-exist in backup request, only one of + them can be used. + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + nullable: true + type: array orderedResources: additionalProperties: type: string diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 5089888014..68d1aefb02 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -1708,6 +1708,61 @@ spec: included in the map will be restored into namespaces of the same name. type: object + orLabelSelectors: + description: OrLabelSelectors is list of metav1.LabelSelector to filter + with when restoring individual objects from the backup. If multiple + provided they will be joined by the OR operator. LabelSelector as + well as OrLabelSelectors cannot co-exist in restore request, only + one of them can be used + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + nullable: true + type: array preserveNodePorts: description: PreserveNodePorts specifies whether to restore old nodePorts from backup. diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index b090c08c0f..de2734d40c 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -343,6 +343,61 @@ spec: type: string type: object type: object + orLabelSelectors: + description: OrLabelSelectors is list of metav1.LabelSelector + to filter with when adding individual objects to the backup. + If multiple provided they will be joined by the OR operator. + LabelSelector as well as OrLabelSelectors cannot co-exist in + backup request, only one of them can be used. + items: + description: A label selector is a label query over a set of + resources. The result of matchLabels and matchExpressions + are ANDed. An empty label selector matches all objects. A + null label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + nullable: true + type: array orderedResources: additionalProperties: type: string diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index c3df834a27..6ead090277 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,15 +29,15 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3\x86C\xbb_Q\xd8ť\xfd\xd4\xed\x1br+\x9e\xdb\xfe\xba\x9c\xf0\xaf\x96\xf0\x936N\x1al߈:CH\xf3\xc4\x17\x99nO\x85\xd1ה\xce1\xed\xf6\x8d\x1b,\xe8U\x0f'>\xaet\x13\x9c\xc1\xf1X\xa8\x14\u007f\xd1\xe6\x06J\xa9\xe8\x1f\xf2\xf89\t\x12\a_\x84??d\xb1\x80\xf7\x03\xf5ij':\x86\x14㆘rU\xc7\xcf\xcbW\xf0\x05\xcf=+\u007f\x04\x8e9\xa7\xf9ƞ\x90\xa2.\x1b\xf5`\xf4\x9e\xa2\xf4\x91\x1f\x1b\xe55\xf2ۃ0N\x8a\xa28\xf9I&g\x1f\xf9\xe1\x13\x92\xe1\x9a\xf4^\xc6\xc9\x1a\xb0\\\xa2l\xe8֦\x04\xa4\xf2\x92\xc0\x87\xd7[]\xbb\x9e*iUшX\xc49\xd7\xf0E;\x8c\x19cهI\xca\x17\xad[\xe1n\xa7\x8d\xf3\x19\x88\xd5\n\xe4.xC#piO\xf0)\x89\u007f\xd1\t\xa4k3u\xad\xf4r\xa0cx\x13\xf2\xb5\xefR\x9c\xfcA\xa6\xc82r\xb6\xf1\xd6:Q\x8c\xe8\xb7\xdft0\xc2n'I\x1f\xe6\xff6⇝\x11|\xd3\xed\xdf\xdc&i\xac\x1b\x83\xf3\x94\xe3B\x13\xaf\xdbG-\x1dp\xf9\x01*x5\xd29ҧ\xddc$p\xa4A\x8b\x02,锉\xb7C\xe64;\xffN\xb6w3\x9d\xbe\xec\xc77M\xe7)\xd3\x1d\x16\xa7\x89-[&\xc1IJ|!\xa3\xb4q,\xb12;\b\xb5'\xa12\xba\xde\x1f\xa2\\NX\xc6\t\xb8yMHAU\xd4{\x12\xf5p\f\xe3j\xa3:)\x98p0\x93w\xd0\x15\xd9\xcb$\xa6!\x11\x1d_\x15\xbc\r\xaf\x81\xacvF\x97\xab\xc0\vNQ݄Ԉ\x91\x9a\xfc\u007f\n\xe4'\x80\xb6\xd7\xeeY\f\xaa\n\x15\b\x1b\xf0I\xa8\xb2\x9cg\xeb\\\x9e\xc2\t\xe3R\xa3\x8a\xc7^煀\x82!\x8f\xe3\xfb\x18\x12?\xbe\xda\xf4~\xf8\xbe\xe3\rX\xa9\u20c6>\xb1\xe4E\xc1R\x9ca\x90c\xf5ѣ\xb1\xb3\b\xa1\x17\x0f\xf4\xd1\xff\xeb\x86\x02\xc7\xc6\xc2|N\xf1)\x9f\a\xdd\aG\xf6\xb4\xcb[\x88\xc1\x0f\x1c\xa1\xc7\x1f\xe4Ο\xd9e\x84\xf5\x1f\xff\xe6G\xf1\xc7$\x9f\xe5ì\xbb\u009eH\xe3w\xc0'\xac\ffb4\xf0\x00x(\x90\xfc\b\x8b\xd8\xf7\x84>\\\xe4\xf2\x1e\xdf\x16Ľg\x04\x17\xdf\xda|\x9f\xb8\xe6\xf8\xb6\xd8\xed\x9b\x05nﻺWa\x94T\xfb\xa5=\xf6\xef\xa1\xdbH\xe4\x16 \x8c\xc4n#\xcbh\xa2\xb9\xc5ح\x13\xbaE\x1c'\x9e\xc0\x1b\x84s\xef\x14\xbc\x8dځ\xb3\x8f\xac@\xf3\xce\xde\x0e3\x85/mBLd\x19\x92\xb8~\x19\xbe\xa9{}\xcd\u007f\xc4gs\xf9\xcfL+on\xed\x1d\xfc\xc7\u007f^Aȸ>\xc7\xf7q\xe9\xe3\xff\x05\x00\x00\xff\xffj\x17\xd2\xcb\u007fX\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05Jq\xfd$]Dʛ\\\xf2\xbb\xdbJ=)eWvT\xf6jU+e\xfd\xa4\x1c\x9f\x0f\x9ci\x928\x81\xc0\x04\xc0P\xe2\x9d\xef\xbb?\x85\x060/\xe4\x90\"0Ծ\u0603\xab\xbax\xa9\x99\x1e\xa0\xd1\xe8wtӂ\xbd\a\xa5\x99\x14/\t-\x18<\x1a\x10\xf6_z|\xffoz\xcc\xe4\xf9\xf2\xc5\x17\xf7L\xe4/ɫR\x1b\xb9x\aZ\x96*\x83\xd70e\x82\x19&\xc5\x17\v04\xa7\x86\xbe\xfc\x82\x10*\x844\xd4\xfe\xac\xed?\tɤ0Jr\x0ej4\x031\xbe/'0)\x19\xcfA!\xf0\xf0\xe9\xe5W\xe3\xff\u007f\xfc\xd5\x17\x84d\n\xf0\xf5;\xb6\x00m\xe8\xa2xID\xc9\xf9\x17\x84\b\xba\x80\x97D\x816R\x81\x1e/\x81\x83\x92c&\xbf\xd0\x05d\xf6c3%\xcb\xe2%\xa9\xff\xe0\xde\xf1\x13q\x8bx\xe7^\xc7_8\xd3\xe6\xdb\xe6\xaf\xdf1m\xf0/\x05/\x15\xe5\xf5\xc7\xf0G\xcdĬ\xe4TU?\u007fA\x88\xced\x01/ɵ\xfdLA3ȿ į\t?;\xf2\xb3^\xbep \xb29,\xa8\x9b\x0f!\xb2\x00qqs\xf5\xfe\xf7\xb7\xad\x9f\t\xc9Ag\x8a\x15\x061\xe3\xe7F\x98&\x94\xbcǵ\xd9\t\xe0&\x103\xa7\x86((\x14h\x10F\x133\aB\x8b\x82\xb3\f\x91XA$DN\xab\xb74\x99*\xb9\xa8\xa1Mhv_\x16\xc4HB\x89\xa1j\x06\x86|[N@\t0\xa0I\xc6Km@\x8d+X\x85\x92\x05(\xc3\x02b\xddh\xd0Q\xe3\u05f5\xb5\x1c\xdb座Hn\t\bܔ=\xca \xf7\x18\xb2\xb35s\xa6륭/\xc7/\x89\n\"'\xff\x05\x99\x19\x93[P\x16\f\xd1sY\xf2\xdc\xd2\xdd\x12\x94EN&g\x82\xfdw\x05[ۅڏrj\xc0\xefw=\x980\xa0\x04\xe5dIy\tg\x84\x8a\x9c,\xe8\x8a(\xb0_!\xa5h\xc0\xc3G\xf4\x98\xbc\xc1\xed\x11S\xf9\x92̍)\xf4\xcb\xf3\xf3\x193\xe1\xfcdr\xb1(\x053\xabs<\nlR\x1a\xa9\xf4y\x0eK\xe0\xe7\x9a\xcdFTesf 3\xa5\x82sZ\xb0\x11N]\xe0\x19\x1a/\xf2\xdfT\xdbvܚ\xabYY\xca\xd3F11k\xfc\x01\xc9|\xc7\x0eX\x82w\xb4\xe4^u\xab\xa8\x11m\u007f\xb2\xd8ywy{פ3\xa6ױ\x8fxo\x10_\xbd\x05\x16aLLA\xb9MDj\xb30A\xe4\x85d\xc2\xe0?2\xce@\xac\xa3_\x97\x93\x053v\xdf\xffY\x82\xb6\x04-\xc7\xe4\x152\x152\x01R\x1695\x90\x8fɕ \xaf\xe8\x02\xf8+\xaa\xe1\xd97\xc0bZ\x8f,b\xf7ۂ&?\\\u007f\xd8a\xad\xf1\x87\xc0\xbc\xb6\xec\x97?\xfd\xb7\x05d\xad\x13c_cS\u007f\xcc\xc9T\xaa\x16s\xb0\xaf\x8c[@\xbb\x0f\xad\x1d\xee\xf4[\x0e\xb6\xfe\x97\xb5\xa9\xfc\xa5z\xd0ҏ\x9dD)\xd8?K@\x16\xe7N,l\xb0\x94\r\x90$\xcc\x0f\xc9b\xbc\xf1\xf7-8\xb5\x03\x1e3^\xe6\x90W\xdcvc-k3\xbe\xdcx\x01\xc5\x11e\xc2ҿe\xffvڢ\xfe\xabe\xa7\x1d3\xa6\n\x88\xa5@&\x1c<\xc2\x04.\xb6\x13\xd3v0\x03\x8b\x8e\xc9\xed\\\x1dA9G'\x1c^\x12\xa3J\u0602\x19\xaa\x14]mAL\x90\xcd\xfb\xe2\xa5z\xde3\x04\xce2h\n\n\x87\x1a'd\xa8ڜ\x11\xf9ı´ega\x957\x92\xb3l\xf5$j\xba^\n\xc7\xcd\x1f\xbe@\xc1\x13\x98\xd3%\x93\xa5\xeaX\x93=\x92\xf6\xd9\xfbZ\x92\xd6\xdcTZf\xe6\xa1\xe4i+\xee\xc4\xd6\\\xca\xfb\xa76\xff\xaf\xf6\x99\x9am\x93\fպ\xb0\x16\xe5\xb7\xdbK\xd1\t\x10x\x84\xac4\x1d\xd3$$/Q\x82HE\n\xa9\xcd\xf6\x8d\xdf\xce|\x88\xe3\aۨ\x96좚\x8d\x95y^\x19\xb6\xce.\xb4\xc57\xa5\x00;ׅݺ\xfaY%K\xf7캀k`\xbc\x1b#dB5\xe4Dz\xb2/9h\xff\xad\x1c\xb7\xbff,g[AW\x8bw\xaa\x06\xa7\x13\xe0D\x03\x87\xccH\xb5\x89\xc9}\xf0\xe9\xc6>\xccr\v\x1e;\xd8f\x9b\xfe\xeb\x85\xed\x00I,\x99?\xccY6wZ\x80\xa5M\x84Cr\t\x1a9\x87\xd5TW\xdb\x16I\x9e\xda{\xff\x91]\xbc\xa3\x1eO\x9c\xa9ux]\xfc\xa4\x1e{\xf0\xdbz<\xc1y78\x8b\xff\xbdSt\xd6㗉\xd8 J\x12\x88\xf6j\xe3\xd5\xc3\x12-ZUVۿ\x9a\x12X\x14fuF\x98\t\xbf>\x05\x91r\xde\xf8\xfeg\xbc1\xf1\x14\u007f\xb5\xfe\xe6A)~\xe7\xae<\x05\xd1\xeeJ\xf5\xf9\xcfpSPX\xdczY\xb1\xf7\x86|\xd7|댰i\xb5!\xf9\x19\x992n@\xad\xedL\xaf\xf3r\bd\xec#\xef\xecXP\x93\xcd/\x1f\xadf\xa3k\x0fԞxY\u007f\xd9\xe9\xc4\xc1Hh\v\xe6'\xe0\x12\xb4_\x99\x82\x85\xb3\x8b\xef\x10\x9b\xf5/hP\\\\\xbf\x86|\x17z\xc8~\x94\xb7\xb1\x90\x8b\xb5\xc96?\xed\x15\xfd}\x97\xe1U\x9f\xcahr\x1e\x8f3B\xc9=\xac\x9c\xc6B\x05\xb1\x9bC\r껝\xe6\xd3&r\xd0\xf5\xe2\xd4cX!\x18\xefKy\xf2\xed}I\xc1\x8d{\xe8\xd0\xf7\xbbF\v\x81vN\xde\xc2u\x98\xb4? \"\xd0\xf2\xde\x1fy\x04\xfdb\x81\x17=\xbd8\xb2?#\t#\xe0>a\x99ն5\xfc\x87\xb8\xb1\xc7\xdam\x91=\x05sV\xec\xb9Pt\x1fj\xc0\xd3\x12h\x94-\xa03Zx\x1cG\xc1ll/\xd1+a\xe8\xe3K\xc2\xc60&G_6\xfet\x14\x05\x13\xb1U(i\x97\xe9\xe2\x11\x0e\x8b\x9c\x19P\x94\x93\xa3&不\xbf\xb4넼I\xa0\xf85\x01KP\xce\x0ep$\xb7\xaf\x03\xdf\r\x053\xaar\x0eZ[\x9e\xfb0\a3\a\xe5\xddJ\x9e\xc8 \xc6\xeb\xec\x86T\xf6|u&\x87\xd6\xe9\xa0Q\x10C\xeah\x9d\xb15f\xf2<\x97\x99>7T\xdf\xebs&\xacH\x1d\xe5\xd4\xd0Q\x83\xe9\x9e;i8\xf2\xf2y\x14,\xe9Qu\x1c\xcf\u007f\xa3J!\x98\x98\x8dh\xf5\x14\x13#:\xd2s\xe0\xfc8b\x96Q\xe2\u008dh{\xb7\xf9ZL\x80!\xd21\xe1F\x9b\xa3_V\f\xdc}yL\xae\xa5ٕ\x81\xb6}T\"\fq<\xee\xe4\xf1\x97\xd7w\xef\xfe~\xf3\xf6\xea\xfa.\x96\xb57\xc5\xc2vV\x9f\xc6$[b\xa1\x83\xd5\xc7\xed\xfe.\xb1\xd0f\xf5Qp\xb7\x88\x85\rV\x1f\x87\xd8\x0e\xb1\xb0\xc9\xea\xe3X\xf0\xa6X\xd8\xc2\xea\xa3\xc0\xae\x8b\x85\xad\xac>\nj[,lc\xf5Q \xbb\xc5B\a\xab\x8f\x17B\x9bb\xa1\xcd\xea\xe3 n\x17\vk\xac>\nl\xb7X\x18X}\xc7kq\xac\x1e\xc42\x99\xcd\u007f\xe7ͯ\x06+\xaa\xf6<\x8e\x0e\x8dČ\x03&\xda|\xaeK+x^̷]\x82b\xf9\x9e\xb6\xd3*Ds\xb1Q\x90I}\x1cB\xc66\x8a\xb5ʢ\x8dc1)V\x9a\x1bOEκ\xc7f<\xcd_\x14I\xc7\ai\xe0dL\xde\xf8\f\x03J^\xfdt\xf5\xfa\xf2\xfa\xee\xea\xeb\xab\xcbwqH!\xe9g\x87\x84\xa4\x91\x9e\xa89\xee0\x0f\x13\xf0\xb2[s\x88\x16\xc8n\x14\n\x96L\x96\x9a\xaf\xaa\xf4\xf6\xfeG\u05cd\xf5\x93\xebS\xcaVD\x83Z\xb2,e\xb6\x9dS\xeb\xa3\xea\xb8\xf1\xa4\u0093\xbe\xfa'Ԟ\x04\xc0\xdbmb\xaf\xfc\xa4\x90\xd6!-c\x0f\xf2\x19\xecc7\x9e\xb0\x92\x13 \x1eV\x81j\xccr\xa7\x1a\x95\x00t\xb7\x8dM\xf6N\\l\x0eT\xbf^Ô\x96\xdcyێ\x8e\xc61\xba\x8c\x1b}Y\xec\xd7J\xee\x19@i\x8e\x16\x9b\xbdu\x17\xb0B\xc4\xe00B\xe8\xd8'ƶ\xd4\x0e\x1di\xaf\xba\xc1|\xeed\xb0)\xa3\xf2\xe6\xea\x91.\xe5\x89\vIO\xd9\xec\r-\xbe\x85\xd5;\x98\xa6\x80XG;\xe6\xcc\xfa\xf4\xd2XӠ\x1e\xa8\xf5\xb8\xa9\xa5pžx!1\x19\xc5]\xa3\x85\x93;\x9f\xfd\x8c:\xacEOڒH\xbf\x83\x15F\x9av\x17F[\x95i\xa8y\xc9\x10+\u007f\x88\xd9\xd7pˤȠ0\xfa\\.\xad\xee\x00\x0f\xe7\x0fR\xdd[K쁙\xf9\xc8\xc5\xc3\xf49^\xc39\xff\r\xfeO\x8f\xd9ݽ}\xfd\xf6%\xb9\xc8s\"\x91Ֆ\x1a\xa6%wiw{g\xfav\x8d\xba\x8e\xc2\x19\xde\xe5?#%\xcb\xff\x1c\xcfl\xc38\x00m\xc8\xc2eb\x1e\x88>n1\x92\xbf\nR\xaa\a\xae,\v\xaf8\x02\x91\n\xc3o\xfb\xa4\xc1n\x1f!a\xd9+\xba=\xd1>\x91\x92\x03]\xaf[\xb1\xdf\xd8?4\xdc5\xf6K\a\xee\x1a\x11\xe1㮁'\xe00R\xe3\xb8\x16\x1b\xfb\xa5\xb3v\x0fop\x162\u007fItY\x14R\x19]\xd5h\x18[F\x10\x17ϨG\v\b\xde\xed;#\xffY\xfd\x88wG\xf4\x0f\xc7\xc7\u007f\xfa\xf6\xf2\xef\xff\xe7\xf8\xf8\xc7\xffL\xfdN\r\xb3Q^\xe7\x10\x80u\x01\xd9X\xc8\x1c,\xcb>s\xff\xf4\x96\xd7E\x86\t2\xd7=У\r5\xa5\x1eϥ6W7gៅ̯nz\x82D\x18:A\x05%\aQ\x02\xb6պ\x89\x19-J\x0f5qz\vM_>\xc7\xd2\xfb\xd7\xf6\xc8\xdcP3\xdf?Ůk<(f\f\b\xb4UA-4\x91\xd33\xcb\x1d\xd1\x14\xe8û%9Z\xbe\x88\x8cP\xb6\x00\xf4\x17lӀ\xa2\x03m#b۳\x9b>\x1c\x8b\x04צe\u007f\xc1KPeS\xf6\x00zqs\x15j-}D\xc4\xf7\x95lն}\f\xf9\x16\x12ο~\x169\x17\xa0\xf7\x13uuN\xb1\xbb\x83\xb1\xdfM\xde\xed\x833,\xcaDE^\x17f:q?\x8e\xb3\xa2Le\xe6\x1e\xc2\x02\x16R\xad\xce\xc2?\xa1\x98\xc3\x02\x14\xe5#m\xa4\xa2\xb3d\xf1\x13\xa6\x8aS\xac\xff\xe5>\x97\xca\xf9\x1b(\u061ci\\\x12O\x03\xaa\x02\x92\x95\xcaZ;|\x15t\x14\xc8?\x9a|\xab觻*Ծ\xa3M\xe4u\xb2z?[\xb3\xe6\x1f\xe8\xc6YJ^.@\x9fUVJ\x0f\xc0\xe8\xd2\x14K\xb2\xa4J\u007fT\x8b+gK\xa6\xf7M\x97\xee\x1aT\xac\xde&\xb2&\x82<\xd6-\x82\t\x03\xb3\x1e&\xda\xe8\x10\xc8\xe84\x1f}\xf9\x88\x1e\x9b-KS\x94\x98<\xbc\xa0\xa6\x8aJ=\x162\xcds\x17F\xa3\xa0O\xdet\x98\xbeHqc\xbbQPc@\x89\x97\xe4?N\xfe\xf1۟G\xa7\u007f>9\xf9\xe1\xabѿ\xff\xf8ۓ\u007f\x8c\xf1?\xfe\xe5\xf4ϧ?\x87\u007f\xfc\xf6\xf4\xf4\xe4\xe4\x87o\xdf|sws\xf9#;\xfd\xf9\aQ.\xeeݿ~>\xf9\x01.\u007f\xdc\x13\xc8\xe9響L\x9e\xf2\xe3\xa8\xf6Ќ\x980#\xa9F\x8e\b\x9e,\xf6\xb0k\x04\xe4\x1e\x8a'\xbd\v\x9aH_\xc1K\x9a\x1a\xdbGd\x1d}U\xab^h\xe8\xa9Yi\xc8\x14\x98O\xcf\xe7\xec\xe6\x15\xd4pw\x8b\xa92\xf8\u007f1n\xe8\xfe\xa6\xa7CSm\xb7`}G\x82\x01\xfa\x1e`1\xb4\xbf\xc4:\x12\xfe\v\xf7\x90\x10\x11\tcp\x95\x0f\xae\xf2m\xe3\x97\xee*\xbfu\xe7\xa7\xf6\x93\xa3\xb7\xbb\xdf\xd9\x1c\xfc\xe4I\xd2.\xf9\xe5\xb4պ2\xe4Q\xaf$\xce01\x9706\xb4ߙOX\x17\x92#\x85,JN\xf7\xbe\x1d[\xcdc3sh3\xb90\n\xa6\x17\xafua\xd0:/ݥ\"D\x1f\xc1\xcd\\7r\xc19a\xc2\tI\xfc\xd8\x03\xe3qv0\xa659\xaf\x03\xa1._qi\xd1\xf00\a\x91t\xb7\xd2\r\xa6\x896T\x19&fc\xf2\xbd\x85\xe5\xb41\x9f\x8b\xc2\x04Y\x94ܰ\"2!\xa9\xb2\xb0\xaa\xda$\x84j-3F\x8d\xc7p\xb4@\xe5T\x9b\xb0%\x98\x96c\xe8=f\\f\x90\x83\xc8\x00kY\x95\x91r0\xec\xf9de1z)\x96nn\x94\xe4\xa5K)\x86h\xee\xd3=\xb7\x8f\x9d\xeej\x8f\xafO\xad\xa9\xb3^#\x99\"\xfa\xb8\x1d\f9\xadK\x89U\xf1\xdd8x\xe9*v\x95\xfd\x92d\x86l\xe8\xd6u|\xbaҌS\xa2\xaaJ.>p\x16P\xba\x9a\xbbUŭ\x15\xd5D}\xe1SSo\x9fE\xb5=\xa4Z\xdbS\xa5\xed\xa7\xce\xeeRe{X<\xf5\x89:D\xb2F?\x05\xb4\x87\x12X(\x98\xb2Ǟ\x1c\xe6BT{DX\x0e°)K\xb2\x13\xacΤ\xa0\x00\x81i\xc2@\xb3\xb9+\x83)\xdaI6)4\xfd\td\xe8;\xcf\xc1a\x18\xfa횟c\xe0\xe6\x037\xdf1\x06n\xbe{\xf8\xe3\xf4\x19\xb3\xf2\x0fh)\xe3\xcd\xe5\xf4\xebկ\x1b\xf7\x9f\x91#<\xc7]\xf9\xea\xbcօU\xce\xf1\x8bq\xc7\x12\x8b\xc0\xe2ѳVd%\xe4\\1}\xf9@\xe6l\x16\xeb\x11\xe3\xb0\x04\xee\xf5{\xb2\xa0\x82\xce\\)M#C\xa8.\xf6v\x84eI\x8a\xe5\xad\xc2k\xb8T+8-\x9b\xe2\x92\xc6\xd1r\xdd.O\x13\xce\ue07c\x86\x82˕\xaf\x98)rrk\xa8\xb1l\xe9\x16L\\\x02\\\x12\xf3\xc0\xd5ܔ\x9cok\x9e\xb3}\xb4I\xef\ni\xae(9'\x05\x82\x1a\x93\xb7\x02b\xc32\x17\xfc\x81\xae\xf4\x19\xb9\x86%\xa83r5\xbd\x96\xe6ƙ\x9b\xf5\xfd\x94\xb8\xc3+=P¦\xe4\xa5k\tG\f\x9d\xa1뢪_\x17G\x14\xaa51'L\x1e\x98\xeek\xa7G\v̍\x03\xf8\x1b\xfc\xaa\x15\x9d\xee\xdf\xcfN>\x9cM![e<\x9dg]d\x98\xecZ\x97_\xaf\xcfm\x9c\xcbh\xa5\r,B\xd90t\xa00,3YH\xa1\xc1\x15\xd4K\xf1pU+t\x0e3\xdds\x8fS\x95\xbcBjsk\xa8ڳL[=ڧ\xf4&\x80\xb1\xe4\x9fQ\xce!'l\xb1\x80\x9cQ\x03<\xdeO\x1e*\x806\xcb=\xba\x0e\x9f\xae\x98d\x92\xde0\xa7\"砰^\xa1\xf7\x01\xb6\xe0\x1bP\v&hl\xc1\x10R\xa5w\xa1\xcb\x12rB\xb3L\xaa\xdcׂ\v\x95\xbd\xa8\x8a\xd7\xf8+\x8e\x87\xbaRC\xf2\xac\xe7\xebEC\x9ep\x99\xddkR\n\xc3x]\x1e2Ԇ\xf4\xbd)\xa3\xa1&\xb1\x98\xea?Gՙ\x18a?\xb2\xf3\xdf\xd4\u007f\xc2\x1fb\x95\xdf>\x96\xcf~\xf5|7\xc7Z\rJ@\xd2\xc0dJ\x19/\xb6\xc2\xc0\xbc`i\xd5\x17KTu}\xd5JФ\x19\x19X\x80\xb8\xddU\x87\"\xdbĺh\xf4>\xb2\n\x89\x1b}s\x14\x12j\x015ǎ\xb2\xc5ɱ\xc6Pؖ3\x01\xcd\xfa\xc5\fk\xa2\xa6\x870\x9b'\xd8\xf1#o\xa1\xa6\a\x81\x99\xc2\x1e#\xabFmK7\xf7>\xc9\xfcJJCN\x8eϏO7\x82Z\xc7\xe9P\xa7\x8c\x83\x93\xae\xae\xc8RVmV2H\xcd\x16\x05_\xe1\xfe\x1c\xe7\xd8\xd1\xc9_\x87UeZ\x8c\x98 S\xb3\xbb\x1c\nB\x9d\x11-\x89Q4t\x19H\x9f\xab\x85f\x81\x1bUz]\xe5\xe4\xf8\xe7\xe33\x02&K\xcd\a&\xe4A\x8ac\x83d4&w\x92\x94\xba\x9ex2̕,\x89\x00W\a\x00\x1e\v\xce2f\xf8\n\xc5|2LY\x1aW|\x11\x1b$b\xa1\xad\xcbGf\xfc=\x9dt\xb0S\xf2\x15\x9ev\xa7*\x10j\x8d\xa1%\x9cρr3O\xbf\xdfg\xe9RH1\xfaoP\x12\xcbx\t\x0f1ջ\x93\x10;k\x8e\x03d\x9c\xa4\xb8\x11\xd6\xdfNLb\xb0\n\xc27\x10\xadr\x92\x8d\x8e\xa4ww7߀i\x8b\xb0$t\xd8\x19\x85\xfc|ti\x83\x9aJ\xd5\xd1e\xf8\xe9\xd1W\xfeͥN\xc2\f\xd9\xecת\x8d\xeb>\xe1\x8c\x14\x91\xe6sv\xc3\xc8vZ\xb2\xcfh$W7\xe9\x89X\u007f\x97\xa5\xc5քN\xf8\xaa\xaa\"\xab\xc1\x90#;\xf5\xf4\xb4g&p?\xff\n4Ǻ\xbdB\x1b\xa0\x89*\xd2\x01\x8eZc.\x87Qj\\\xdfݹ\x03\xd9cG\x9b%\xb0<\xed\x8f\xf1L\xa5\xb3IW\xe1EA\xe1د\x9f\xe3Gb\x92\x1b\xbc\xc2\xed\x82\xff}\xd2+7\x91\x86\xf6\xc7n\x89\xbe\xb6sb\xf1\x8e0\x98\xc0i\xe2\xa1\xe81\xbb\xfeY¤w\xb6*銀9\\\xf5\x82\xe9\xef^Ƨ\xa5\xad\x8f\x83\xdc/I.\xe1\xd5\x1cω&\x9c\xde\xc7\xc7S\xbfTK\x92\x96\x88\xd8~\xbd\x1f&z\xdeP \xbd\xf5-\xbc̓|\xddx\U000f2c51\x84fYZ\xa1(7|\xf7rdX\x1a\xd426\xc1\xb1\x1e\xbdI\xac\x90\xf1\xfe\xcb0z]x;\xccu\xb7\x83\\v먗\xa8\x88(\x17\x93\x1e\x9c\xa4*\x80\xa1LM0~\xe3{8S\xaab\x9b\xd78\xbd\x10\xc2\xed\xa3\xef\xa1\nC\xc5\f\xc8\v;\xd3?\xfe\xe1\x0f\xbf\xff\xc3\x18ѐ\f5\x04\x96\xa9 W\x17\xd7\x17?ݾ\u007f\x85E\xdcR\xa9\xfcYn\xb6aنd\xf9ӎ\xcc#(\x8b\xbdRc\xa1\xb3>;lm\r\xef\xffv\xcee\x9d\x1agk\x0e#\x91\xdd|$>\xd3G\x88\x8d\xf0\x10}h;\xdbdŭ\xcc\xee\x0f`i\x1f߽\xbaq\xa0jc;i\x17\xa8\b.f&\x96\x92/]\xbf\xc0\xbbW7\x88\xa0\xb4\x9d\xb5oc|\x00]}+;\xc7p\x13ޥ\xe6$Ae\x8b\xc2w̤D\x01\xe5L\x1b\x96ᷪ0E\xa2}/\xefS\xb2x>\x19\xbf\xc2\xf1ې\x0e\x84.\x86\xe4Ӽ\xe6\x9ah\xb9\x18\xfa\xb0\x88\x86k\"\xf5Jנ\x91\x1c\\#q\xa2^\xaa~z\xfc\xa0\x91|\xda\x1a\xc9\xe7&#\x93_-\x14\xdc\x1aY\xf4̚p@\x0e\x943\x11:\xd1mKj y\u0096\xba\xde\xd1\x177W\x95w\\\xb6\x12\x110y%\x1a\xaa.\xb3y\x88\xcd\b\xd0\xfa\x1c\xd3#\xca\xc2y\xbeBC\xc9\xf8\x88U\xa1\x00\xbb\xf0IqVU$@t\x80p?\x82\xc9\xe2O\v\xfad|\ue20f'\x86\xedꛆ\x91)\xaa瀝*\xe0\x91\x19\x1d\xba]S-\x85\v\xe1\xfa\xedc2>\x80\xc94)\xa8\xd6.pg\xeaE\xb8\x8f\xdc\xc8\xfc8!zۘ\x10\x99)\x9a\x01)@1\x99\x13\xac\xfa\x97ˇ\xf8yN`Ƅ\x0e\xf4k'\x1a\x0e\x86Օ )\"\\w\x9e}\xd7\xea\"\x85\xed\xcaK\x93\xc9\x04>\xec_\xf7X\\O \x8a\xbe:\x89Ӵǧ\xa4\x9c\xaf\xea\x83\x1anz\x9a\xc3o\xd2f&Q*\x12\xeau\xafg\x12\xc5G\x03[\x99G\xf6(\xd4YI}ȿE\x9dL\xdbS\x95\xcd{\xb4\xf9\"\xf1\xe9\xcbCjӓcHm\xda{\f\xa9MCjӐ\xda4\xa46\r\xa9M{\x81\x18R\x9b\xaa\x19\r\xa9M;Ɛ\xda4\xa46==\x86Ԧ!\xb5\xa9\x1eCj\xd3^cHm\xdac\f\xa9MCj\xd3\xd61\x04\x12\x87Ԧ_c qHm\x8a{}Hm\x8a\x18CjӐ\xda\x14Ɛ\xda\x149\x06\x8ddHm\xfa5j$\x9f\x9b\x8c\xecU],\xf2\xb5\x90\xc7s\xa3\xe4\xa4G\x99\xb1\x1b\x8cճ̧\x01\xc9irm\x1d7\x9d1y\xd5J\xcf\xf0M\xf8]\x95\x96(\x88>ѧNO\xea[\xaf'\xba&S(\n\xa6\xcf\v\xe9\xfe_\x9dS\xd0H&p\xfe\xb5\x18\xe1\x90*|S\xb2\b\x9e\xca H\xe2u\xbb\xb3\a0\x13 \x1a\xe6!3\a\xfah7=2\x06vd\v\x04\xb0I̵;S`-⟜\n\xe2\xb3\x046\xa3\xfd}\x12.0\x15n#ҟ\b\xb1\xca\x0e\xd8\x16\xe5OS\xc9\xf5\xe1#\xfc\xcf\x10\xdd?|d\u007fGT\x9f\xacd\x99\x04sKD\xdfG\xe6\x13i\xb33\x9a\x1f\xa2\xf2i0\xbb#\xf9\xad\x88|*1\xf5\x8a\xe2\xf7\bN\xf5T\xae\xd3=\xc9ɚ\x92O6\xbe\x9b+\xd0sɣym\x8bϾa\x82-ʅe\x13ڲG\xb6\xac\xb2\x99\xe3i$\xe499\xadÅ\xe1,`\x96\x036\xb1\xa4\x8c\xa7\x94\xaa\xc3\xd2zs\x8a\xfe\t]f\x19@n\xe5\xe4\xeb:\x04\x1e\r\xf3\xf7\xe3j宫\x06\xd3\xe4E,幆\x8ah\xdf\xfd\xfewI\xbb\x9fb\x19&&l<\x9d\xac\x81\x90\xa31\xd9?Q\xa3\x8f\xba\x91\xeaHy\x9e\xe4\x8c\x1d\x89\x19\xe4\uf262aGR\x06aib\xf6@\t\x19\xbd8g\xcfD\x8c\x1dI\x18\x1eG\x89\n\xc8f\x02\xc6z\"E\x1a\xcaӓ/zȶ\xe7J\xba؞p\x91J\x92\xa4w\xb2E\xffD\x8b\x03v;\xac3\azw\xc7\xef\xe5\xa2;\x80\xe7\xb0gR\xc5s\xa1\xe5\x10)\x04\x1f\xb1\xfbl\xf2\xae\xf6I\x9e\xe8\x998\xd1'i\"5abG\xb2D\x1fOs\xcfD\x89^\xe4\x93\x1a\x8eH\x0eE\xf4\x0fC\xf4\x0eA\xecH\x88\xe8\xd3\x13\xb63\xf4\x90ڿ0\x8cv\xd8a-|\x90\xa8\x166C\x0e\a\r\x1d\x1cI\b=(:\x95\xf9'\x05U\x92\x996\x13\xcc0\xca_\x03\xa7\xab[Ȥȣ5\xa3\xb5\x86H\xd5y\xd5\x0e\x9c\xb3\xcc\xe33!Z\xb71\xe7\xd4w΄<\\\xa8\rѐx֊\xea#\xa1\x18\xa7\xb0\xab7\xedۓ\x1f7nA>\x9a\xcb\xc0])=\x04\x11\xfcU>\x1095 \xc8\t\x13\x81\x0e\xe2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfe\xf5\xc5W\xf1\x9c\xcbM\xe6\xf3u\xec\xa0kK\xeb\xe7\xf3\xeb\xf9\x0f\x1cޱ\xe7\x01O\xcbx\x1f}˹\xe7\x1c\x84m\xfe\x1e\xbdyu\x1b\xbe\x178\xef\xc0M\xd0K\xed\xcb6$\xc0\xfcL\x89*9\xed\xecɔ3\x92\xd0ylW\xbaY\x9d:\x16\rvK\xaaY\x9d6\x16?\xd1mifI)c\x1f\xddù\x96&\x96n~nI\x11\xf3\xeaY\xa2\x12\x9f\x9c\x1e6\xd8a\x91cG\x1a\xd8`\x87}Bv\xd8\xe7aa4j\x9d|\xa3h\x067\aS3\x03\xbb\"y\xa9\xa8\x17\x19A\xc1K\xb27,\x93\x11\x00\xb9\xe3TU\xe1\x1a\xac\xb82-yB\U0006ac90\xa2]\xfd\xc9\xe5T4\x8b\xb8D\x03\xf5\xd9.\x1d\xab\xf6\x8aR\xca\t-\x94tj\x1fQ\xa5\x10V\xea\xfa\xb3d\x91bm%\x9d&!\x9b5{4\x9b\xd9\xed\xb2*\x16V\xc1a\t\xf2\xe5a\x0e\"h\x99~\xc2vvS\xa926\xe1+2\xa7<%\xfc\xf2\xc0̜Pr\xcf8\xf7\xd3\x1c\x93[0\xc4̙Nt\xa6r)f\xb8\x19\xd4M\x18\x1e\vȬڑq\xa0\xa2,\xd2\xd6o\x95Օ,UX\xbfo\x1b\x17f\x99\x92\xb4!\x18?\v[}\xacw\x1f\xd8\x04ĺ\x04\xc5RC\xa8\xd3\xf4\xc04\x9c\xf5\xc1lh3\xea\u0381[w\xa1\xe4\x92吓\xc9*\x8d\xfee\x8eZ똼Gx\x81\xef\v)F\x02f\xd4\xdaF\xf1'\xd5\tqw\xe6\xdd<]=\n\x91\xb3\x8c\x9a\x04\x1bKca\xbd\xba\x9c\x1eY2\x8aXhPn4\xd0\x13!\x89D\xa5\xb8\x14̬06:/\r\xc9\xe5\x838\x1dc\xeb\xd9\x14&E\xc9\x04\f\xf5\xf7Z]+A\x14X\x9a\x80\xa0\x13\x9e\xa2\x9c`&\xee]'\x81\x92)PS&t\xf7\x9bQ\x03\x9d\xfe\x00G\x0f\x87=\x0eL\xfb\b蔔BC\xf4\xfd\x99\x86}\xf8\xc7\u007f\xfdp\xf6![\x80,\xcd'\xe5 |\x98\xb3l\xde\xf47\xb0\x05h\"\xcb>\xd7\u058c$/\xfc\xb4\xba)\xe2\x99\xdbG\xfe⼊IZcl\x88\xbd#n\xb4^ͯJ\x9c\x8eZ7\xb5<\xec\xf5\xf5\xedO\xdf]\xfc\xe5\xf2\xbb1\xb9\xa4ټY\x86T\x10j\xe5F\x14L\x94+s\xba\x04BI)\xd8?K\xd7y\x9c\x9cT\xdf9\r9\xf8Qp\xd3\xf2\xf5\x93,E+(\xa2\x98@k\x83\xbec\x1a\x1b\xbd\"\x14\x9f\xce*5\x90\xa9\x92\x8b8\xa6Բ\x1eɥ\x05\xe3\xbcEhi\xceA\x01\x99\xb1e\xa4\x90\xb5P}sd\x9a\x87\xa4b<\xc2\xf6\xf4X-\x96Nd\x19i\x02́\b0\xf6tW\x11.)t\xab\xa6m\xa9A\xc7\xe5\x97OJL\xa5.\x14[P\xc5\xf8\xaa9I\xab\xbe^\xcb\xe0\x87[Ŋ\xda&\n_\xbf\xbd\xbc%\xd7o\xefH\xa1\xb0\xac\xa7\xcb\x19\x8e\xb6 \xed\xf6\x92\t\xd8\rr\x1b\x9e\x8fɅX\xb9\x0f9^\x1e\xa9ep\xa6\r\xa0\xa5\xe2]\t\xa1i\xfd\xd1Wc\xfc\xbf#\xbb\x83*6DT\xa5\x97g\x1b\x97l\x9c\xe7\x82M\"\xef\x91\xe2\xd2\x1b4\xd0\xf3\x8eMB\xaa\xd7ڥ\t\xbf\xae\x1b\x8bz\x05\x85\xeb\xcb\x1e+-iEҸ\x85\xc8\f\xed\xf9\xe3\xc9>\x9dt\ah\xd6\\R\xbc\u05ed\xd3\xe15\xad\x1cV\x8e^\x13\xfc=R4̪\xab\x9b@\x8eN\xa3FI\x90\x00\xd4ҡ\xd5IX\xee&\xe8\x12$\xce\xc8W\xe4O\xe4\x91\xfc)\x01\xe2\x1f\xff\xf0\x87\xdf\xff1ޟ\xd5G\x9f裌:o\xf7\xd5M\xcf}\xfe\u07b21\v\xc9\ue311d\u0092\xee\xb88\xcbހ\xb2b\xc2SL<.{xl\xed\x12>I\xb2wY\x18W\xd3Z\xf9rF\u007f\x02\xc4\xca\t\xbb\x85\xf0S|\xb1\xe4O\x9e\xf0\xed\x14\xff*\xb5\xb9\xf6\xec\x8c\xe9ƌ\x93(\xc2\x1fn\xb2\xa0&\x9b\xb7\xf9\xad5!\x92\x8e}]~\x99\xe4\x12cY\xee:М%\xe4\v\u007f\xbc\xa3\x9b\x96>ۢ\xd4M\x8a\xea\xc3J\xd7\xdc\xfa\xe8\xa7\xf0z\xb9+X\x9e\xc8\x13\n\x99{\x83\xc1.9o\b\xc9X\x8b\xc1\x8d-v\x83\x8fR\xa4\x15\u007f\xa9/\xe6[^\x98Q\xe1n\x12OA)\xbci\x96\x82\xd2\x15fL\xb2\f\x12Ȳ\a\x17,\x9442\x93<\x85\xb6Pk|I\xee^ŗQlg\x9e\xfa9\xa0\x8f\xd6E\xab\xdf$\x13\xe6\xdf^ߜ\xd9)\x9d\x11\xa9\xc8\xed\xab\xbb\x9b~W\x99\b9\xba{us\xf4\x01\xf7$-:5j\xebr\x91\xef\x06*\x88\xb3\xce\xfaT\x14\x88Mtn\x85\x00\xad\x053Z\xd0bt\x0f\xab(\x9d7\x1dKI8ڜ\xb4[\xfc\x82\xee\u007f\x13L\x01\xcd\xd9'TL\xc1s\xa9z^\xddU\x15\x16r\x19\xe94Bk/@\a\x91\x17\x92\t\xa3\xbbJ-D\x81\xdd4\x19?\x99\x94š\xd4B\xc7\x18J-l\x1dC\xa9\x85\xa1\xd4\xc2Pja(\xb5\xd0=\x86R\vC\xa9\x85\xcf*yz(\xb5\xd0\x18C\xa9\x85\x841\x94Z\xd86\x86R\v{\x8d\xa1\xd4\xc2\xe6\x18J-t\x8e\xa1\xd4B\xc7\x18J-\xec;\x86R\v\xd5\xf8\xe5\\\xf1\x19J-|\xaaW|\x86R\v\xfb\x8c\xcf\xe3\"\xd4Pja(\xb5\x10\xf02\x94Z\x88\x1aC\xa9\x85\xb51\x94Z\xf8\\\x89j(\xb50\x94Z\x18J-t\u007f\xf7\xd7n\x87\r\xa5\x16>U;\xec\xf3\xb00\x86R\vC\xa9\x85\xa1\xd4\xc2Pja(\xb50\x94Z\x18J-\f\xa5\x16\x86R\v\xbf\"\xafb\x92֨@\xcbReqvp\x9b\xc8^\xc9EQ\x1a \xef\x02\xa8JY\x8eZ8\xca\x12\xa6\x9b7\xfa?l'\xc2L\x8a)\x9byE\xef|A\x05\x9d\xc1\xa8\xc2Ϩ\xbe\u007fw\xfe!r\xe39[\xb0\xb8\"\vv\xd4\x15\vnzx8\x12\r\xea\xbe\xe6tOc\xba\xa0ƀ\x12/\xc9\u007f\x9c\xfc\xe3\xb7?\x8fN\xff|r\xf2\xc3W\xa3\u007f\xff\xf1\xb7'\xff\x18\xe3\u007f\xfc\xcb\xe9\x9fO\u007f\x0e\xff\xf8\xed\xe9\xe9\xc9\xc9\x0f߾\xf9\xe6\xee\xe6\xf2Gv\xfa\xf3\x0f\xa2\\ܻ\u007f\xfd|\xf2\x03\\\xfe\xb8'\x90\xd3\xd3?\u007f\x19=\xd5\x03\x1b\xa7\xed\xf3\xf8\x1dRN\x9dR\x84|{A\x1f-\x83\x8d'\x85\x85,\x85q\xb7l\xdc1\xafN\x84KÊ=\x94\xe4S9\x98\xa4\x8f\xa1\xed\xf3ц\xf3\x191\x86\xf3\xe9\xce\xe7;O;\xed\x13\x1a=DžW\x99v\x9c\xd0h\x98Ap\xa3\xa1[͓i\"\x17\xccXs:\xe5\x9aq\xa3\x90\n^Ii\xba\xa8\x1d\xaf\x8a\xd7\xf2\xa6\xee6\x05\xd3\xcd\v\x1a\x8dK\xe12ؾ)j)\x15u\x9c\x02y\xce(\x87)\x13\x90;\xf5\xf4\xd7\xc7\xef\x92^Ӑ\x95\x8a\x99\xd5+)\f\xe5Cú\x19\xe7\x85q\xe5eH\n\xfa+\xa09֔)\xa8\x99\xbb<\xd5\x05\xd5\xf7\x90\xbb\x1f\x12u\xec*$kg\\-\xfdnU@r<\x15uk\x97\xfd\x8bq\xdexol\x8f\xf2^4\u007f+\xf8Ꝕ\xe6몌I/B\xfe\xde[K\xed8P4R\xe6X\xb6\xdb\xceo\x84\x9b\x88e[\x9a\x95V<\xf5\xa5X\t\x1f\x98A\xa8R\\\xe8o\x94,\xa3U\x81\re\xfd\x9b\xab\xd7\xc8/K\x9f!#\x8cZai\xaa\x14&\xd1>s\x95=\xf67\x9fӔ\x94mS\xb1\x87\x10\xae'o\xe8\x8aP\xae\xa57\x1c\x13\x82\xc1]\x1e\x12\xe2]5)7\xa3'\xd2\xcc\xd7}:\xc8\x1e6\xbf\x13_\xc0\xa8N\xb0\xa9<\x99v\t}\\H\b\x96ރ&\x85\x82\fr\x10Y4\xf5~\x9c4\b\xa4\xfck),{\xe9E\xfbW!\xffǹ\x8c\xfbY\xf5\x98\xa9\xe4mz\x8a\xf9J\xc8\\J\r\xca%\x87\xa9\x12\xd26\xfe\xdbr\x02\x1c\x8cs\x94`\x91[j\x9c\xe7\x8f-\xe8,\xfe4QS\x89B#\t\b]*\xf0NsCr\x99`\x06\xf8:Rv\xe9\u007f\xbbzM\xbe\"'v\xed\xa7H\xfeS\xcaxJ\xd5\x17\xbc\xfd\xb1\xc6M\xd84LѢ4^'\x10h\xec+Ǫψ\x90D\x97\xd9<\xe04\xc5;\x14\x9cW\xfe\x86\x14^a\x1bX\xd3'\xc0\x9az\nֿiP\xbd\xe5\xea\xdf>\x80\\\xed\xe3\x06\xb3\xbc\xa9\xbdk\xc8P\xc8\x02\fͩ\xa1)\xc1\xb7R4\xaa#\xae\x1d\x85\x14\xda\xdd}\x14\x90\xb4\xa3a\xfeʎ\xc2Ǒ\xd2\x1a\xbec\xa2|t\xb7\x03\xfa;\x94o/\x11\x1c\xf1\xa1\xa4\x14\x892\x01B\x8b\x823W\xbeo\xadE\xccU\x8bt\xd3\xf6~\xd3\xd4D\xf1@9\x97V\xcdH\b\x8f+*r\xb9\xd8X\xbc5D\x81&Xō\x05w\x1c\xcem\x87-^vׇ\xf3\xd7v\xd8\xfa\xb8\xee9,!\xa1J\xf9z\x13%\v\xc5\x1a\xa4\x81j\x10l\x92\xf7\x93\xd3\tp\xa7\x1a\xba\x93\xa37ON\xb2W4ѩ\xaa$\xef_\xf2\xe2\x9d\xe4\xe0r\xe3\x03\x92,\xd8_\f\x8e\xf0\xe5\xbe8B\xefS\vG\xc9^\xf4O\x11Ge\x82\x86G\xd6qd\xd5\xc46\x8e,\xd8_\b\x8e\x92C\x10\x1a\xb2L.\x8a\x1b%\xa7,\xfe\xb0n\x88~\x0f\xaeNΉ\x17\xfd\xa5\x86\xae,r\xd4#\x11x\xbcF\xee'CU\xe3\xd2\x135N\xe6\xf9[\\\xd1@\xff\xbf\x86\n\x81\\\xfblM\xaf\xf0_\x8d\x9fm3_\xa8\x90y\x00\xf4A\xa5\x9b\xcc(ǎDitA\xd6ic\x1d`\x8f\xfb\\\xc45\xb6\xf3pBN\x1f\xb6d\xc1_\x12<\x03$4\xf7\x9394jǻ\vxw\uef8c\x85\x9d\x048\\\x8b\xb3zJH\xbe\xcaC\xec\xca~1m\xbaҗ\xca~S\xf5T\xb2\b\a\x91\xa7V\x82*\xa8\x99\x9f\x11\x05\x1c/\xee\x05\x86v\xef\x1cZ\xc7i\xfb\xd4Xp\xe0\fa\xe3P\xcffR\xa4]b\xc7UcX h\xc4S䁖\xc1\x1f}\x17\x88-\xa1Oʧ ͏\xdcJ*\x8fgZT͎{&r\u007fo\xac\x85|\xef\nK;{\xce.\xc3[\x9f,o2ח\xe4\x1fig\xaf\xda02\xda<\xdaI\x10\x9b\xec\xa0\xe3h'\xc1t\xec\xe0\x9d3\x17CM\xa5QOW4q^\xacf\xb0\xb3B@B.k\x18\x15\xf7\xfa\x9b\xc03hY\xe4\x88\bً\x93՜1\xd0\xc0ч=_\xe9\xe5\x1b\x12\xd4\xe1d\x95ꁉ\\>\xe8CyS\xbew\xe0\x82\xe9\x9cYvg\x98\x98%\xa4`\xd4\x1e\x15\xcay+C\xef\x10.\x95\xc0\t\xaa>\xa9\x9b\xae\x83d\x9f\xbd#\xe6\xb6/\xb1\xb7\xff`\x8b{\xa3vW\xc4;Sv\xb87\x9c\xbb\"\x1a\xe4\xc7qo\xcc\x16\x9a\xbeR\xf6\xbb\x86Q~[\xc4w\xf8!\xeb\xb4\xfc͛ۋ6\xc84Ύy\xbdʩ\xc7\x16&\xa1\xf9\x82iͤ \x0f0\x99Ky\x9f\x04\xf7$\xa4\xceϘ\x99\x97\x93q&\x17\x8d,\xfa\x91f3}\xeeO\xf6\xc8b'\xad\xc9\t\x13<\xdczp\x1eBat\x88\x18\xd8ŤiY\x15V\x91\x00}\xa7B\x9fຉ\xf6\xeb\xd4\"UxcძT\x9b\xa4x\x9dXP\xfc\trLƋ\xaf.Ө\xf6\xe4\b\xb3ޗ4qk\xf7҅~>|\x1b\x01g\xaae\xa0\xfb\xb7\x11\xf8k\r\x8b\xe4\xe0\x8aC$\xda}l\xdaj\xe8]+$.\xa2\x9dhK\x1ec\xed6?\xc5\xe3\xa6\xd3!\xa9\x8e\a\tG\x05\xbdM\xbc\x98\xd3\x11:\bP\xdeX\x81\x96\x041\x18;s)\xa4r\xc7۪\xf4R$\xf4\xfcv\x03\xfdW.\xdf\fi\xd6+\x1a\x8d\xedz\x95\x9e\xeei\x87O\x87\xc3\xf46\xac\rd\xd5\x16\xe7\xd8I/S\xff\xc0\xcc\x1c\xdb7͡\xf5\x81t\xcc*И\xb0$\b(%\x95\xbf7\x12\x12\rR\xeb*;\xfd\a/\xb7X\xa6@\xed\xbf\x8eu\x9f\fZ\xd2j\x1fk?\xa0-ǁ\xe9\x1424\xd9\x1b;\x97\x04܅hN\xea~c\xfej\xb8eB\xf6\x9b\x89\xc7k\xc1\x1e-\x06\x9al\xa0'\x16B_\xacn\x90\xa7cB\xae\xd2\f\xd1p\xb1\xfb\xccr\x9a&t\u007f\xb3(\x95\x14D\xab35n\xa2\x0f\xe7\xa5I\x06\xc0\xac^;\xa3tɐ\x92oA\x9a9\x17\a\x11Ø{\xe1\x81\xd9#\xe8\x99P\xaaOl3\u007fc#\x1f#\xcd9\xb6\x9e\xc3\x11\xfcc\xc91\x84\x1d\xb9\x1c\x84Ňq\x89˙:h>\aْӑ\x9e\xddD\x9e;É<_\xb4\x99\x1c \xe2L>J\x94'\xedⷫ\xe8ܳ\xcd\xefm\x03JãiU\x8f\xa8\xb5{q\xea\xaa\xdaWU\xb1\xf9*T\xe3g\xff\x1d\x9b3\xdfn?/\xa4+6\xd0,u\xef\xfb\x9aƩ)\xa50\x8c\x87\xe0բ\xe0\xd6xl\xcd8:\x1b\x12a5\xfa\r\x9fUȨ\xdb\x1b\xfbB\xffq\xe7\xe5\xbfP\fU-\x8dC=\xef\x9b\xeaS.\xfa\x11\xa9\x01\xfb\xf6\xf3X\xb2\xcd\xc8\x10o#9\x9bN!\xdcp\x8e\x14{\x05Uta\r\aM|\xea\xef\x04f\xcc]3\xadT\xab\xc8\bEU$\xec̩{̐\x05\x9b͝\x97\x86P,E\x19_n\xd2H\xc2%\xcd\trq\xa9\xc8\x03U\vk\xb1\xd0l\x8e\xf5\x1b\xa9 y\x19}\xf0\xb1\x93\xdcj\xa4\r5@d\x01\xae\xa4\x84\xdb\x1b\x8boW]+\x8eL\x87\xe6\xd3C\xf3\xe9\xbd\xc7\xd0|zh>=4\x9f\x8e\x1dC\xf3\xe9\xa1\xf9\xf4\xdech>=4\x9f\x1e\x9aO\xbb14\x9fN\x18C\xf3\xe9mch>\xbd\xd7\x18\x9aOo\x8e\xa1\xf9t\xe7\x18\x9aOw\x8c\xa1\xf9\xf4\xbech>]\x8d_Nӳ\xa1\xf9\xf4\xa7\xda\xf4lh>\xbd\xcf\xf8\x0f\vch>=4\x9f\x1e\x9aO\x0fͧ\x87\xe6\xd3C\xf3\xe9\xa1\xf9\xf4\xd0|zh>\xfd+\xf2*&\xde\x14\xc9YT˶=\xba\x05\xa4\xd4>\t\xb5;-#+\xa7SP(|qvA9J\xbb\x04\x16:CU\xa2\xdbg\xc4b\xa3@\x054w5/\xe2\xfc\x82\x9d\xd3\nEH\xb1}\x99\xbb\x97\x1a\x19\x1e \x97o\xbf\xae\x9d\xae)\xad\x0e\xd2n\a\xe2zފ,\xfd\xaePM\b\x1d\xd5Y\xe3p\xeb\x12\xf03.\xb5\xbf'\x8b\xc8\xce\xe6T\b\xe0^\xe9fq\x98\x9dSM&\x00\x82\xc8\x02\x84S[(\xd1L\xcc8\x10j\f\xcd\xe6c\xbb\x828\xef\x99'\x02ߵ\xae\x9e\xa96\n\xe8\xc2\x11\x83\x82El\x9fA;EB3%\xb5&\x8b\x92\x1bVT\x93$\x1a\xb0TFd\xd2\xd2մ\xde`L\x12\xaf/\xa0\x9eU\xab\x88\x9e\xa3+\x83\xd68\xf3\x86*s\x86\xed`\x17\x85Y\xb9\xbbTq\x82\x0f\xbbv*mH\xc6\x19\b\xe3W\xed\xea3\xe2<\xcfHln<^\xdfu\xbb\xa0=jE\x8e^\x91\xc2hw\xd3'm\xa2~\x8a9\xd3\xde\xfb\xa6\xcf\b5APF\x13}\xa0%$\xfb\xa0\xc0\xb9Y\xfb\x9f\x12\xa7Y\x97\xf4\xd7\xf5U\xb3\x9a\x19N9\x8dS\f\x03S:k\xd5r\xa8\xedCLrG\xb6\x1a\x05\x16\xcb\x0e9,\xe0\xc1\x11\xb0\xb4\xfc\x032`K\xf4\aY\xce\x18\x05q\x9d\x8b>;\x13m\xe8\xaeo@k:\x83\x9b\xc8\x14\x9bm\x0eb̲\xa9\x89+\xd2\xe0\xc2jdF6t\xb8\xfa\x02J\xdb\x02\x8d\x02\xbbpk\xacl\xce\aŌ\x01$b\xec\\\x85\x99\x87\x91y\xe0\x1b\x93k^\x8fy\x13>\xe8>\x14K\xb5V\x9f\x12\xb9\xbb\xd41\x012Q\f\xa6d\xca\x04\xe5\xfe\x1eF܅#\xecgA\xb5%M\xaa5(\xf4\xb9x\xb7S\xc0M\x1c\xc1~\xef\x11iT)2\xda\xe8r\x89\xd5\xefؔ\xcc\xf0\xaeG\xa411\xa7\x82\xfc\xebW\xff\xfeG2YY-\x18\x8dc#\r\xe5\xd5\x06r\x10\xb3\xc8\xda\xfe^<\xb5\xeb\x90U\x94\xc0قź\x85\xac1\xf0\xbb\xfbI;\xd0x\x9e\xc3\xf2\xbcA\x9f#.gq8}\x15\xeeWVw&c\x14\xf9$\xd7~\a\x1b\x90\x9ce\xabdF\x10\x9a琹|p\xae\xbc\x9e'\xb6\xbe\xe2XȢ\xe4.%\xe3\xebPY2\nd\xa9a\xb3\x1aV'\x1f\x8c\xa5\x860\xb5\xf5\x0e\xe1\xeeʔ_J\x9c\xd2\xe2\v\xcf\xf9\xd0x\xd53\a\xfd\xc4_S\xce'4\xbb\xbf\x93\xdfə~+.\x95\x8al\xb3\x8f\xd4\x1f\xf0\xc1\xa9\xd5b楸ǖ\xb7u\xada\x19'mei\x8a҄K\xdeM\xf7n\xd8\xcc\xe8z\x90\x95\x82\x16<\xc3\xf5\xec\xe0ў[t\xcfƱ\x03_|\xc7q\x17.gռu`\x06\xb17\x82~\xf7տ\xfe\x9bcYD*\xf2o_\xe1\x95Q}\xe6\x84\x18\xea\x06V\x91]P\xcec\x83YM\x06c\x89~\xdc\xc1$\x9e\x9dG\x98tv\xf0,&\xf7\xdd\xdd\xdf\xd1\xdefF\x03\x9f\x9e\xb9\xda#\xc1\x83\x18\x05\xf4\x18\x95\xb8c/e\xb1\xc8\xcdG0h\x97\x92\x97\vx\rK\x96\xc5E\xf8[\xa8nA\t\x91 δ!2\xae\nĄ\xcb\xec\x9e\xe4\x1eP\xe3n\xc6z\x1f\xeb\x18\xcc$\xdcBٺ\xba\xfa\xfe\t\x89mF\xb4\xa0EQ\xd5rP\xf4\xa1\xb5X\xe4%\xd1\x17Phj\xa0:=\xab\xc3M7Va\x0f\xef6\xb0Z\x03\n\x04S\xc4J?7\xfc\xcd덆T\xa1\x83^R\xae\x81\xdf\x13\xa7\xa7ٝC\xce\x1c\x1f^\xef\x91\xf4\x90v\xa7\xa7\x85cQ\xe5\n,\xa8\xf16Mb\xfe\fRm\x01J3m\x15\x98\xf7x&^q\xca\x16\xe97\xfcS\x1a\x12\xf4h\x01\x9b\x92\x970j\xd0i\xe4\x8bш\xeeU\xf7(\xeen\x8bci\xd8\xd27\x9d\xeb\xdf\xc8\xdc\x03BV\xed\xda0[S6\x9a\x1c\xb6\x15z\xe8\xa5p\xf4e\xfb\xefk\x1c5\xb9\xbe[g\xfcq\xc6\x03\xe4`zf\xff1\xd87N\xfe\x00\xdc\x1b\xf9\xb6_FߺsM\x87\x8d'\xa8\x86\xe9\xe5}$c\x97\x1b\x9b\x00\xdeR\x90\x9f\x1e9~y\xfcAy\xb8C\xb7\x92\x05\x9d\xa15\xd2\x13\xeb\xeb\xe0\xfa\x15\x9a\xb5f2B\xacz\xc6 \\ȫ\xda\xe6I@݅\xfaZ\x0e\a\xf3\t+\x8f%@|\xa0+B\x95,E\xeeb\x0fuP\xea\xcd\x1a:\xae\xa5H\x99\xb2\x8f~\xfbJSUM[L\x13`\x82\xbc\x18\xbf\xf8\xeas\x13\xfc\xb8\x925\xc1\x9fX\xf8\xb9\xc1\xb7>(\x16B\xcb\xf6\x9e\x98x\xe3]\xacu\x87\xf5\xa4\xb2\x93.\x06\x84@\x1e\x143\x9e\x9a\x1f\x98\x06r\x12\xeb5\x0fC\xaaf-\xcbӶK/\xa9w{\xbf\xa2\xa7\xba\x9c<\x83dp\f=\x01=Ȅ\xba|\xf1:\x1df\x87Xi\"\xfd(\xa5\xd3lj\x9bͱ\xabzu\xfaA\x0f\x89߲\xcb\xc7\"\xa1s\\k\xdb.\x1f\v\x8a^\xff\xa2\u07bf\x94sR\x8b\xf0\xed\xfb\x97\x00w\xbbZ\xf0\x17\x98\xd3e\x92\xfc\xd3l\xc18U\x1cS\xcbn\x1d&ɤ4\x04Ē))\x92n_\x10\xb2\xa4\x8aa\xb5q\x05X\v2\x03M\xbe\xf8\x80S\xe6\x1e\x19\xe9L\x16\x9d\xecA\xd5/W\xfa\x8c\x9fT\x1e\nN\xe0\xfd\xe4\xea&\x8d\xab쥍T\xb0\xa5\xb2\xb6(9_\xbb\xd4\xd8\xd97\xc1>g\xb5\x93-w\xbbv\xd9\x0fa\x8a\u0590\xd4\x05\xdd\x1be\x8d\x17\\\x02\xbe\xe6,C\x87\xbd\b\u007fp\xffeg\xed?ұB\xb7\x97.\t\x15\xf3\xb20:{\x86\xc9\x15\xa2\xfe\x82\xab\xa0\xe0>\xbc\xb9\xfc\xadN\xc1\x9d\ai/\xa4u\xd1a\x98H$\x91\xd5ϯ!,P\xce>\xf8\xda$\x9b&\xc6j\x1a\xf4\xcfMhv_\x16\x9f\x16\xfa\xb0\x03\xf5-p\xd4\r\x9e@\xddw\xcdg\x1d\xda\x16`\xe8\xf2Ÿ\xfd\x17k[3n0\v\xb9S5{p\x99\x90\x16k\xee\xc2YΖ,/)oQ`\x03g5j\xf1B*\xe3]\tRX\x12\u05ff\xdf\xc2qua0\xfa\xac\xee\xf6\x02\xa3\xe3Ǫ\xdf>\x15\xb6\x9b\x05\xb7\u074bk\xaf8,\xfa8\xaeo\a\xae\x03\x1e=k\xb7\xf6\xc3\xd64\xdb;_\x900<\x87+\xbf\xb8~\xbdM\xbd\xd9\xe9\xb2oM\xf5b\xc7t\xfc\x99\xa96|W\x17\x06\xaf\x88\xf9;_\xfa\x8cPr\x0f\xab3\x97\xfc*|\x19z\x0f\xc4u\r\xf6j\xc3=lWU\xec\xcb\x0e\xde6\xc4\xec\xe3\xc0\xbf\x87\x9d\xbe\xaf\x16:\xeeaU\x85\xdd\x11/\xf6\x87\x10\x00\xadQ\xe1۸\xef\x96\xfd;\xa3\x9c{\xe9\x1b\x01k{O\xbfB\xb3\x02K|\x8eT\xec\x1a\x8e\xb5o\xd5,\x85\x9e\xb3\xe2\xa9\xe4\x18\x8a)\xdbr\x1a\xb0_5\xefu\xe0\x1d\xfd]\x893r-\x8d\xfd\x9f\xcbG\xa6\x9f\xb8\x90c\xf7\xf2\xb5\x04}-\r>\xdd\x1b9nj{\xa3\xc6=\x8e$-\x1c\x8f\xc4KJ\xf8\x8dj\x99WO\xdf\u007f\xafP\xcc4\xb9\x12\x96Qy\x1cT\x97\x15\xb5\a\u07fcc\x88\\m\xb7&\xea\xbe݂\xef\xd0j\xbf\xd1\xc4\\\xf3S\xbbQޚ\x86\x9b\x82\xf3h\xbb\xbf`\x82v\xc1i\x06\xb9\xef3a7\xde(j`\xc6v\xb7\x1fX\x80\x9aa\xa2A6ߵ\xaa=B\x87{*އP\x91\xb7\xb3\x9aQ\x85\xf6\xe7P\xa1\xbd\fA\xf1\xb9\x05\x1b\xa1\x93\x18\xe57Or\xb4'1\xb6)\x8bܧ\xbd0\xa7\x85\xa5\xfc\xff\xb1\xec\x19\x89\xe8\u007fIA\x99\xd2cr\xe1o\xa8l\xf9n\xf3\r\xaf\xeb4\x81[\xb8L\x13\xbb\vK\xca\xc1\x15*\xa6\x82\xc0\xce\xf2+r\xba!-\xcf\xc8\xc3\\j\x94\fu\x10\xe9\xe8\x1eVGg\xad\x13\xb2\x05\xa2}\xf8J\x1c\x9dU\xf1\xb2֡\xac\xe4\x14\x860\x8e\xf0oG\xe3\r\x01\xbb\x05\xf6\x13bw'\x95\xec\xf8c\xa5u\xbfq\xa9M\x9b;\xbf/}줍\x8d\n\x8c\xcdo\xb6\x88\xa3\xa9\x1c\xb7̊\xaeOR5\x03\xd3e\x82x\x8d\x19S\x19\xc6\xe4B\xac6\xe0\xe2ŸN\x95\xdb\x1bq\x15\x9d\x15\xad\xf6E\xd8\xd2\x123$\x1a\xa0|\xe2\x92\xee6\x84탛\xbb\xb6cSP\x80\xaa%\\\xcb\x1cn\xa4\xea\xca\xefh\xc7k֟\xef\xb0h\x1bH\x91<\xc7<{|\xb4c\xbe\xa8\x1b{\xbd\xf8\x90Ƨ\xff\xfe\xcd\xfb\xa7\xd6\xf3\xaezp\xf7B(\xf6\xd6p\xfbձ\x0e\xfb\xbe\xbbk#h\xa1\xe7Ґ\x93p\xa9=\xe3\xb2\xcc\xfd\xcd~\xd5\x11\xef\xe9\xb1J\x9d\xcd!/9t7\x1d\xdc(S\x19\x1e\r\xba_)\xd8?\xcbv\x8b\xde\xe0\xa1\xf2Ow\x1d\x84\x1a'\x95i\xdd\xf0\xa8Zv\xf4\x17\xdc\xcf\xf0%oEz\xc8[R\xe1\x9b \xddY\x90\xda\xe0=%a\x1aEׂٙ\xf9\xf6\x1c\xfe\xf1\xcekva\rێC\a\xfb\xe8\x16\xae#\xffՍ\x88\xf8\x96c\xe5r\xe9\xdb0\xbah\xee\xd6\xe5\xdcg\xb40\xa5\n\xed\xafK\x85\xfd\xc0\xea\x16&4`Σ\xa8\x05v\xbba\xe0\xfd\x82L\x8a;\xb6\x00m\xe8\xa2x\x82B^m\xbea7@\xaa\\W\x95N\x9a.\x82\xba\xe9V\x97UL\xebVo\xf9\xb8\x01ہA\x9d̂\x86\x9c\xc0\x12\x04\xf1\x97|0\xf2\xee\x1c\x10\x1d@\xef\xd08QK\x8c\x05\x048\x98\xef;\x95\x8a`W\xbdj\xea\x9b\x14\x11\xae\x8e\xe7\xd4\xc0\xa8\xf3&\xe1^'\xb1S\xea\xb8\x1e\xb9O \xf8\xd25\xd2E\t\x94a\x9e\x98\xdd^\xce\xdd\xdb\xe1恿\xea\xf7\x00\n\xc8\f\x84Eq'\xc7\xf1\xaa\xack\xf5d\x11\xebOp\xe5\xc0\xb9sݱJ\xcaC\v_\xac\x80\x13\xa4\xcaV\x97\x19>\xd2y\xcbj\xd7\x05z\u007f\xe3\xe3\x1dPݕ\xb7\xd4B\xc4\xd7\xcdg\xbd\xad\xe2p\x80KϨ\xeb\x93\xe7:\x962\x05\x9d\xa4\xef\xa7$\xf1\xcb\x11g\x9c\x90bN\xf5S\xec\xf2\xc6>S\xb5\xe4j\x1cʊS\xbe\xdb2'\x10\xe5b\x13\xf8\x88\\\xc3Cǯ_#ѿ\xf7m\x95;\x8e҈\\\x89\x1b%g\xaa\xabJ\xec(\x1c\xac\x0e\n\x19\x91\x1b\xaa\f\xa3\x9c\xaf\xbe\xee\xeeF\x13\xbe\x1e\x85;?\x95'\xb5\x04\xf7X\xad\x882\xe1Ο\xa5T:\t\xbd\xaa\xfd\xc6\x1e뚎;69|tl\x8dP\b\x8e\n\xd6\x06\x8a)Xڌ`:\x95\xca8\xc5w4\"l\xea\xf9g\x97\xf2A\x19G\x15\xce\x05\xd1\b3\xb5\x81X\t$\xa9\b\x15+\xa2\x90X\xb1\xf5ׂ\xae\x9c\v\x9efYi\x8f\xe7\xb96\xb4K\xce\xf4rȡQ\xe9\x89l\x8b\xb5\xd7\xf6\x057\x9f\xaf\xba\xefWuJ\x10\x9cC\x1dv\xcav\x9cak50\xbc\xd5_\xe9\xa0ڞ\xb2nK䩢\x1ax'\xf2j\xbb\x81\xdc\x0e\xb8U\x0f\x87\x05\xb8+\x95\x1bːM\x15y\xbb;\x91\xe9\xf0\xaaݳl\x8e\x15\xf5\xcc\\\xc9rV\xb5K\xdf\xc6@\xb7\xb9\x15K\xbc\xf4[\xf0rf\xc9\xda;\xfaL\xa9D\xc3z\U0006effc\x9e\xee.\xa0\xbbQ\xb8Cw\xd7-\x89\xf7\x94\x02\xd8z\xb8\x9fd\xaf\xae\xbb\u007f\xba\x12yY\xb1\xd4\xcb}d\xf3\xfb\xb5\xc7\xd7\x02)VJ\xd7\x10\xbd<\xed@\xce\t\x9b:\xafifg\xbd\x99\xbc\xf3\x81\x03\"\x0fT\t&fO-\xfe{\xffX\x87j\xe2!t('\x1d\x8b\xa8ԕ(\xe5$LrK\xaeO\xa5\xb0\xf4PO:\xcf\xd0ƏH\xc8y\x03\xc9\xfeK\xfe\x97Z\xadwe.|\xa0\xd2\xe1\xf6\x9e\x89\xfceH\b,x\xa9(\xf7\xff̤plA\xbf$?\xfc\xf8EX\xd0{P\xba\xfa\xf1\xff\x05\x00\x00\xff\xff\x14\x93\x06\u0098\xed\x01\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c]oܸ\xf1ݿb\xe0>\xa4\x05\xbc\xeb\v\xfa\xd0\xc2o9LJ\x1a\x97\xe6\x82\xd8\xf5K\xd1\a\xae4k\xf1,\x91*I\xad\xb3=\xdc\u007f/f(\xeak\xa5\x15\xb5q\xd0\xeb\xc1|I\xcc%\x87\xc3\xf9\xe6pij\xd5ju&J\xf9\x80\xc6J\xad\xae@\x94\x12\xbf8T\xf4\x97]?\xfdծ\xa5\xbeܽ={\x92*\xbd\x82\xeb\xca:]|F\xab+\x93\xe0{\xdcJ%\x9d\xd4\xea\xac@'R\xe1\xc4\xd5\x19\x80PJ;Aݖ\xfe\x04H\xb4rF\xe79\x9a\xd5#\xaa\xf5S\xb5\xc1M%\xf3\x14\r\x03\x0fK\xef\xbe[\xffe\xfd\xdd\x19@b\x90\xa7\xdf\xcb\x02\xad\x13Ey\x05\xaa\xca\xf33\x00%\n\xbc\x02\x9bd\x98V9\xda\xf5\x0es4z-\xf5\x99-1\xa1\xd5\x1e\x8d\xae\xca+h\u007f\xf0\x93jL\xfc.\xee\xea\xf9ܕK\xeb~\xecu\u007f\x90\xd6\xf1Oe^\x19\x91w\xd6\xe3^+\xd5c\x95\v\xd3\xf6\x9f\x01\xd8D\x97x\x05\x1fi\xa9R$\x98\x9e\x01\xd4\x1b\xe3\xa5W ҔI%\xf2OF*\x87\xe6Z\xe7U\x11H\xb4\x82\x14mbd\xe9\x98\x14wN\xb8ʂނ˰\xbb\x0e\xb5\x9f\xadV\x9f\x84ˮ`myܺ̄\r\xbfz\x12y\x00u\x97\xdb\x13n\xd6\x19\xa9\x1e\xc7V{\a\xd7F+\xc0/\xa5AK(CʜU\x8f\xf0\x9c\xa1\x02\xa7\xc1T\x8aQ\xf9^$OU9\x82H\x89\xc9z\x80g\x8dI\xbfs\x0e\x97\xfb\f!\x17ց\x93\x05\x82\xa8\x17\x84ga\x19\x87\xad6\xe02i\xe7iB@z\xd8zt>\f\xbb=B\xa9pX\xa3\xd3\x01\x15\xa4z} \x91=\x98\xef\x1eq\x1c\x98\xffy\xf7\xd6\xcbM\x92a!\xaeꑺD\xf5\xee\xd3\xedß\xefz\xdd0\x90\x83z\x97 -\bx`\xa1\x06S\xab\x1f\xb8L80H\\C\xe5hDip\x15(\x936 \x01\xb4\x81\x12\x8dԩL\x02Ey\xb2\xcdt\x95\xa7\xb0A\"\uee99P\x1a]\xa2q2\xa8\x8do\x1d3\xd1\xe9\x1d`\xfc\x866\xe5Gy)B˂S+\x03\xa65\x1d\xbclK\xdb\xe2\xcf\x04\xee\x01\x06\x1a$\x14\xe8\xcdϘ\xb85ܡ!0\x01\xebD\xab\x1d\x1a\xa2@\xa2\x1f\x95\xfcO\x03ے\xc4:\x16$\x87\xb5.\xb7\x8d\x95O\x89\x1cv\"\xaf\xf0\x02\x84J\xa1\x10{0H\xab@\xa5:\xf0x\x88]\xc3ߵA\x90j\xab\xaf s\xae\xb4W\x97\x97\x8f\xd2\x05\xf3\x98袨\x94t\xfbK\xb6trS9m\xece\x8a;\xcc/\xad|\\\t\x93d\xd2a\xe2*\x83\x97\xa2\x94+F]\xb1\x89\\\x17\xe9\x1f\x02G\xed\x9b\x1e\xae\a\xba\xe2\x1b\x1b\xb1#\x1c k\xe6\x05\xc6O\xf5\xbbh\tM]D\x9d\xcf7w\xf7]a\x92vH}\xa6{G\xc2Z\x16\x10\xc1\xa4\xdab\xad\x8d[\xa3\v\x86\x89*-\xb5T\x8e\xffHr\x89jH~[m\n\xe9\x88\xef\xff\xae\xd0:\xe2\xd5\x1a\xae\xd9g\x90\x1cV%iO\xba\x86[\x05ע\xc0\xfcZX\xfc\xe6\f J\xdb\x15\x116\x8e\x05]w7\x1c\xec\xa9\xd6\xf9!\xb8\xa6\t~\x05\x1d\xbf+1\xe9\xa9\f͓[\x99\xb0b\xb0\xe5kL\xc0\xc0\xfa\xf96\xae\xb5P\x9b\x1e\x1a>\xec?jmb}\xc2\x01L\xa8M\xcc\xfa\xe0\x97\tj\xf2OX\x94\xa4\xae3(\xde\xd7\xc3\bE\xa2Qڄ \xc1Y\x06\xf3\xa6k\xab\x06\aF\x85\x97ː赓im5\x0e\xa8y\x9c\xa2\x1e\xb1\xad\xa8r\xf7@~\x1c\xed\xbd\xfe\x8c\xd6\xc9dl\xe4`\x13\xefG'\x06~\xa3%\n\xbb\f\r)'\xff\xc0\xf6n\x14.\xb0\xceXL\xd9\xe4\x89'r\x99\x1bO\x01\xb2\x9dy\x0e\xa5Na\xe7W\x82\xcd> }ț\x96?\x1b\xads\x14cT\xc3/I^\xa5\x986a\xce(]\x06\xbb\xbd9\x98\xc4\x01\xa1\x90\x8a\xa4\x8c\xc2/BU5\xbfN\xec\x93\xfd\x950\bd(\xa4\xf20A\xfa\xb0d3!pԤ\xc3b\x02ϣ\x12\xe9\x1b\x05\x9eb\x93\xe3\x158S\x8d\xc9z\x80!\x8c\x11\xfb#4\vA\xf3\x12\x925sjs\x9e\xcb\x04\x89X\x8d\xd1f\xaa1i&\xf6\xf7\u007fH\xb0L\xeb\xa7\x18\"\xfd\x8dƵ\xce\t\x12>\x9b\xc0\x063\xb1\x93\xda\xd8a\x84\x83_0\xa9\x1cN\xe9\x91p\x90\xca\xed\x16\r\xc1›\x89\xbf\x8f\x11븉\xa0f\x8e3\xfe`_-ӉyL\x8d\xa9\xad\xb0)\x9e\x84\n\x8c8Y\xec\xaa\x04\xa9R\xb9\x93i%r\x90\xca:\xa1\x12\xbf?\xd1\xe07\xbe?\x98\x13\x88\x03\xfc\xbd\x01\x0e\xbb .\xf5<\x9bVH\xe1h\xa1\u0378p\x84v\bf\x9a\f\x1bA\x16PO\xb9\xa3\xb6\x19:\xc5ը\xa4\xecR[\xbbs\xd1r\xca\a\x85\xb9\xd8`\x0e\x16sL\x9c6\xd3\xe4\x89\x11\x02\xdfb\xed\xe7\x04eG,i\xeb3HPg\x8dhۜ\x86\xe7L&\x99\x8f\xdfH\xca\x18\x16\xa4\x1a-[\fQ\x96\xf9\xfeئ!F2\xea\xc5\xe6\x8cF\xdb\"\xcc\xc7\x10\xee\x94!i[\xa4\rnی5\xeeS\xbd\x11\x9bW\xa2\xf7\xd0T_%\xec\xb7\a\xd3_^؉ܒ\xcew\xb7[\xc0\xa2t\xfb\v\x90.\xf4\xc6@\xa5\x00\xab\xc5\xe3wƸӴ\xe5v8\xfbŵ\xe5E\xb8֠\xf1;a\x1a;\xab\xbb\xdaW-b؇\xee\xcc\v\x90ۆa\xe9\x05le\xee\x90c\xa99D;\x81\xce,\xe7^\x92@\xb1\xbe\x97Z!\\\x92\xdd4Gڈ\x19\x03Z\r\x01\xf8\xb8<\x9ca\x98\a\x11 \xa1\t*8\v\"\r\x16>\xbbr\xcf\xfa\xd1\xf6p\x04\xf8\xee\xe3{L\xe7H\x06\xf1\x92z\xb0\xa9w\x83H\xa7\x8b\x02o0\ndgS\x1c\xa65g<\x9fC\xbb\x00\x01O\xb8\xf7\x91\xd5\xe8\xe1r\xac\x11kE\x03\xd2 '\xf4،<\xe1\x9eA\xd5\x19\xba(xKDŷ'\xdc\xc7\x0e\x1d\x10\x95\xf0\xabs\x14\x9e\xba\xd4\xc1\xbb\x88Q\xa5\xb65D\xadu\a\x9c\x8e\xdb,,3J\xa1\x05\x8a\x9f\xb8\xed\x86a\xbd\xb4\xf4\x13\xee\xdfX\xcf>ҚL\x96\v(@\x06\x1b,\xb2\x86\x85|\xec\x83\xc8e\xda,\xc6z\xb2\x00⭺\x80\x8f\xda\xd1?7_\xa4%\x14U\n\xef5ڏ\xdaq\xcf7%\xb1\xdfĉ\x04\xf6\x93Y-\x95w\vD\x97E\xeb\xb78\xb0\v%\x11m\xd8&-\xdc*:\x9fy\xfa,aS\x86\x019\x8fVQY\xce\xe8*\xadV\xec\xa6\xc3j\v\x80v\xf1\xaaY\xa5M\x8fS\x17\v!\x8e\xa2X\xa3wO\xde\xca\xffr\x90\v?\xd6\f\x96\xb9H0\x85\xb4\xe2|;'ޅ\xc3G\x99@\x81\xe6\x11\xa1$\xbf\x11/T\v,\xb9o'Ha|h\x11Z\xed\x16\xd28\xc4V\xa4\xf5\x91#\x03\x9b\xa3\x86Odُ\x0f\x8f\xdb%\xbbw\x8e\x87\xa2\xa8߽\xd2]\xe6Y\x16\xf2\xeb0\x06\xf1H\xfa\xf0\xa3\x10\x9c\xec\xfd\x85\xdc+\x8b\xf7\xafq\xdePHc\xd7\xf0\x8e/\xb4s\xec\xce\x0fY\xc2\xceRQ \t\x13i\x81\xe4d'r\n\x1f\xc8x+\xc0\xdc\a\x13z{\x10Ař\x98\xe7L[\xef\xf3\xb7\x12s\xbe\xdd:\u007f\xc2\xfd\xf9Ł\xf5:\xbfU\xe7q0\xc9\xe6\x1f\x18\xad&j\xd1*\xdf\xc39\xffv\u0381\xd9\x12\x159!x[ \xd5\xd1C\xf9\xfay\xc9Q\x80\xce\xda!j\xa1\xc9\xcd%-\x85\xf0s\xbb\x88\x96\xe9R[\xb7\b\xadO\xda:\x9f\x00\xec\x85\xdb#\x19\u0098\xd3_\x9d5\x04\xb1uh\xc0:m\u0085(\x99\xddA\x82\x9c8o\xe7yO\xacn\xb2\x91\x1e0\x1d2\xcf[\v\xe1m\xfa\xb9\xbf)\xa5\xff\xcf\xc3L8Xbإ\xd1\tZ;/J\x91\x9ec&a\xdb$k\x85?\xbcm\xa3LsL*9\xb4e\xa18\x91\xf6\x84\x83\xcd͗Nޙ\xcc\x10\xfd\x1d#ʧ\xe0\b\\\xe8T\x14bx9\x1f\x8d\ued5f\x1d\x14\xb0\x06\xe6\x0fL\xe6\xb1b\xa3\xb2,n\xaeE\xf2\xb7\x16x\x14R\xdd\xf2B\xf0\xf6\x9b\x05+\x10L9\x9ez\x94\xb9\x0e\xf3[\x864\x1d\xb1\xe7W\bW͚\xefj\f\xf68{x\x93\x11\xcf)\xa0`Zi\xd7M\xd6\xd4+\xbd\xb1\xb0\x95ƺ\x16\xe1\x05P\xa5\xe5\xeb\xe4o{\xc6T7Ɯ|\xc4\xfc\xc9\xcf\xee\xa4\x153\xfd\\\x17F,9X\a\xe2gb\x87 \xb7 \x1d\xa0Jt\xa58\xe1E悖Y\x00\xd13\xd1;\x93H\x9fٙ\xac\xaa\"\x9e +\x96N\xa9f\xb3c\xdd)?\b\x19\x97\x9d\x82\xd3\xd8\xead\x81\xba\x9au\xedm\xebWv\xf8ٽ\n\x98B|\x91EU\x80(\x88-K\u038d[_?\x18\xcae<\xaf\x9f\x85tu\x05\xa1\xbfX]fM\x13]\x949:\x84\rn\xb5a{`e\x8aM\xf8P\xf3\u007f\xb4\xded\xaa\t\xd8\n\x99Wf\x81\x8d^̙\xa5\xe7\xb6\xda<\xbd\xfca,\x1e\x91\x15\x1332\xe9\xbe h\x9e\xf7\x1f\xa5Y\x162\u007f2\xf8\xf2\xa1ii$I\xa9\x9e\x8bNgar\xf4ڏNk\xe1\x15j?\x15\x9e\xceBeL^\xc3Ӧ\xbd\x86\xa7\xaf\xe1\xe9kx:h\xaf\xe1\xe9kx\xfa\x1a\x9e\x8e\xb7\xd7\xf0\xb4\xd3^\xc3\xd3h\xff\x11\x83\xe1\x8a3\xb7G\x06Da\x15Y\x821\x87\xf6\xccZu\xa5\xd1u^Y\x87fI\x85\xf4\xed\xf8̑\x1a\xfa\xc4\x0fY\xf1\x17zSRӖ\xae\xb4N\xaf)\x99&\x95\f\xca\xe4\xbfň\x88£ˠ\xa7\xab\xedc\v\xe8\xe6\xca\xe6\xfa\xb5\xe3M\xb9\x9a\xff\xdf\x04A\x9c\x0e\xcb\xd7\xdc\xf3\xdf\xf8tk\xae\xfa\xb5o|\x0e\b\x18\xff&\xeb\xca#\xcb\xdaf\x8aَ\x17\xe2Oy\xf8@\xcb\xc1\xe5B\x9f\x98\xa6W\xf8\xfd\x9b\xa6eD\xb5\xd9t\x8dY}k\x89N\xecޮ\xfb\xbf8]W\x9cM\xec\xecY\xba\xcc\u007f\u007fDGW\xf5\xd8-k\x0frZ\u007f\xfb7\xa4\xf1\x04Dm@\xc9\xdc3 @\xe8\x91\x1f~*\xfd\x11\xf9d=\x9f?\xa8\xc5ץ-\xadFk\xea\x87\xe6\xbd\xcaWԠ-\xfb@`\xb6\xde,\x06i\x88\xa92\x1b\xaf\x1f\x9b\x81\xba\xa4\xb6,\xf6\f\x1eQG\x16_=\x16G\x1e\xe0/vckƢ\xa3\xb6\xd8\xfa\xb0oS\x15\x16Y\v֩\xf0\x9a\x05yb\x05X4\xc1⪽\xa2k\xbc:\x95[\xf3\xd4:R\xd95^\xaf5\vr\xac\x9e+\xa6J+\n\xd7\xe8ڬ\xa6\xe2j>\x93\xf8U\x15Y/_\xfb\xfd\x92q\xfe\xf1\xfa\xaa\xa8\xaa\xaa\xa8\xb3\xc0<\xceQuSK\xab\xa5\xa2\xa8\xba\xb42\xaa\xa9z:\xb2pT=\xd4a\xadӱ\xad\xccVAMW8\x1d\x03;V\xfb\x14Q\xd7t\x04d\xb7\xe2iq\x180+M3\x03ƿ\xaa\x0fm\xde\xd7\xe6\xff\v\t\xfc\xdaMk\x93\xa2\x99=\x95,A}\x16\xed~\xber\xb0\xfe\xe0s\x9d\xf019\x8d\xea\x9ex\xa6\xa2(\xdd|>\x92\xc0\x8fR\xa5^\x92HY:1\r\xbfP\xc1\x05fM\x985]q\xdbF\xb4\x83Ӗ\xc5R\x90QOa\xb3\xf7Y!\xbb\x86\x1b\x91d\xcd\xc0\t\x88\xbcr&,\x9d\xec\v\xe1\xe0\xbc9\xc6^\x86\x99\xd4s\xbe\x06\xf8A7\x19\x84\xce\xe7\xa1\x13p\xad,\xca|\x0f\x95E8\xef\x03\xfa\xbaSؤ\xecX%J\x9b\xe9\xf0\x92@\xc4A\xec\xae?c$_\x12\xde\x11Hr]\xa5\xcd\nG\xd8-\xd4\x1e>=p$\xc7_O'\xedW\xe6u\xa4\x16\xceU\x83\x8f\xd0'@N=\x1e\xb1\x88d\xd3Y\x15\xeb\xb4\x11\x8f\xf8A\xfbw5bh֟\xd1{Z\xa5\xb6\xab!\xc7Z\x17MNJ\xb2\xdf\xdb\x10`{\xf5Rk[\x9b\x84\"l\xa7L\ue31e;\x97Gl\xee\xfe\xfe\x83ߐ\x93\x05\xae\xdfW>õ*\x85\xb1H\x94\x0e\x1b\xf5\x936Ӗ0\xd3ϐ\xeb\x9a\x0e\xdf\x0f\xf7a\x90\xafz8\x99v\xd2n\xfc\x13\x16A|\x03\xe9bD\xfea|f\xe7\xb0\xdcaⱜ\x98\xdeN\xc2\x12\xd6\xeaD\xb2-\xe2\x14\x05߰|\xbb7\x16\x8eEdGLFe\xf1\xa7g\x85\xe6sPT{\xab<\xa7f^]\xf9\xc7\xc1\xc4\xc0\xe01\xf3A\xf6o0|\xcc署@ֿ\xf7\xe6\xcd8\x13.\xbcCsH\xba\x19\xfd\x9f\xd6\xfd\xf1\x88z5\xfe\xf4˪y\x8d\xe6,\x82\xb2\xfe\xbd\xb1\x98\a~\xfc\xa3n\x89(]ej\xf7\x9aT\x86\x1f\x9c \xe8\xdfc8퉟\xf6\xb9\xb3\x19^\xb6\x0f\xa0\xb5\x19\x86\xd9\xe7\xd6F\xf8\xd7<\x164\xf9z\x8e\xf7\xae\xfe9\xb4\x15\xc1?\x8d\x9d\xa3z\xc0\x0ft\xcc\xec\xf4\x13\x8din\xc7kB\xf3\xc4\xf0\xb0\xc7\xdd\x14\xea\xe3ם+\xf8\x88\xcf#\xbd7\x8a6qx\x18\xf5w\x9a\x98r\x86b\xecy\xb3\xa3[\xdc5\xb3\xf8By\xc4Z\xf4\xcd\xdc`\xf8 S-\xf2\xbc\x03\xd1_\x1e\x8f\xb1\xf5\x8fr\xeb\xd3F\t\xed\xe9O\a#&\r\xd7Q\xa35e\xb0FU\xea\xa0Ӣ\xd9a\xda\x11\x92ڇw{\xaa\xcd\xc1;+\xb5b\xc2/\xbf\x9e\xb5:*\x92\x04KWߏt\x1f\x85p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\x03V\xa9\x92%\xa4e\x06\xddMƷ\xca\xd2\xfbG\xbd~Sr\xf6\xcf\x12\x9a50\xab\x88\xb4{\xba\x8b\fk\x9cT\xa1\xb4F\x06\x859:\u007f\xc1\xfd\xf4_rQ#\ay\xc7\xd5\xd7&H\xcb#\x84\xd2X\x97\x80\xebF\x91e\x1ffJ\\;>\xf7xgY\r\xbf\x86]\xe6o\a;\xec\x16\x0f\x13\xf7խ\f\xd8\x1d'\xd2ޝm\xc3袹[{\xc76\xa1\x85.\xa5\x8b\v$\xa5\xc4\xfe\xbfu\xcbB\xea1\xe7P\xd4\x02\xbb[\x01vy\x00L\xf0;\x96\x83\xd24/\x0ePȫ\xed7\xcc\x06\b\x99\xaa\xaa\xb2a3$X7\xd9\xed2\x90i\xdd\xda9\x9d6`[0\xa8_\x18А\x12X\x01'\xeeR?f\xdaڀc\a\xd0;T\xc0\xe5\ns\u007f<\x1c\xbc\xdf7\x17\x92`\x17\xedj\xea\xdb\x14\xe1KE\xa5Tä\xb3rH\xaf\x93\xd8)E\xf1Z\xee!V\x83w\x9d\x9d@M\xf0^\x88\xd9\xde,\xb3o\xfb\x9bƮ\xb4\xc7\x03H \v\xe0\x06ŝ\x1cǩe\xb6\xb5\xabA\xac;\xc1U\xc0\xf6\xcev\xc3-\xa9\xfb\x80U\xd4+/R\x97\a\x02)\x19\x1f鬪\xb0\xaf`\x96\xbb\xe1\xfd\x0e\xa8꺧\xd0B\xc4\xd7\xcdg\x9d\xf6mq\x80KO\xa8틽\x04\x02\\3\t\x9d\xa4\xef\xa6$\xf0\xcb\x01g\x9c\x90bI\xd5!vyc\x9e\xa9Z\xf06\x0ee\xc5)\xdf\xed\x98\x13\xf02\xdf\x06>!\xd7\xf0\xd0\xf1\xeb\xd7H\xf4hQu\x1f\xa5\t\xb9\xe27R,dWW\x88\x89?X\x1d\x142!7TjF\xb3l\xfduw\xf7I\xff\xf5 ܹ\xa9\x1c\xd4\x12\xecc\xb5\xd2ĸ=\u007f\x86R\xe9\f\xbb\xbb\xd6\xc4z\xa2j:\xee\xf6\xd8 \xb4\xa91\xaa\xc0\x1b\xe3\xac\r\x14\xaf\\(=\x81\xf9\\Hm\x95\xb4Ʉ\xb0\xb9\xe3\x9f]\xca\ae\x19\xbalm\xd2\x1ca\xba\x0e\bU\x02\xc9\xd8\xfa|M$\x12+\xb6\xfa\xcd\xe9ڦ\xdc\xd0$)\xcd\xf1geNY\xb7\xde|\xa8\x88\x1e\xd6@\xb9\xdam\xe8\xb5\x13쪇\xfd\x02l\t\x95\xade\x88\xa6K|w\xfa\x00S\xfeU\xb3g\xc9\x12+h\xeb\xa5\x14\xe5b\xe9Ip\x17\x03\xdde'\x95X\xe4\xa7\xc8ʅ!k\xe7\xd4ҥ\xe4\r\x9b̹\xb9\xd2z\xba\xfb\x80\xeeG\xe1\x1e\xb5_\xb5$\xde!\x05\xb0\xf5\xf00\xc9^\x95\xb7\xfat%\xf2\xaab\xa9\x97}d\xf3\xfb\x8d\xc77\x12\xa7\x8c\x94\xae!:yځ\x9cS6\xb7\x9e\xc1\xc4\xccz;Y\xff\x03'@=P\xc9\x19_\x1cZ\xfc\xf7\xee\xb1\x0e\xd5\xc4A\xe8PN:\x16Q\xa9+Aʉ\x9f\xe4\x8e\xdc\xfeJa\x19\xa0\x9et\x9e\xa1\xad\x1f\x91\x90\xd3\x06\x92ݗ\xdc/\xb5Zo\xcbڹ\xc4D\x8b\xdb{\xc6ӗ\xfe\x02P\x91\x95\x92f\ue7c9\xe0\x96-\xa8\x97\xe4\x87\x1f\xbf\xf0\vz\x0fRU?\xfe\xbf\x00\x00\x00\xff\xff\a{y+\x88\xf9\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ars\xf4\\d\xb50\xe1\x8f?Ϛ5*\x92\x04\vW\xa5y\xb5߶??\xe7\x1f\xe1\xe9z\xfe\x99h\xe5\xddn{\x05\xff\xfd?gP\x99\xe3\x87\xf0>=U\xfe_\x00\x00\x00\xff\xff\x11z\x10\b\t`\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V͎\xe44\x10\xbe\xe7)J\xcba/$\xbd+\x0e\xa0\xdc\xd0\xc0a\x05\x8cFӫ\xb9 \x0en\xa7\xd2mƱCU\xb9\x87\x06\xf1\xee\xc8vҝN\xd2̀\x84o\xb1\xeb端~REY\x96\x85\xea\xcd\x13\x12\x1b\xefjP\xbd\xc1\xdf\x05]\xfc\xe2\xea\xf9\x1b\xae\x8c\xdf\x1c?\x16\xcf\xc655\xdc\x05\x16\xdf=\"\xfb@\x1a\xbf\xc3\xd68#ƻ\xa2CQ\x8d\x12U\x17\x00\xca9/*^s\xfc\x04\xd0\xde\tyk\x91\xca=\xba\xea9\xecp\x17\x8cm\x90\x92\xf1\xd1\xf5\xf1C\xf5u\xf5\xa1\x00ЄI\xfd\xb3\xe9\x90Eu}\r.X[\x008\xd5a\r\x8c\x14\x95DI`\xc2\xdf\x02\xb2puD\x8b\xe4+\xe3\v\xeeQG\xc7{\xf2\xa1\xaf\xe1\xf2\x90\xf5\aP9\xa0m2\xb5M\xa6\x1e\xb3\xa9\xf4j\r\xcb\x0f\xb7$~4\x83To\x03)\xbb\x0e(\t\xf0\xc1\x93\xdc_\x9c\x96\xc0L\xf9Ÿ}\xb0\x8aV\x95\v\x00־\xc7\x1a\x92n\xaf46\x05\xc0\xc0T\xb2U\x0e\\\x1c?fs\xfa\x80\x9d\xcaN\x00|\x8f\xeeۇOO_m\xaf\xae\x01\x1adM\xa6\x97\xc4\xf7Jd`\x18\x14\f(@<(\xad\x91\x19t B'\x90Q\x82q\xad\xa7.\xe5\xe8l\x1a@\xed|\x10\x90\x03\xc2S\xa2|\x88\xac:\x8b\xf4\xe4{$1#\x1b\x83ڥ\xfa&\xb73\xac\xefc8Y\n\x9aXv\xc8\xc9\xd3@\t6\x03\x03\xe0[\x90\x83a \xec\t\x19\x9d\xccQ&~ZP\x0e\xfc\xeeW\xd4R\r9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 51f5f6ea3a..7fc0f7a4c6 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -59,6 +59,15 @@ type BackupSpec struct { // +nullable LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` + // OrLabelSelectors is list of metav1.LabelSelector to filter with + // when adding individual objects to the backup. If multiple provided + // they will be joined by the OR operator. LabelSelector as well as + // OrLabelSelectors cannot co-exist in backup request, only one of them + // can be used. + // +optional + // +nullable + OrLabelSelectors []*metav1.LabelSelector `json:"orLabelSelectors,omitempty"` + // SnapshotVolumes specifies whether to take cloud snapshots // of any PV's referenced in the set of objects included // in the Backup. diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index 1b1b8c05a8..aab5b35c97 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -71,6 +71,15 @@ type RestoreSpec struct { // +nullable LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` + // OrLabelSelectors is list of metav1.LabelSelector to filter with + // when restoring individual objects from the backup. If multiple provided + // they will be joined by the OR operator. LabelSelector as well as + // OrLabelSelectors cannot co-exist in restore request, only one of them + // can be used + // +optional + // +nullable + OrLabelSelectors []*metav1.LabelSelector `json:"orLabelSelectors,omitempty"` + // RestorePVs specifies whether to restore all included // PVs from snapshot (via the cloudprovider). // +optional diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 4c905d4f7e..aee977c6d2 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -210,6 +210,17 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.OrLabelSelectors != nil { + in, out := &in.OrLabelSelectors, &out.OrLabelSelectors + *out = make([]*metav1.LabelSelector, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + } + } if in.SnapshotVolumes != nil { in, out := &in.SnapshotVolumes, &out.SnapshotVolumes *out = new(bool) @@ -1251,6 +1262,17 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.OrLabelSelectors != nil { + in, out := &in.OrLabelSelectors, &out.OrLabelSelectors + *out = make([]*metav1.LabelSelector, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + } + } if in.RestorePVs != nil { in, out := &in.RestorePVs, &out.RestorePVs *out = new(bool) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 0747409bbe..53554e8b38 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -331,6 +331,35 @@ func TestBackupResourceFiltering(t *testing.T) { "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", }, }, + { + name: "OrLabelSelector only backs up matching resources", + backup: defaultBackup(). + OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, + {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a1", "b1")).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a2", "b2")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a4", "b4")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a5", "b5")).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + }, + }, { name: "resources with velero.io/exclude-from-backup=true label are not included", backup: defaultBackup(). diff --git a/pkg/backup/item_collector.go b/pkg/backup/item_collector.go index b3ebb9c0dc..5cbc178361 100644 --- a/pkg/backup/item_collector.go +++ b/pkg/backup/item_collector.go @@ -291,53 +291,44 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group continue } - var labelSelector string - if selector := r.backupRequest.Spec.LabelSelector; selector != nil { - labelSelector = metav1.FormatLabelSelector(selector) + var orLabelSelectors []string + if r.backupRequest.Spec.OrLabelSelectors != nil { + for _, s := range r.backupRequest.Spec.OrLabelSelectors { + orLabelSelectors = append(orLabelSelectors, metav1.FormatLabelSelector(s)) + } + } else { + orLabelSelectors = []string{} } log.Info("Listing items") unstructuredItems := make([]unstructured.Unstructured, 0) - if r.pageSize > 0 { - // If limit is positive, use a pager to split list over multiple requests - // Use Velero's dynamic list function instead of the default - listPager := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) { - return resourceClient.List(opts) - })) - // Use the page size defined in the server config - // TODO allow configuration of page buffer size - listPager.PageSize = int64(r.pageSize) - // Add each item to temporary slice - list, paginated, err := listPager.List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector}) + // Listing items for orLabelSelectors + errListingForNS := false + for _, label := range orLabelSelectors { + unstructuredItems, err = r.listItemsForLabel(unstructuredItems, gr, label, resourceClient) if err != nil { - log.WithError(errors.WithStack(err)).Error("Error listing resources") - continue + errListingForNS = true } - if !paginated { - log.Infof("list for groupResource %s was not paginated", gr) - } - err = meta.EachListItem(list, func(object runtime.Object) error { - u, ok := object.(*unstructured.Unstructured) - if !ok { - log.WithError(errors.WithStack(fmt.Errorf("expected *unstructured.Unstructured but got %T", u))).Error("unable to understand entry in the list") - return fmt.Errorf("expected *unstructured.Unstructured but got %T", u) - } - unstructuredItems = append(unstructuredItems, *u) - return nil - }) - if err != nil { - log.WithError(errors.WithStack(err)).Error("unable to understand paginated list") - continue - } - } else { - // If limit is not positive, do not use paging. Instead, request all items at once - unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: labelSelector}) + } + + if errListingForNS { + log.WithError(err).Error("Error listing items") + continue + } + + var labelSelector string + if selector := r.backupRequest.Spec.LabelSelector; selector != nil { + labelSelector = metav1.FormatLabelSelector(selector) + } + + // Listing items for labelSelector (singular) + if len(orLabelSelectors) == 0 { + unstructuredItems, err = r.listItemsForLabel(unstructuredItems, gr, labelSelector, resourceClient) if err != nil { - log.WithError(errors.WithStack(err)).Error("Error listing items") + log.WithError(err).Error("Error listing items") continue } - unstructuredItems = append(unstructuredItems, unstructuredList.Items...) } log.Infof("Retrieved %d items", len(unstructuredItems)) @@ -467,3 +458,60 @@ func newCohabitatingResource(resource, group1, group2 string) *cohabitatingResou seen: false, } } + +// function to process pager client calls when the pageSize is specified +func (r *itemCollector) processPagerClientCalls(gr schema.GroupResource, label string, resourceClient client.Dynamic) (runtime.Object, error) { + // If limit is positive, use a pager to split list over multiple requests + // Use Velero's dynamic list function instead of the default + listPager := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) { + return resourceClient.List(opts) + })) + // Use the page size defined in the server config + // TODO allow configuration of page buffer size + listPager.PageSize = int64(r.pageSize) + // Add each item to temporary slice + list, paginated, err := listPager.List(context.Background(), metav1.ListOptions{LabelSelector: label}) + + if err != nil { + r.log.WithError(errors.WithStack(err)).Error("Error listing resources") + return list, err + } + + if !paginated { + r.log.Infof("list for groupResource %s was not paginated", gr) + } + + return list, nil +} + +func (r *itemCollector) listItemsForLabel(unstructuredItems []unstructured.Unstructured, gr schema.GroupResource, label string, resourceClient client.Dynamic) ([]unstructured.Unstructured, error) { + if r.pageSize > 0 { + // process pager client calls + list, err := r.processPagerClientCalls(gr, label, resourceClient) + if err != nil { + return unstructuredItems, err + } + + err = meta.EachListItem(list, func(object runtime.Object) error { + u, ok := object.(*unstructured.Unstructured) + if !ok { + r.log.WithError(errors.WithStack(fmt.Errorf("expected *unstructured.Unstructured but got %T", u))).Error("unable to understand entry in the list") + return fmt.Errorf("expected *unstructured.Unstructured but got %T", u) + } + unstructuredItems = append(unstructuredItems, *u) + return nil + }) + if err != nil { + r.log.WithError(errors.WithStack(err)).Error("unable to understand paginated list") + return unstructuredItems, err + } + } else { + unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: label}) + if err != nil { + r.log.WithError(errors.WithStack(err)).Error("Error listing items") + return unstructuredItems, err + } + unstructuredItems = append(unstructuredItems, unstructuredList.Items...) + } + return unstructuredItems, nil +} diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index f307416a37..0fa2a3b7fe 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -162,6 +162,12 @@ func (b *BackupBuilder) LabelSelector(selector *metav1.LabelSelector) *BackupBui return b } +// OrLabelSelector sets the Backup's orLabelSelector set. +func (b *BackupBuilder) OrLabelSelector(orSelectors []*metav1.LabelSelector) *BackupBuilder { + b.object.Spec.OrLabelSelectors = orSelectors + return b +} + // SnapshotVolumes sets the Backup's "snapshot volumes" flag. func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { b.object.Spec.SnapshotVolumes = &val diff --git a/pkg/builder/restore_builder.go b/pkg/builder/restore_builder.go index 4fa24ac35f..a30c5a9b36 100644 --- a/pkg/builder/restore_builder.go +++ b/pkg/builder/restore_builder.go @@ -113,6 +113,12 @@ func (b *RestoreBuilder) LabelSelector(selector *metav1.LabelSelector) *RestoreB return b } +// OrLabelSelector sets the Restore's orLabelSelector set. +func (b *RestoreBuilder) OrLabelSelector(orSelectors []*metav1.LabelSelector) *RestoreBuilder { + b.object.Spec.OrLabelSelectors = orSelectors + return b +} + // NamespaceMappings sets the Restore's namespace mappings. func (b *RestoreBuilder) NamespaceMappings(mapping ...string) *RestoreBuilder { if b.object.Spec.NamespaceMapping == nil { diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 2eb59bf4b2..4aea251563 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -461,6 +461,11 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err)) } + // validate that only one exists orLabelSelector or just labelSelector (singular) + if request.Spec.OrLabelSelectors != nil && request.Spec.LabelSelector != nil { + request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified")) + } + return request } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index fffa831486..fa1a95a382 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -189,6 +189,13 @@ func TestProcessBackupValidationFailures(t *testing.T) { backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(), expectedErrs: []string{"backup can't be created because backup storage location read-only is currently in read-only mode"}, }, + { + name: "labelSelector as well as orLabelSelectors both are specified in backup request fails validation", + backup: defaultBackup().LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}).OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, + {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}).Result(), + backupLocation: defaultBackupLocation, + expectedErrs: []string{"encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified"}, + }, } for _, test := range tests { diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 4d01b36dfa..910bd56cd7 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -326,6 +326,11 @@ func (c *restoreController) validateAndComplete(restore *api.Restore, pluginMana restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err)) } + // validate that only one exists orLabelSelector or just labelSelector (singular) + if restore.Spec.OrLabelSelectors != nil && restore.Spec.LabelSelector != nil { + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, fmt.Sprintf("encountered labelSelector as well as orLabelSelectors in restore spec, only one can be specified")) + } + // validate that exactly one of BackupName and ScheduleName have been specified if !backupXorScheduleProvided(restore) { restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, "Either a backup or schedule must be specified as a source for the restore, but not both") diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 1e37f2f567..d7ee568a5c 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -308,6 +308,15 @@ func TestProcessQueueItem(t *testing.T) { expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, }, + { + name: "new restore with labelSelector as well as orLabelSelector fails validation", + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}).OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}).Result(), + backup: defaultBackup().StorageLocation("default").Result(), + expectedErr: false, + expectedValidationErrors: []string{"encountered labelSelector as well as orLabelSelectors in restore spec, only one can be specified"}, + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), + }, { name: "valid restore with schedule name gets executed", location: defaultStorageLocation, diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 63bb402551..d22ddbc378 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -183,6 +183,17 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( ls = &metav1.LabelSelector{} } + var OrSelectors []labels.Selector + if req.Restore.Spec.OrLabelSelectors != nil { + for _, s := range req.Restore.Spec.OrLabelSelectors { + labelAsSelector, err := metav1.LabelSelectorAsSelector(s) + if err != nil { + return Result{}, Result{Velero: []string{err.Error()}} + } + OrSelectors = append(OrSelectors, labelAsSelector) + } + } + selector, err := metav1.LabelSelectorAsSelector(ls) if err != nil { return Result{}, Result{Velero: []string{err.Error()}} @@ -264,6 +275,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( namespaceIncludesExcludes: namespaceIncludesExcludes, chosenGrpVersToRestore: make(map[string]ChosenGroupVersion), selector: selector, + OrSelectors: OrSelectors, log: req.Log, dynamicFactory: kr.dynamicFactory, fileSystem: kr.fileSystem, @@ -305,6 +317,7 @@ type restoreContext struct { namespaceIncludesExcludes *collections.IncludesExcludes chosenGrpVersToRestore map[string]ChosenGroupVersion selector labels.Selector + OrSelectors []labels.Selector log logrus.FieldLogger dynamicFactory client.DynamicFactory fileSystem filesystem.Interface @@ -1862,6 +1875,27 @@ func (ctx *restoreContext) getSelectedRestoreableItems(resource, targetNamespace continue } + // Processing OrLabelSelectors when specified in the restore request. LabelSelectors as well as OrLabelSelectors + // cannot co-exist, only one of them can be specified + var skipItem = false + var skip = 0 + ctx.log.Debugf("orSelectors specified: %s for item: %s", ctx.OrSelectors, item) + for _, s := range ctx.OrSelectors { + if !s.Matches(labels.Set(obj.GetLabels())) { + skip++ + } + + if len(ctx.OrSelectors) == skip && skip > 0 { + ctx.log.Infof("setting skip flag to true for item: %s", item) + skipItem = true + } + } + + if skipItem { + ctx.log.Infof("restore orSelector labels did not match, skipping restore of item: %s", skipItem, item) + continue + } + selectedItem := restoreableItem{ path: itemPath, name: item, diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 5c19611630..973c91cb8c 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -252,6 +252,36 @@ func TestRestoreResourceFiltering(t *testing.T) { test.PVs(): {"/pv-1"}, }, }, + { + name: "OrLabelSelectors only restores matching resources", + restore: defaultRestore().OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, + {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}).Result(), + backup: defaultBackup().Result(), + tarball: test.NewTarWriter(t). + AddItems("pods", + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("a1", "b1")).Result(), + builder.ForPod("ns-2", "pod-2").Result(), + ). + AddItems("deployments.apps", + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").ObjectMeta(builder.WithLabels("a3", "b3")).Result(), + ). + AddItems("persistentvolumes", + builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabels("a5", "b5")).Result(), + builder.ForPersistentVolume("pv-2").ObjectMeta(builder.WithLabels("a4", "b4")).Result(), + ). + Done(), + apiResources: []*test.APIResource{ + test.Pods(), + test.Deployments(), + test.PVs(), + }, + want: map[*test.APIResource][]string{ + test.Pods(): {"ns-1/pod-1"}, + test.Deployments(): {"ns-2/deploy-2"}, + test.PVs(): {"/pv-2"}, + }, + }, { name: "should include cluster-scoped resources if restoring subset of namespaces and IncludeClusterResources=true", restore: defaultRestore().IncludedNamespaces("ns-1").IncludeClusterResources(true).Result(), diff --git a/site/content/docs/main/api-types/backup.md b/site/content/docs/main/api-types/backup.md index 3955bba8cd..37cb30bf9e 100644 --- a/site/content/docs/main/api-types/backup.md +++ b/site/content/docs/main/api-types/backup.md @@ -59,6 +59,13 @@ spec: matchLabels: app: velero component: server + # Individual object when matched with any of the label selector specified in the set are to be included in the backup. Optional. + # orLabelSelectors as well as labelSelector cannot co-exist, only one of them can be specified in the backup request + orLabelSelectors: + - matchLabels: + app: velero + - matchLabels: + app: data-protection # Whether or not to snapshot volumes. This only applies to PersistentVolumes for Azure, GCE, and # AWS. Valid values are true, false, and null/unset. If unset, Velero performs snapshots as long as # a persistent volume provider is configured for Velero. diff --git a/site/content/docs/main/api-types/restore.md b/site/content/docs/main/api-types/restore.md index daa293e923..a8d064a6ef 100644 --- a/site/content/docs/main/api-types/restore.md +++ b/site/content/docs/main/api-types/restore.md @@ -61,6 +61,13 @@ spec: matchLabels: app: velero component: server + # Individual object when matched with any of the label selector specified in the set are to be included in the restore. Optional. + # orLabelSelectors as well as labelSelector cannot co-exist, only one of them can be specified in the restore request + orLabelSelectors: + - matchLabels: + app: velero + - matchLabels: + app: data-protection # NamespaceMapping is a map of source namespace names to # target namespace names to restore into. Any source namespaces not # included in the map will be restored into namespaces of the same name. From 6a721403fb7a9767bf0e25d5b3a659d2d14d7a20 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 10:46:48 -0300 Subject: [PATCH 141/366] Fix DeepEqual when status is updated Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index d22ddbc378..cbb335f278 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1227,6 +1227,18 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // and which backup they came from. addRestoreLabels(obj, ctx.restore.Name, ctx.restore.Spec.BackupName) + // Clear out status. + + objWithoutStatus := obj.DeepCopy() + if objWithoutStatus, err = resetStatus(objWithoutStatus); err != nil { + errs.Add(namespace, err) + return warnings, errs + } + if !shouldRestoreStatus { + ctx.log.Infof("Resetting status for obj %s/%s", obj.GetKind(), obj.GetName()) + obj = objWithoutStatus + } + ctx.log.Infof("Attempting to restore %s: %v", obj.GroupVersionKind().Kind, name) createdObj, restoreErr := resourceClient.Create(obj) isAlreadyExistsError, err := isAlreadyExistsError(ctx, obj, restoreErr, resourceClient) @@ -1255,7 +1267,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso addRestoreLabels(fromCluster, labels[velerov1api.RestoreNameLabel], labels[velerov1api.BackupNameLabel]) fromClusterWithLabels := fromCluster.DeepCopy() // saving the in-cluster object so that we can create label patch if overall patch fails - if !equality.Semantic.DeepEqual(fromCluster, obj) { + if !equality.Semantic.DeepEqual(fromCluster, objWithoutStatus) { switch groupResource { case kuberesource.ServiceAccounts: desired, err := mergeServiceAccounts(fromCluster, obj) From e374eb9da4e417d741d111955ec422987b638067 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Fri, 25 Mar 2022 13:05:11 -0300 Subject: [PATCH 142/366] Add StatusUpdater Signed-off-by: Rafael Leal --- pkg/client/dynamic.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/client/dynamic.go b/pkg/client/dynamic.go index 1148418050..8fcfab107b 100644 --- a/pkg/client/dynamic.go +++ b/pkg/client/dynamic.go @@ -89,6 +89,11 @@ type Deletor interface { Delete(name string, opts metav1.DeleteOptions) error } +// StatusUpdater updates status field of a object +type StatusUpdater interface { + UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) +} + // Dynamic contains client methods that Velero needs for backing up and restoring resources. type Dynamic interface { Creator @@ -97,6 +102,7 @@ type Dynamic interface { Getter Patcher Deletor + StatusUpdater } // dynamicResourceClient implements Dynamic. @@ -129,3 +135,7 @@ func (d *dynamicResourceClient) Patch(name string, data []byte) (*unstructured.U func (d *dynamicResourceClient) Delete(name string, opts metav1.DeleteOptions) error { return d.resourceClient.Delete(context.TODO(), name, opts) } + +func (d *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) { + return d.resourceClient.UpdateStatus(context.TODO(), obj, opts) +} From 7f1f881c2813b0ee301f08931822cfd9aa8975fe Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 11:58:47 -0300 Subject: [PATCH 143/366] Add resource status Signed-off-by: Rafael Leal --- pkg/apis/velero/v1/restore.go | 19 ++++ pkg/restore/restore.go | 168 ++++++++++++++++++++-------------- 2 files changed, 116 insertions(+), 71 deletions(-) diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index aab5b35c97..6dfb0a959c 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -86,6 +86,12 @@ type RestoreSpec struct { // +nullable RestorePVs *bool `json:"restorePVs,omitempty"` + // RestoreStatus specifies which resources we should restore the status + // field. If nil, no objects are included. Optional. + // +optional + // +nullable + RestoreStatus *RestoreStatusSpec + // PreserveNodePorts specifies whether to restore old nodePorts from backup. // +optional // +nullable @@ -113,6 +119,19 @@ type RestoreHooks struct { Resources []RestoreResourceHookSpec `json:"resources,omitempty"` } +type RestoreStatusSpec struct { + // IncludedResources specifies the resources to which will restore the status. + // If empty, it applies to all resources. + // +optional + // +nullable + IncludedResources []string `json:"includedResources,omitempty"` + + // ExcludedResources specifies the resources to which will not restore the status. + // +optional + // +nullable + ExcludedResources []string `json:"excludedResources,omitempty"` +} + // RestoreResourceHookSpec defines one or more RestoreResrouceHooks that should be executed based on // the rules defined for namespaces, resources, and label selector. type RestoreResourceHookSpec struct { diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index cbb335f278..3761477be1 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -206,6 +206,20 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( req.Restore.Spec.ExcludedResources, ) + // Get resource status includes-excludes. Defaults to excluding all resources + restoreStatusIncludesExcludes := collections.GetResourceIncludesExcludes( + kr.discoveryHelper, + []string{}, + []string{"*"}, + ) + if req.Restore.Spec.RestoreStatus != nil { + restoreStatusIncludesExcludes = collections.GetResourceIncludesExcludes( + kr.discoveryHelper, + req.Restore.Spec.RestoreStatus.IncludedResources, + req.Restore.Spec.RestoreStatus.ExcludedResources, + ) + } + // Get namespace includes-excludes. namespaceIncludesExcludes := collections.NewIncludesExcludes(). Includes(req.Restore.Spec.IncludedNamespaces...). @@ -268,83 +282,85 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( } restoreCtx := &restoreContext{ - backup: req.Backup, - backupReader: req.BackupReader, - restore: req.Restore, - resourceIncludesExcludes: resourceIncludesExcludes, - namespaceIncludesExcludes: namespaceIncludesExcludes, - chosenGrpVersToRestore: make(map[string]ChosenGroupVersion), - selector: selector, - OrSelectors: OrSelectors, - log: req.Log, - dynamicFactory: kr.dynamicFactory, - fileSystem: kr.fileSystem, - namespaceClient: kr.namespaceClient, - restoreItemActions: resolvedActions, - itemSnapshotterActions: resolvedItemSnapshotterActions, - volumeSnapshotterGetter: volumeSnapshotterGetter, - resticRestorer: resticRestorer, - resticErrs: make(chan error), - pvsToProvision: sets.NewString(), - pvRestorer: pvRestorer, - volumeSnapshots: req.VolumeSnapshots, - podVolumeBackups: req.PodVolumeBackups, - resourceTerminatingTimeout: kr.resourceTerminatingTimeout, - resourceClients: make(map[resourceClientKey]client.Dynamic), - restoredItems: make(map[velero.ResourceIdentifier]struct{}), - renamedPVs: make(map[string]string), - pvRenamer: kr.pvRenamer, - discoveryHelper: kr.discoveryHelper, - resourcePriorities: kr.resourcePriorities, - resourceRestoreHooks: resourceRestoreHooks, - hooksErrs: make(chan error), - waitExecHookHandler: waitExecHookHandler, - hooksContext: hooksCtx, - hooksCancelFunc: hooksCancelFunc, - restoreClient: kr.restoreClient, + backup: req.Backup, + backupReader: req.BackupReader, + restore: req.Restore, + resourceIncludesExcludes: resourceIncludesExcludes, + resourceStatusIncludesExcludes: restoreStatusIncludesExcludes, + namespaceIncludesExcludes: namespaceIncludesExcludes, + chosenGrpVersToRestore: make(map[string]ChosenGroupVersion), + selector: selector, + OrSelectors: OrSelectors, + log: req.Log, + dynamicFactory: kr.dynamicFactory, + fileSystem: kr.fileSystem, + namespaceClient: kr.namespaceClient, + restoreItemActions: resolvedActions, + itemSnapshotterActions: resolvedItemSnapshotterActions, + volumeSnapshotterGetter: volumeSnapshotterGetter, + resticRestorer: resticRestorer, + resticErrs: make(chan error), + pvsToProvision: sets.NewString(), + pvRestorer: pvRestorer, + volumeSnapshots: req.VolumeSnapshots, + podVolumeBackups: req.PodVolumeBackups, + resourceTerminatingTimeout: kr.resourceTerminatingTimeout, + resourceClients: make(map[resourceClientKey]client.Dynamic), + restoredItems: make(map[velero.ResourceIdentifier]struct{}), + renamedPVs: make(map[string]string), + pvRenamer: kr.pvRenamer, + discoveryHelper: kr.discoveryHelper, + resourcePriorities: kr.resourcePriorities, + resourceRestoreHooks: resourceRestoreHooks, + hooksErrs: make(chan error), + waitExecHookHandler: waitExecHookHandler, + hooksContext: hooksCtx, + hooksCancelFunc: hooksCancelFunc, + restoreClient: kr.restoreClient, } return restoreCtx.execute() } type restoreContext struct { - backup *velerov1api.Backup - backupReader io.Reader - restore *velerov1api.Restore - restoreDir string - restoreClient velerov1client.RestoresGetter - resourceIncludesExcludes *collections.IncludesExcludes - namespaceIncludesExcludes *collections.IncludesExcludes - chosenGrpVersToRestore map[string]ChosenGroupVersion - selector labels.Selector - OrSelectors []labels.Selector - log logrus.FieldLogger - dynamicFactory client.DynamicFactory - fileSystem filesystem.Interface - namespaceClient corev1.NamespaceInterface - restoreItemActions []framework.RestoreItemResolvedAction - itemSnapshotterActions []framework.ItemSnapshotterResolvedAction - volumeSnapshotterGetter VolumeSnapshotterGetter - resticRestorer restic.Restorer - resticWaitGroup sync.WaitGroup - resticErrs chan error - pvsToProvision sets.String - pvRestorer PVRestorer - volumeSnapshots []*volume.Snapshot - podVolumeBackups []*velerov1api.PodVolumeBackup - resourceTerminatingTimeout time.Duration - resourceClients map[resourceClientKey]client.Dynamic - restoredItems map[velero.ResourceIdentifier]struct{} - renamedPVs map[string]string - pvRenamer func(string) (string, error) - discoveryHelper discovery.Helper - resourcePriorities []string - hooksWaitGroup sync.WaitGroup - hooksErrs chan error - resourceRestoreHooks []hook.ResourceRestoreHook - waitExecHookHandler hook.WaitExecHookHandler - hooksContext go_context.Context - hooksCancelFunc go_context.CancelFunc + backup *velerov1api.Backup + backupReader io.Reader + restore *velerov1api.Restore + restoreDir string + restoreClient velerov1client.RestoresGetter + resourceIncludesExcludes *collections.IncludesExcludes + resourceStatusIncludesExcludes *collections.IncludesExcludes + namespaceIncludesExcludes *collections.IncludesExcludes + chosenGrpVersToRestore map[string]ChosenGroupVersion + selector labels.Selector + OrSelectors []labels.Selector + log logrus.FieldLogger + dynamicFactory client.DynamicFactory + fileSystem filesystem.Interface + namespaceClient corev1.NamespaceInterface + restoreItemActions []framework.RestoreItemResolvedAction + itemSnapshotterActions []framework.ItemSnapshotterResolvedAction + volumeSnapshotterGetter VolumeSnapshotterGetter + resticRestorer restic.Restorer + resticWaitGroup sync.WaitGroup + resticErrs chan error + pvsToProvision sets.String + pvRestorer PVRestorer + volumeSnapshots []*volume.Snapshot + podVolumeBackups []*velerov1api.PodVolumeBackup + resourceTerminatingTimeout time.Duration + resourceClients map[resourceClientKey]client.Dynamic + restoredItems map[velero.ResourceIdentifier]struct{} + renamedPVs map[string]string + pvRenamer func(string) (string, error) + discoveryHelper discovery.Helper + resourcePriorities []string + hooksWaitGroup sync.WaitGroup + hooksErrs chan error + resourceRestoreHooks []hook.ResourceRestoreHook + waitExecHookHandler hook.WaitExecHookHandler + hooksContext go_context.Context + hooksCancelFunc go_context.CancelFunc } type resourceClientKey struct { @@ -1356,6 +1372,16 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } + // if it should restore status, run a UpdateStatus + if ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) { + updated, err := resourceClient.UpdateStatus(obj, metav1.UpdateOptions{}) + if err != nil { + warnings.Add(namespace, err) + } else { + createdObj = updated + } + } + if groupResource == kuberesource.Pods { pod := new(v1.Pod) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pod); err != nil { From 0b9d6ae73d5322cd5ed02bdd43879c56c37aa20f Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Mon, 28 Mar 2022 14:52:01 -0300 Subject: [PATCH 144/366] Add restore status mechanism Signed-off-by: Rafael Leal --- pkg/apis/velero/v1/restore.go | 2 +- pkg/restore/restore.go | 36 +++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index 6dfb0a959c..251f1a0453 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -90,7 +90,7 @@ type RestoreSpec struct { // field. If nil, no objects are included. Optional. // +optional // +nullable - RestoreStatus *RestoreStatusSpec + RestoreStatus *RestoreStatusSpec `json:"restoreStatus,omitempty"` // PreserveNodePorts specifies whether to restore old nodePorts from backup. // +optional diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 3761477be1..b13e97f4cf 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1127,19 +1127,30 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } } - // Clear out non-core metadata fields and status. - if obj, err = resetMetadataAndStatus(obj); err != nil { + // Clear out non-core metadata fields. + if obj, err = resetMetadata(obj); err != nil { errs.Add(namespace, err) return warnings, errs } + shouldRestoreStatus := ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) + + ctx.log.Infof("restore status includes excludes: %+v", ctx.resourceStatusIncludesExcludes) + // Clear out status. + if !shouldRestoreStatus { + ctx.log.Infof("Resetting status for obj %s/%s", obj.GetKind(), obj.GetName()) + if obj, err = resetStatus(obj); err != nil { + errs.Add(namespace, err) + return warnings, errs + } + } + for _, action := range ctx.getApplicableActions(groupResource, namespace) { if !action.Selector.Matches(labels.Set(obj.GetLabels())) { continue } ctx.log.Infof("Executing item action for %v", &groupResource) - executeOutput, err := action.RestoreItemAction.Execute(&velero.RestoreItemActionExecuteInput{ Item: obj, ItemFromBackup: itemFromBackup, @@ -1270,12 +1281,18 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } // Remove insubstantial metadata. - fromCluster, err = resetMetadataAndStatus(fromCluster) + fromCluster, err = resetMetadata(fromCluster) if err != nil { ctx.log.Infof("Error trying to reset metadata for %s: %v", kube.NamespaceAndName(obj), err) warnings.Add(namespace, err) return warnings, errs } + fromCluster, err = resetStatus(fromCluster) + if err != nil { + ctx.log.Infof("Error trying to reset status for %s: %v", kube.NamespaceAndName(obj), err) + warnings.Add(namespace, err) + return warnings, errs + } // We know the object from the cluster won't have the backup/restore name // labels, so copy them from the object we attempted to restore. @@ -1373,7 +1390,8 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } // if it should restore status, run a UpdateStatus - if ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) { + if shouldRestoreStatus { + obj.SetResourceVersion(createdObj.GetResourceVersion()) updated, err := resourceClient.UpdateStatus(obj, metav1.UpdateOptions{}) if err != nil { warnings.Add(namespace, err) @@ -1669,7 +1687,7 @@ func resetVolumeBindingInfo(obj *unstructured.Unstructured) *unstructured.Unstru return obj } -func resetMetadataAndStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { +func resetMetadata(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { res, ok := obj.Object["metadata"] if !ok { return nil, errors.New("metadata not found") @@ -1687,9 +1705,11 @@ func resetMetadataAndStatus(obj *unstructured.Unstructured) (*unstructured.Unstr } } - // Never restore status - delete(obj.UnstructuredContent(), "status") + return obj, nil +} +func resetStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + delete(obj.UnstructuredContent(), "status") return obj, nil } From 04aa7a849fb754ad9e8742a879a233ad31caf78d Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Mon, 28 Mar 2022 14:52:54 -0300 Subject: [PATCH 145/366] Fixes tests hopefully Signed-off-by: Rafael Leal --- pkg/apis/velero/v1/zz_generated.deepcopy.go | 31 ++++++++++++++++ pkg/restore/restore.go | 1 + pkg/restore/restore_test.go | 39 +++++++++++++++++++-- pkg/test/fake_dynamic.go | 5 +++ 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index aee977c6d2..8b31757004 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1278,6 +1278,11 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = new(bool) **out = **in } + if in.RestoreStatus != nil { + in, out := &in.RestoreStatus, &out.RestoreStatus + *out = new(RestoreStatusSpec) + (*in).DeepCopyInto(*out) + } if in.PreserveNodePorts != nil { in, out := &in.PreserveNodePorts, &out.PreserveNodePorts *out = new(bool) @@ -1334,6 +1339,32 @@ func (in *RestoreStatus) DeepCopy() *RestoreStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RestoreStatusSpec) DeepCopyInto(out *RestoreStatusSpec) { + *out = *in + if in.IncludedResources != nil { + in, out := &in.IncludedResources, &out.IncludedResources + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExcludedResources != nil { + in, out := &in.ExcludedResources, &out.ExcludedResources + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreStatusSpec. +func (in *RestoreStatusSpec) DeepCopy() *RestoreStatusSpec { + if in == nil { + return nil + } + out := new(RestoreStatusSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Schedule) DeepCopyInto(out *Schedule) { *out = *in diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index b13e97f4cf..835b393bb8 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1009,6 +1009,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } if groupResource == kuberesource.PersistentVolumes { + resetStatus(obj) switch { case hasSnapshot(name, ctx.volumeSnapshots): oldName := obj.GetName() diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 973c91cb8c..87528a6277 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -1867,6 +1867,7 @@ func assertRestoredItems(t *testing.T, h *harness, want []*test.APIResource) { // empty in the structured objects. Remove them to make comparison easier. unstructured.RemoveNestedField(want.Object, "metadata", "creationTimestamp") unstructured.RemoveNestedField(want.Object, "status") + unstructured.RemoveNestedField(res.Object, "status") assert.Equal(t, want, res) } @@ -2805,7 +2806,7 @@ func TestRestoreWithRestic(t *testing.T) { } } -func TestResetMetadataAndStatus(t *testing.T) { +func TestResetMetadata(t *testing.T) { tests := []struct { name string obj *unstructured.Unstructured @@ -2824,7 +2825,39 @@ func TestResetMetadataAndStatus(t *testing.T) { expectedRes: NewTestUnstructured().WithMetadata("name", "namespace", "labels", "annotations").Unstructured, }, { - name: "don't keep status", + name: "keep status", + obj: NewTestUnstructured().WithMetadata().WithStatus().Unstructured, + expectedErr: false, + expectedRes: NewTestUnstructured().WithMetadata().WithStatus().Unstructured, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res, err := resetMetadata(test.obj) + + if assert.Equal(t, test.expectedErr, err != nil) { + assert.Equal(t, test.expectedRes, res) + } + }) + } +} + +func TestResetStatus(t *testing.T) { + tests := []struct { + name string + obj *unstructured.Unstructured + expectedErr bool + expectedRes *unstructured.Unstructured + }{ + { + name: "no metadata don't cause error", + obj: &unstructured.Unstructured{}, + expectedErr: false, + expectedRes: &unstructured.Unstructured{}, + }, + { + name: "remove status", obj: NewTestUnstructured().WithMetadata().WithStatus().Unstructured, expectedErr: false, expectedRes: NewTestUnstructured().WithMetadata().Unstructured, @@ -2833,7 +2866,7 @@ func TestResetMetadataAndStatus(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - res, err := resetMetadataAndStatus(test.obj) + res, err := resetStatus(test.obj) if assert.Equal(t, test.expectedErr, err != nil) { assert.Equal(t, test.expectedRes, res) diff --git a/pkg/test/fake_dynamic.go b/pkg/test/fake_dynamic.go index 2a90323d4c..df2b2f6e22 100644 --- a/pkg/test/fake_dynamic.go +++ b/pkg/test/fake_dynamic.go @@ -72,3 +72,8 @@ func (c *FakeDynamicClient) Delete(name string, opts metav1.DeleteOptions) error args := c.Called(name, opts) return args.Error(1) } + +func (c *FakeDynamicClient) UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) { + args := c.Called(obj, opts) + return args.Get(0).(*unstructured.Unstructured), args.Error(1) +} From 278bee12699304c7e1763739e66b0fd9fcfae6b4 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Mon, 28 Mar 2022 14:58:25 -0300 Subject: [PATCH 146/366] Update codegen Signed-off-by: Rafael Leal --- config/crd/v1/bases/velero.io_restores.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 68d1aefb02..4bc98b3a41 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -1773,6 +1773,26 @@ spec: PVs from snapshot (via the cloudprovider). nullable: true type: boolean + restoreStatus: + description: RestoreStatus specifies which resources we should restore + the status field. If nil, no objects are included. Optional. + nullable: true + properties: + excludedResources: + description: ExcludedResources specifies the resources to which + will not restore the status. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources specifies the resources to which + will restore the status. If empty, it applies to all resources. + items: + type: string + nullable: true + type: array + type: object scheduleName: description: ScheduleName is the unique name of the Velero schedule to restore from. If specified, and BackupName is empty, Velero will From 7f229747190be16e6b598a9174336b54a0960013 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 11:59:49 -0300 Subject: [PATCH 147/366] Update CRDs Signed-off-by: Rafael Leal --- pkg/apis/velero/v1/zz_generated.deepcopy.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 8b31757004..c0a8f2c567 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1352,7 +1352,6 @@ func (in *RestoreStatusSpec) DeepCopyInto(out *RestoreStatusSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreStatusSpec. From 8ecc11fad269a682a8f2077949cb71beb1b8f3d3 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 12:00:34 -0300 Subject: [PATCH 148/366] Add restore status to cli Signed-off-by: Rafael Leal --- pkg/cmd/cli/restore/create.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index caf94dc51f..ac03246d4f 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -85,6 +85,8 @@ type CreateOptions struct { ExistingResourcePolicy string IncludeResources flag.StringArray ExcludeResources flag.StringArray + StatusIncludeResources flag.StringArray + StatusExcludeResources flag.StringArray NamespaceMappings flag.Map Selector flag.LabelSelector IncludeClusterResources flag.OptionalBool @@ -115,6 +117,8 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.Var(&o.IncludeResources, "include-resources", "Resources to include in the restore, formatted as resource.group, such as storageclasses.storage.k8s.io (use '*' for all resources).") flags.Var(&o.ExcludeResources, "exclude-resources", "Resources to exclude from the restore, formatted as resource.group, such as storageclasses.storage.k8s.io.") flags.StringVar(&o.ExistingResourcePolicy, "existing-resource-policy", "", "Restore Policy to be used during the restore workflow, can be - none or update") + flags.Var(&o.StatusIncludeResources, "status-include-resources", "Resources to include in the restore status, formatted as resource.group, such as storageclasses.storage.k8s.io.") + flags.Var(&o.StatusExcludeResources, "status-exclude-resources", "Resources to exclude from the restore status, formatted as resource.group, such as storageclasses.storage.k8s.io.") flags.VarP(&o.Selector, "selector", "l", "Only restore resources matching this label selector.") f := flags.VarPF(&o.RestoreVolumes, "restore-volumes", "", "Whether to restore volumes from snapshots.") // this allows the user to just specify "--restore-volumes" as shorthand for "--restore-volumes=true" @@ -279,6 +283,13 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { }, } + if len([]string(o.StatusIncludeResources)) > 0 { + restore.Spec.RestoreStatus = &api.RestoreStatusSpec{ + IncludedResources: o.StatusIncludeResources, + ExcludedResources: o.StatusExcludeResources, + } + } + if printed, err := output.PrintWithFormat(c, restore); printed || err != nil { return err } From 206709b978c2e9ba298fbd27dd47505c598d3eed Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 11:35:11 -0300 Subject: [PATCH 149/366] Cleanup Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 835b393bb8..0c3f422a8a 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1256,7 +1256,6 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso addRestoreLabels(obj, ctx.restore.Name, ctx.restore.Spec.BackupName) // Clear out status. - objWithoutStatus := obj.DeepCopy() if objWithoutStatus, err = resetStatus(objWithoutStatus); err != nil { errs.Add(namespace, err) From d85ed612cb3df7dfea101db0638223275f8e4ba4 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 12 May 2022 11:55:50 -0300 Subject: [PATCH 150/366] Cleanup Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 0c3f422a8a..8fdc014da3 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1137,14 +1137,6 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso shouldRestoreStatus := ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) ctx.log.Infof("restore status includes excludes: %+v", ctx.resourceStatusIncludesExcludes) - // Clear out status. - if !shouldRestoreStatus { - ctx.log.Infof("Resetting status for obj %s/%s", obj.GetKind(), obj.GetName()) - if obj, err = resetStatus(obj); err != nil { - errs.Add(namespace, err) - return warnings, errs - } - } for _, action := range ctx.getApplicableActions(groupResource, namespace) { if !action.Selector.Matches(labels.Set(obj.GetLabels())) { From 18621a0e132d96d3795735f34218fe8c7562ed75 Mon Sep 17 00:00:00 2001 From: danfengl Date: Mon, 9 May 2022 06:51:18 +0000 Subject: [PATCH 151/366] Add setting TTL in backup E2E test Signed-off-by: danfengl --- test/e2e/backups/deletion.go | 13 +- test/e2e/backups/sync_backups.go | 27 ++- test/e2e/backups/ttl.go | 200 ++++++++++++++++++++ test/e2e/basic/enable_api_group_versions.go | 8 +- test/e2e/bsl-mgmt/deletion.go | 25 ++- test/e2e/e2e_suite_test.go | 7 +- test/e2e/types.go | 12 ++ test/e2e/upgrade/upgrade.go | 25 +-- test/e2e/util/csi/common.go | 24 ++- test/e2e/util/kibishii/kibishii_utils.go | 23 +-- test/e2e/util/providers/common.go | 10 +- test/e2e/util/velero/install.go | 6 + test/e2e/util/velero/velero_utils.go | 51 +++-- 13 files changed, 345 insertions(+), 86 deletions(-) create mode 100644 test/e2e/backups/ttl.go diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 0c5c2deed4..a531865f19 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -118,7 +118,13 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa if err != nil { return err } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots, ""); err != nil { + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName + BackupCfg.Namespace = deletionTest + BackupCfg.BackupLocation = backupLocation + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.Selector = "" + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) @@ -141,9 +147,6 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa if useVolumeSnapshots { snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, deletionTest, backupName, KibishiiPodNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - if err != nil { - return errors.Wrap(err, "exceed waiting for snapshot created in cloud") - } err = WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName, snapshotCheckPoint) @@ -185,7 +188,7 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa } backupName = "backup-1-" + UUIDgen.String() - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, deletionTest, backupLocation, useVolumeSnapshots, ""); err != nil { + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index 2fb2df6d7b..cc3fefb23f 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -89,8 +89,14 @@ func BackupsSyncTest() { Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) }() + var BackupCfg BackupConfig + BackupCfg.BackupName = test.backupName + BackupCfg.Namespace = test.testNS + BackupCfg.BackupLocation = "" + BackupCfg.UseVolumeSnapshots = false + BackupCfg.Selector = "" By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false, ""); err != nil { + if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") } Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) @@ -116,14 +122,21 @@ func BackupsSyncTest() { Expect(CreateNamespace(test.ctx, client, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) }) + if !VeleroCfg.Debug { + defer func() { + Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), + fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) + }() + } - defer func() { - Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), - fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) - }() - + var BackupCfg BackupConfig + BackupCfg.BackupName = test.backupName + BackupCfg.Namespace = test.testNS + BackupCfg.BackupLocation = "" + BackupCfg.UseVolumeSnapshots = false + BackupCfg.Selector = "" By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, test.testNS, "", false, ""); err != nil { + if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") } Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go new file mode 100644 index 0000000000..6b024808e7 --- /dev/null +++ b/test/e2e/backups/ttl.go @@ -0,0 +1,200 @@ +/* + * + * Copyright the Velero contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package backups + +import ( + "context" + "flag" + "fmt" + "math/rand" + "strings" + "time" + + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + + . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" + . "github.com/vmware-tanzu/velero/test/e2e/util/providers" + . "github.com/vmware-tanzu/velero/test/e2e/util/velero" +) + +type TTL struct { + testNS string + backupName string + restoreName string + ctx context.Context + ttl time.Duration +} + +func (b *TTL) Init() { + rand.Seed(time.Now().UnixNano()) + UUIDgen, _ = uuid.NewRandom() + b.testNS = "backup-ttl-test-" + UUIDgen.String() + b.backupName = "backup-ttl-test-" + UUIDgen.String() + b.restoreName = "restore-ttl-test-" + UUIDgen.String() + b.ctx, _ = context.WithTimeout(context.Background(), time.Duration(time.Minute*30)) + b.ttl = time.Duration(10 * time.Minute) + +} + +func TTLTest() { + useVolumeSnapshots := true + test := new(TTL) + client, err := NewTestClient() + if err != nil { + println(err.Error()) + } + Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + + BeforeEach(func() { + flag.Parse() + if VeleroCfg.InstallVelero { + // Make sure GCFrequency is shorter than backup TTL + VeleroCfg.GCFrequency = "4m0s" + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) + } + }) + + AfterEach(func() { + if VeleroCfg.InstallVelero { + VeleroCfg.GCFrequency = "" + if !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) + } + } + }) + + It("Backups in object storage should be synced to a new Velero successfully", func() { + test.Init() + oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS), func() { + Expect(CreateNamespace(oneHourTimeout, client, test.testNS)).To(Succeed(), + fmt.Sprintf("Failed to create %s namespace", test.testNS)) + }) + + By("Deploy sample workload of Kibishii", func() { + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, + test.testNS, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, + VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + }) + + var BackupCfg BackupConfig + BackupCfg.BackupName = test.backupName + BackupCfg.Namespace = test.testNS + BackupCfg.BackupLocation = "" + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.Selector = "" + BackupCfg.TTL = test.ttl + + By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { + Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") + return "Fail to backup workload" + }) + }) + + var snapshotCheckPoint SnapshotCheckPoint + if useVolumeSnapshots { + if VeleroCfg.CloudProvider == "vsphere" { + // TODO - remove after upload progress monitoring is implemented + By("Waiting for vSphere uploads to complete", func() { + Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, + test.testNS)).To(Succeed()) + }) + } + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, test.testNS, test.backupName, KibishiiPodNameList) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + + Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, + test.backupName, snapshotCheckPoint)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + } + + By(fmt.Sprintf("Simulating a disaster by removing namespace %s\n", BackupCfg.BackupName), func() { + Expect(DeleteNamespace(test.ctx, client, BackupCfg.BackupName, true)).To(Succeed(), + fmt.Sprintf("Failed to delete namespace %s", BackupCfg.BackupName)) + }) + + if VeleroCfg.CloudProvider == "aws" && useVolumeSnapshots { + fmt.Println("Waiting 7 minutes to make sure the snapshots are ready...") + time.Sleep(7 * time.Minute) + } + + By(fmt.Sprintf("Restore %s", test.testNS), func() { + Expect(VeleroRestore(test.ctx, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, test.restoreName, test.backupName)).To(Succeed(), func() string { + RunDebug(test.ctx, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, "", test.restoreName) + return "Fail to restore workload" + }) + }) + + By("Associated Restores should be created", func() { + Expect(ObjectsShouldBeInBucket(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, test.restoreName, + RestoreObjectsPrefix)).NotTo(HaveOccurred(), "Fail to get restore object") + + }) + + By("Check TTL was set correctly", func() { + ttl, err := GetBackupTTL(test.ctx, VeleroCfg.VeleroNamespace, test.backupName) + Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + t, _ := time.ParseDuration(strings.ReplaceAll(ttl, "'", "")) + fmt.Println(t.Round(time.Minute).String()) + Expect(t).To(Equal(test.ttl)) + }) + + By(fmt.Sprintf("Waiting %s minutes for removing backup ralated resources by GC", test.ttl.String()), func() { + time.Sleep(test.ttl) + }) + + By("Check if backups are deleted by GC", func() { + Expect(WaitBackupDeleted(test.ctx, VeleroCfg.VeleroCLI, test.backupName, time.Minute*10)).To(Succeed(), fmt.Sprintf("Backup %s was not deleted by GC", test.backupName)) + }) + + By("Backup file from cloud object storage should be deleted", func() { + Expect(ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, test.backupName, + BackupObjectsPrefix, 5)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + }) + + By("PersistentVolume snapshots should be deleted", func() { + if useVolumeSnapshots { + Expect(WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, + test.backupName, snapshotCheckPoint)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") + } + }) + + By("Associated Restores should be deleted", func() { + Expect(ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, test.restoreName, + RestoreObjectsPrefix, 5)).NotTo(HaveOccurred(), "Fail to get restore object") + + }) + }) +} diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index c307163873..1266a79d4d 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -242,7 +242,13 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso backup := "backup-rockbands-" + UUIDgen.String() + "-" + strconv.Itoa(i) namespacesStr := strings.Join(tc.namespaces, ",") - err = VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, namespacesStr, "", false, "") + var BackupCfg BackupConfig + BackupCfg.BackupName = backup + BackupCfg.Namespace = namespacesStr + BackupCfg.BackupLocation = "" + BackupCfg.UseVolumeSnapshots = false + BackupCfg.Selector = "" + err = VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg) if err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, "") return errors.Wrapf(err, "back up %s namespaces on source cluster", namespacesStr) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index f385d73712..91307f19e0 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -19,7 +19,6 @@ import ( "context" "flag" "fmt" - "strings" "time" "github.com/google/go-cmp/cmp" @@ -29,7 +28,6 @@ import ( . "github.com/onsi/gomega" . "github.com/vmware-tanzu/velero/test/e2e" - util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" @@ -179,18 +177,25 @@ func BslDeletionTest(useVolumeSnapshots bool) { Expect(AddLabelToPvc(context.Background(), pvc2, bslDeletionTestNs, label_2)).To(Succeed()) }) + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName_1 + BackupCfg.Namespace = bslDeletionTestNs + BackupCfg.BackupLocation = backupLocation_1 + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.Selector = label_1 By(fmt.Sprintf("Backup one of PV of sample workload by label-1 - Kibishii by the first BSL %s", backupLocation_1), func() { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, backupName_1, bslDeletionTestNs, - backupLocation_1, useVolumeSnapshots, label_1)).To(Succeed()) + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed()) }) + BackupCfg.BackupName = backupName_2 + BackupCfg.BackupLocation = backupLocation_2 + BackupCfg.Selector = label_2 By(fmt.Sprintf("Back up the other one PV of sample workload with label-2 into the additional BSL %s", backupLocation_2), func() { Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, backupName_2, bslDeletionTestNs, - backupLocation_2, useVolumeSnapshots, label_2)).To(Succeed()) + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed()) }) if useVolumeSnapshots { @@ -208,14 +213,6 @@ func BslDeletionTest(useVolumeSnapshots bool) { Expect(SnapshotCRsCountShouldBe(context.Background(), bslDeletionTestNs, backupName_2, 1)).To(Succeed()) }) - } else if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { - By(fmt.Sprintf("CSI VolumeSnapshotContent CR in backup %s should be created", backupName_1), func() { - Expect(util.CheckVolumeSnapshotCR(client, []string{podName_1}, bslDeletionTestNs, backupName_1)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content CR.") - }) - - By(fmt.Sprintf("CSI VolumeSnapshotContent CR in backup %s should be created", backupName_2), func() { - Expect(util.CheckVolumeSnapshotCR(client, []string{podName_2}, bslDeletionTestNs, backupName_2)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content CR.") - }) } var snapshotCheckPoint SnapshotCheckPoint diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a2d98727da..35ec776385 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -65,6 +65,7 @@ func init() { flag.StringVar(&VeleroCfg.AdditionalBSLCredentials, "additional-bsl-credentials-file", "", "file containing credentials for additional backup storage location provider. Required if testing multiple credentials support.") flag.StringVar(&VeleroCfg.Features, "features", "", "Comma-separated list of features to enable for this Velero process.") flag.BoolVar(&VeleroCfg.Debug, "debug-e2e-test", false, "Switch to control namespace cleaning.") + flag.StringVar(&VeleroCfg.GCFrequency, "garbage-collection-frequency", "", "Frequency of garbage collection.") } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) @@ -93,12 +94,14 @@ var _ = Describe("[ResourceFiltering][IncludeNamespaces][Restore] Velero test on var _ = Describe("[ResourceFiltering][IncludeResources][Backup] Velero test on include resources from the cluster backup", BackupWithIncludeResources) var _ = Describe("[ResourceFiltering][IncludeResources][Restore] Velero test on include resources from the cluster restore", RestoreWithIncludeResources) var _ = Describe("[ResourceFiltering][LabelSelector] Velero test on backup include resources matching the label selector", BackupWithLabelSelector) -var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup deletion", BackupDeletionWithRestic) +var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup deletion", BackupDeletionWithRestic) var _ = Describe("[Backups][Deletion][Snapshot] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) -var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) +var _ = Describe("[Backups][TTL] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", TTLTest) var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) +var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) + var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) diff --git a/test/e2e/types.go b/test/e2e/types.go index d24c40635a..9a92ea495d 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -17,6 +17,8 @@ limitations under the License. package e2e import ( + "time" + "github.com/google/uuid" ) @@ -51,6 +53,7 @@ type VerleroConfig struct { KibishiiDirectory string Features string Debug bool + GCFrequency string } type SnapshotCheckPoint struct { @@ -62,3 +65,12 @@ type SnapshotCheckPoint struct { PodName []string EnableCSI bool } + +type BackupConfig struct { + BackupName string + Namespace string + BackupLocation string + UseVolumeSnapshots bool + Selector string + TTL time.Duration +} diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index aff943aa6c..794feba25a 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -27,7 +27,6 @@ import ( . "github.com/onsi/gomega" . "github.com/vmware-tanzu/velero/test/e2e" - util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" . "github.com/vmware-tanzu/velero/test/e2e/util/providers" @@ -79,6 +78,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade client TestClient err error ) + By("Create test client instance", func() { client, err = NewTestClient() Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") @@ -125,7 +125,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade Expect(err).To(Succeed()) }) } - + VeleroCfg.GCFrequency = "" By(fmt.Sprintf("Install the expected old version Velero (%s) for upgrade", upgradeFromVelero.UpgradeFromVeleroVersion), func() { //Set VeleroImage and ResticHelperImage to blank @@ -162,9 +162,14 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade }) By(fmt.Sprintf("Backup namespace %s", upgradeNamespace), func() { + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName + BackupCfg.Namespace = upgradeNamespace + BackupCfg.BackupLocation = "" + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.Selector = "" Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg.UpgradeFromVeleroCLI, - tmpCfg.VeleroNamespace, backupName, upgradeNamespace, "", - useVolumeSnapshots, "")).ShouldNot(HaveOccurred(), func() string { + tmpCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { err = VeleroBackupLogs(context.Background(), tmpCfg.UpgradeFromVeleroCLI, tmpCfg.VeleroNamespace, backupName) return "Get backup logs" @@ -178,10 +183,6 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, upgradeNamespace)).To(Succeed()) }) - } else if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { - By("CSI VolumeSnapshotContent CR should be created", func() { - Expect(util.CheckVolumeSnapshotCR(client, KibishiiPodNameList, upgradeNamespace, backupName)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot content") - }) } var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.NamespaceBackedUp = upgradeNamespace @@ -206,13 +207,6 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade By("Sleep 5 minutes to avoid snapshot recreated by unknown reason ", func() { time.Sleep(5 * time.Minute) }) - // TODO: add WaitUntilSnapshotsNotExistInCloud verification when Upgrade test is enabled in nightly - // err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, - // VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, - // backupName, snapshotCheckPoint) - // if err != nil { - // return errors.Wrap(err, "exceed waiting for snapshot created in cloud") - // } } // the snapshots of AWS may be still in pending status when do the restore, wait for a while // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 @@ -223,6 +217,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade } By(fmt.Sprintf("Upgrade Velero by CLI %s", tmpCfg.VeleroCLI), func() { + tmpCfg.GCFrequency = "" Expect(VeleroInstall(context.Background(), &tmpCfg, useVolumeSnapshots)).To(Succeed()) Expect(CheckVeleroVersion(context.Background(), tmpCfg.VeleroCLI, tmpCfg.VeleroVersion)).To(Succeed()) diff --git a/test/e2e/util/csi/common.go b/test/e2e/util/csi/common.go index b517b5f541..e2e9265b26 100644 --- a/test/e2e/util/csi/common.go +++ b/test/e2e/util/csi/common.go @@ -65,11 +65,20 @@ func GetCsiSnapshotHandle(client TestClient, backupName string) ([]string, error } var snapshotHandleList []string for _, i := range vscList.Items { + if i.Status == nil { + fmt.Println("SnapshotHandle Status s nil") + continue + } if i.Status.SnapshotHandle == nil { fmt.Println("SnapshotHandle is nil") continue } - fmt.Println(*i.Status.SnapshotHandle) + + if i.Labels == nil { + fmt.Println("VolumeSnapshotContents label is nil") + continue + } + if i.Labels["velero.io/backup-name"] == backupName { tmp := strings.Split(*i.Status.SnapshotHandle, "/") snapshotHandleList = append(snapshotHandleList, tmp[len(tmp)-1]) @@ -120,13 +129,12 @@ func GetVolumeSnapshotContentNameByPod(client TestClient, podName, namespace, ba return "", errors.New(fmt.Sprintf("Fail to get VolumeSnapshotContentName for pod %s under namespace %s", podName, namespace)) } -func CheckVolumeSnapshotCR(client TestClient, pods []string, nampespace, backupName string) error { - for _, podName := range pods { - if snapshotContentName, err := GetVolumeSnapshotContentNameByPod(client, podName, nampespace, backupName); err != nil || snapshotContentName == "" { - return errors.Wrap(err, "Fail to get Azure CSI snapshot content") - } else { - fmt.Println("Found volumesnapshotcontent: " + snapshotContentName) - } +func CheckVolumeSnapshotCR(client TestClient, backupName string, expectedCount int) error { + var err error + var snapshotContentNameList []string + if snapshotContentNameList, err = GetCsiSnapshotHandle(client, backupName); err != nil || len(snapshotContentNameList) != expectedCount { + return errors.Wrap(err, "Fail to get Azure CSI snapshot content") } + fmt.Println(snapshotContentNameList) return nil } diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 005354f28d..45d4d8f8ee 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -28,7 +28,6 @@ import ( veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" . "github.com/vmware-tanzu/velero/test/e2e" - util "github.com/vmware-tanzu/velero/test/e2e/util/csi" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/providers" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" @@ -72,8 +71,13 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, - kibishiiNamespace, backupLocation, useVolumeSnapshots, ""); err != nil { + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName + BackupCfg.Namespace = kibishiiNamespace + BackupCfg.BackupLocation = backupLocation + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.Selector = "" + if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, backupName, "") return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace) } @@ -87,10 +91,6 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re if err := WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Error waiting for uploads to complete") } - } else if providerName == "azure" && strings.EqualFold(veleroFeatures, "EnableCSI") { - if err := util.CheckVolumeSnapshotCR(client, KibishiiPodNameList, kibishiiNamespace, backupName); err != nil { - return errors.Wrapf(err, "Fail to get Azure CSI snapshot content") - } } snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, kibishiiNamespace, backupName, KibishiiPodNameList) if err != nil { @@ -108,14 +108,6 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re if err := DeleteNamespace(oneHourTimeout, client, kibishiiNamespace, true); err != nil { return errors.Wrapf(err, "failed to delete namespace %s", kibishiiNamespace) } - if useVolumeSnapshots && providerName == "azure" && strings.EqualFold(veleroFeatures, "EnableCSI") { - err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, - backupName, snapshotCheckPoint) - if err != nil { - return errors.Wrap(err, "exceed waiting for snapshot created in cloud") - } - } time.Sleep(5 * time.Minute) // the snapshots of AWS may be still in pending status when do the restore, wait for a while @@ -215,6 +207,7 @@ func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClie if err := WaitUntilServiceAccountCreated(oneHourTimeout, client, kibishiiNamespace, serviceAccountName, 10*time.Minute); err != nil { return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, kibishiiNamespace) } + // add the image pull secret to avoid the image pull limit issue of Docker Hub if err := PatchServiceAccountWithImagePullSecret(oneHourTimeout, client, kibishiiNamespace, serviceAccountName, registryCredentialFile); err != nil { return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, kibishiiNamespace) diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index accfafec18..ee5dd47da0 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -37,10 +37,10 @@ type ObjectsInStorage interface { } func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { - fmt.Printf("|| VERIFICATION || - Backup %s should exist in storage [%s]", backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - %s %s should exist in storage [%s]\n", subPrefix, backupName, bslPrefix) exist, _ := IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if !exist { - return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected", backupName)) + return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected\n", backupName)) } fmt.Printf("|| EXPECTED || - Backup %s exist in object storage bucket %s\n", backupName, bslBucket) return nil @@ -48,11 +48,11 @@ func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bsl func ObjectsShouldNotBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, retryTimes int) error { var err error var exist bool - fmt.Printf("|| VERIFICATION || - Backup %s should not exist in storage %s", backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - %s %s should not exist in storage %s\n", subPrefix, backupName, bslPrefix) for i := 0; i < retryTimes; i++ { exist, err = IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if err != nil { - return errors.Wrapf(err, "|| UNEXPECTED || - Failed to get backup %s in object store", backupName) + return errors.Wrapf(err, "|| UNEXPECTED || - Failed to get backup %s in object store\n", backupName) } if !exist { fmt.Printf("|| EXPECTED || - Backup %s is not in object store\n", backupName) @@ -60,7 +60,7 @@ func ObjectsShouldNotBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, } time.Sleep(1 * time.Minute) } - return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s still exist in object store after backup deletion", backupName)) + return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s still exist in object store after backup deletion\n", backupName)) } func getProvider(cloudProvider string) (ObjectsInStorage, error) { var s ObjectsInStorage diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 30b1b1c427..ed3f6156dd 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -90,6 +90,8 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps veleroInstallOptions.UseRestic = !useVolumeSnapshots veleroInstallOptions.Image = veleroCfg.VeleroImage veleroInstallOptions.Namespace = veleroCfg.VeleroNamespace + GCFrequency, _ := time.ParseDuration(veleroCfg.GCFrequency) + veleroInstallOptions.GarbageCollectionFrequency = GCFrequency err = installVeleroServer(ctx, veleroCfg.VeleroCLI, &installOptions{ InstallOptions: veleroInstallOptions, @@ -131,6 +133,7 @@ func configvSpherePlugin() error { if err != nil { return errors.WithMessagef(err, "Failed to create velero-vsphere-plugin-config configmap in %s namespace", VeleroCfg.VeleroNamespace) } + fmt.Println("configvSpherePlugin: WaitForConfigMapComplete") err = WaitForConfigMapComplete(cli.ClientGo, VeleroCfg.VeleroNamespace, configmaptName) if err != nil { return errors.Wrap(err, fmt.Sprintf("Failed to ensure configmap %s completion in namespace: %s", configmaptName, VeleroCfg.VeleroNamespace)) @@ -209,6 +212,9 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption } } + if options.GarbageCollectionFrequency > 0 { + args = append(args, fmt.Sprintf("--garbage-collection-frequency=%v", options.GarbageCollectionFrequency)) + } if err := createVelereResources(ctx, cli, namespace, args, options.RegistryCredentialFile, options.ResticHelperImage); err != nil { return err diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 449f6e0a50..9c1cb87090 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -47,6 +47,7 @@ import ( ) const BackupObjectsPrefix = "backups" +const RestoreObjectsPrefix = "restores" const PluginsObjectsPrefix = "plugins" var pluginsMatrix = map[string]map[string][]string{ @@ -246,19 +247,18 @@ func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st } // VeleroBackupNamespace uses the veleroCLI to backup a namespace. -func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace, backupName, namespace, backupLocation string, - useVolumeSnapshots bool, selector string) error { +func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace string, backupCfg BackupConfig) error { args := []string{ "--namespace", veleroNamespace, - "create", "backup", backupName, - "--include-namespaces", namespace, + "create", "backup", backupCfg.BackupName, + "--include-namespaces", backupCfg.Namespace, "--wait", } - if selector != "" { - args = append(args, "--selector", selector) + if backupCfg.Selector != "" { + args = append(args, "--selector", backupCfg.Selector) } - if useVolumeSnapshots { + if backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes") } else { args = append(args, "--default-volumes-to-restic") @@ -268,11 +268,13 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace, back // TODO This can be removed if the logic of vSphere plugin bump up to 1.3 args = append(args, "--snapshot-volumes=false") } - if backupLocation != "" { - args = append(args, "--storage-location", backupLocation) + if backupCfg.BackupLocation != "" { + args = append(args, "--storage-location", backupCfg.BackupLocation) } - - return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupName, args) + if backupCfg.TTL != 0 { + args = append(args, "--ttl", backupCfg.TTL.String()) + } + return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupCfg.BackupName, args) } // VeleroBackupExcludeNamespaces uses the veleroCLI to backup a namespace. @@ -502,7 +504,7 @@ func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace if err != nil { fmt.Print(stdout) fmt.Print(stderr) - return nil, errors.Wrap(err, "failed to verify") + return nil, errors.Wrap(err, "Failed to get vSphere snapshot ID list from snapshots.backupdriver.cnsdp.vmware.com") } stdout = strings.Replace(stdout, "'", "", -1) lines := strings.Split(stdout, "\n") @@ -814,9 +816,9 @@ func GetSnapshotCheckPoint(client TestClient, VeleroCfg VerleroConfig, expectCou snapshotCheckPoint.ExpectCount = expectCount snapshotCheckPoint.NamespaceBackedUp = namespaceBackedUp snapshotCheckPoint.PodName = kibishiiPodNameList - if strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { snapshotCheckPoint.EnableCSI = true - if err := util.CheckVolumeSnapshotCR(client, kibishiiPodNameList, namespaceBackedUp, backupName); err != nil { + if err := util.CheckVolumeSnapshotCR(client, backupName, expectCount); err != nil { return snapshotCheckPoint, errors.Wrapf(err, "Fail to get Azure CSI snapshot content") } var err error @@ -832,3 +834,24 @@ func GetSnapshotCheckPoint(client TestClient, VeleroCfg VerleroConfig, expectCou fmt.Println(snapshotCheckPoint) return snapshotCheckPoint, nil } + +func GetBackupTTL(ctx context.Context, veleroNamespace, backupName string) (string, error) { + + checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", + "get", "backup", "-n", veleroNamespace, backupName, "-o=jsonpath='{.spec.ttl}'") + fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) + stdout, stderr, err := veleroexec.RunCommand(checkSnapshotCmd) + if err != nil { + fmt.Print(stdout) + fmt.Print(stderr) + return "", errors.Wrap(err, "failed to verify") + } + // lines := strings.Split(stdout, "\n") + // complete := true + // for _, curLine := range lines { + // fmt.Println(curLine) + + // } + // return complete, nil + return stdout, err +} From 8f31bff1b53be3056e16da23ecf99cc73f2b68ee Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Fri, 13 May 2022 12:37:58 -0400 Subject: [PATCH 152/366] Add note about restoring when resource scaled to 0 Signed-off-by: Abigail McCarthy --- site/content/docs/main/restic.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/restic.md b/site/content/docs/main/restic.md index ac75f0f9d0..8fa1dd8b33 100644 --- a/site/content/docs/main/restic.md +++ b/site/content/docs/main/restic.md @@ -248,7 +248,7 @@ Instructions to back up using this approach are as follows: ### Using opt-in pod volume backup -Velero, by default, uses this approach to discover pod volumes that need to be backed up using Restic, where every pod containing a volume to be backed up using Restic must be annotated with the volume's name. +Velero, by default, uses this approach to discover pod volumes that need to be backed up using Restic. Every pod containing a volume to be backed up using Restic must be annotated with the volume's name using the `backup.velero.io/backup-volumes` annotation. Instructions to back up using this approach are as follows: @@ -412,7 +412,7 @@ data: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 - runAsGroup: 999 + runAsGroup: 999 ``` @@ -524,6 +524,8 @@ within each restored volume, under `.velero`, whose name is the UID of the Veler 1. Once all such files are found, the init container's process terminates successfully and the pod moves on to running other init containers/the main containers. +Velero won't restore a resource if a that resource is scaled to 0 and already exists in the cluster. If Velero restored the requested pods in this scenario, the Kubernetes reconciliation loops that manage resources would delete the running pods because its scaled to be 0. Velero will be able to restore once the resources is scaled up, and the pods are created and remain running. + ## 3rd party controllers ### Monitor backup annotation From 1537bf5d62872654205e47985af3fe0090d489b6 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Fri, 13 May 2022 15:36:03 -0300 Subject: [PATCH 153/366] Update CRD Signed-off-by: Rafael Leal --- config/crd/v1/crds/crds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 6ead090277..314c400b8b 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -36,7 +36,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\x03V\xa9\x92%\xa4e\x06\xddMƷ\xca\xd2\xfbG\xbd~Sr\xf6\xcf\x12\x9a50\xab\x88\xb4{\xba\x8b\fk\x9cT\xa1\xb4F\x06\x859:\u007f\xc1\xfd\xf4_rQ#\ay\xc7\xd5\xd7&H\xcb#\x84\xd2X\x97\x80\xebF\x91e\x1ffJ\\;>\xf7xgY\r\xbf\x86]\xe6o\a;\xec\x16\x0f\x13\xf7խ\f\xd8\x1d'\xd2ޝm\xc3袹[{\xc76\xa1\x85.\xa5\x8b\v$\xa5\xc4\xfe\xbfu\xcbB\xea1\xe7P\xd4\x02\xbb[\x01vy\x00L\xf0;\x96\x83\xd24/\x0ePȫ\xed7\xcc\x06\b\x99\xaa\xaa\xb2a3$X7\xd9\xed2\x90i\xdd\xda9\x9d6`[0\xa8_\x18А\x12X\x01'\xeeR?f\xdaڀc\a\xd0;T\xc0\xe5\ns\u007f<\x1c\xbc\xdf7\x17\x92`\x17\xedj\xea\xdb\x14\xe1KE\xa5Tä\xb3rH\xaf\x93\xd8)E\xf1Z\xee!V\x83w\x9d\x9d@M\xf0^\x88\xd9\xde,\xb3o\xfb\x9bƮ\xb4\xc7\x03H \v\xe0\x06ŝ\x1cǩe\xb6\xb5\xabA\xac;\xc1U\xc0\xf6\xcev\xc3-\xa9\xfb\x80U\xd4+/R\x97\a\x02)\x19\x1f鬪\xb0\xaf`\x96\xbb\xe1\xfd\x0e\xa8꺧\xd0B\xc4\xd7\xcdg\x9d\xf6mq\x80KO\xa8틽\x04\x02\\3\t\x9d\xa4\xef\xa6$\xf0\xcb\x01g\x9c\x90bI\xd5!vyc\x9e\xa9Z\xf06\x0ee\xc5)\xdf\xed\x98\x13\xf02\xdf\x06>!\xd7\xf0\xd0\xf1\xeb\xd7H\xf4hQu\x1f\xa5\t\xb9\xe27R,dWW\x88\x89?X\x1d\x142!7TjF\xb3l\xfduw\xf7I\xff\xf5 ܹ\xa9\x1c\xd4\x12\xecc\xb5\xd2ĸ=\u007f\x86R\xe9\f\xbb\xbb\xd6\xc4z\xa2j:\xee\xf6\xd8 \xb4\xa91\xaa\xc0\x1b\xe3\xac\r\x14\xaf\\(=\x81\xf9\\Hm\x95\xb4Ʉ\xb0\xb9\xe3\x9f]\xca\ae\x19\xbalm\xd2\x1ca\xba\x0e\bU\x02\xc9\xd8\xfa|M$\x12+\xb6\xfa\xcd\xe9ڦ\xdc\xd0$)\xcd\xf1geNY\xb7\xde|\xa8\x88\x1e\xd6@\xb9\xdam\xe8\xb5\x13쪇\xfd\x02l\t\x95\xade\x88\xa6K|w\xfa\x00S\xfeU\xb3g\xc9\x12+h\xeb\xa5\x14\xe5b\xe9Ip\x17\x03\xdde'\x95X\xe4\xa7\xc8ʅ!k\xe7\xd4ҥ\xe4\r\x9b̹\xb9\xd2z\xba\xfb\x80\xeeG\xe1\x1e\xb5_\xb5$\xde!\x05\xb0\xf5\xf00\xc9^\x95\xb7\xfat%\xf2\xaab\xa9\x97}d\xf3\xfb\x8d\xc77\x12\xa7\x8c\x94\xae!:yځ\x9cS6\xb7\x9e\xc1\xc4\xccz;Y\xff\x03'@=P\xc9\x19_\x1cZ\xfc\xf7\xee\xb1\x0e\xd5\xc4A\xe8PN:\x16Q\xa9+Aʉ\x9f\xe4\x8e\xdc\xfeJa\x19\xa0\x9et\x9e\xa1\xad\x1f\x91\x90\xd3\x06\x92ݗ\xdc/\xb5Zo\xcbڹ\xc4D\x8b\xdb{\xc6ӗ\xfe\x02P\x91\x95\x92f\ue7c9\xe0\x96-\xa8\x97\xe4\x87\x1f\xbf\xf0\vz\x0fRU?\xfe\xbf\x00\x00\x00\xff\xff\a{y+\x88\xf9\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ars\xf4\\d\xb50\xe1\x8f?Ϛ5*\x92\x04\vW\xa5y\xb5߶??\xe7\x1f\xe1\xe9z\xfe\x99h\xe5\xddn{\x05\xff\xfd?gP\x99\xe3\x87\xf0>=U\xfe_\x00\x00\x00\xff\xff\x11z\x10\b\t`\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V͎\xe44\x10\xbe\xe7)J\xcba/$\xbd+\x0e\xa0\xdc\xd0\xc0a\x05\x8cFӫ\xb9 \x0en\xa7\xd2mƱCU\xb9\x87\x06\xf1\xee\xc8vҝN\xd2̀\x84o\xb1\xeb端~REY\x96\x85\xea\xcd\x13\x12\x1b\xefjP\xbd\xc1\xdf\x05]\xfc\xe2\xea\xf9\x1b\xae\x8c\xdf\x1c?\x16\xcf\xc655\xdc\x05\x16\xdf=\"\xfb@\x1a\xbf\xc3\xd68#ƻ\xa2CQ\x8d\x12U\x17\x00\xca9/*^s\xfc\x04\xd0\xde\tyk\x91\xca=\xba\xea9\xecp\x17\x8cm\x90\x92\xf1\xd1\xf5\xf1C\xf5u\xf5\xa1\x00ЄI\xfd\xb3\xe9\x90Eu}\r.X[\x008\xd5a\r\x8c\x14\x95DI`\xc2\xdf\x02\xb2puD\x8b\xe4+\xe3\v\xeeQG\xc7{\xf2\xa1\xaf\xe1\xf2\x90\xf5\aP9\xa0m2\xb5M\xa6\x1e\xb3\xa9\xf4j\r\xcb\x0f\xb7$~4\x83To\x03)\xbb\x0e(\t\xf0\xc1\x93\xdc_\x9c\x96\xc0L\xf9Ÿ}\xb0\x8aV\x95\v\x00־\xc7\x1a\x92n\xaf46\x05\xc0\xc0T\xb2U\x0e\\\x1c?fs\xfa\x80\x9d\xcaN\x00|\x8f\xeeۇOO_m\xaf\xae\x01\x1adM\xa6\x97\xc4\xf7Jd`\x18\x14\f(@<(\xad\x91\x19t B'\x90Q\x82q\xad\xa7.\xe5\xe8l\x1a@\xed|\x10\x90\x03\xc2S\xa2|\x88\xac:\x8b\xf4\xe4{$1#\x1b\x83ڥ\xfa&\xb73\xac\xefc8Y\n\x9aXv\xc8\xc9\xd3@\t6\x03\x03\xe0[\x90\x83a \xec\t\x19\x9d\xccQ&~ZP\x0e\xfc\xeeW\xd4R\r9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), From d111cc5fc4a48af19d9b35ceddc2cdb31bcb5b92 Mon Sep 17 00:00:00 2001 From: JenTing Hsiao Date: Sun, 15 May 2022 15:07:13 +0800 Subject: [PATCH 154/366] Remove jenting from maintainers (#4888) Signed-off-by: JenTing Hsiao --- .github/auto-assignees.yml | 1 - MAINTAINERS.md | 4 ++-- site/content/contributors/02-jentinghsiao.md | 7 ------- site/static/img/contributors/jenting.png | Bin 20668 -> 0 bytes 4 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 site/content/contributors/02-jentinghsiao.md delete mode 100644 site/static/img/contributors/jenting.png diff --git a/.github/auto-assignees.yml b/.github/auto-assignees.yml index ddca227743..346fcc9aab 100644 --- a/.github/auto-assignees.yml +++ b/.github/auto-assignees.yml @@ -10,7 +10,6 @@ reviewers: groups: maintainers: - dsu-igeek - - jenting - sseago - reasonerjt - ywk253100 diff --git a/MAINTAINERS.md b/MAINTAINERS.md index c7638fce4d..561c0ee960 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -7,7 +7,6 @@ | Maintainer | GitHub ID | Affiliation | | --------------- | --------- | ----------- | | Dave Smith-Uchida | [dsu-igeek](https://github.com/dsu-igeek) | [Kasten](https://github.com/kastenhq/) | -| JenTing Hsiao | [jenting](https://github.com/jenting) | [SUSE](https://github.com/SUSE/) | Scott Seago | [sseago](https://github.com/sseago) | [OpenShift](https://github.com/openshift) | Daniel Jiang | [reasonerjt](https://github.com/reasonerjt) | [VMware](https://www.github.com/vmware/) | Wenkai Yin | [ywk253100](https://github.com/ywk253100) | [VMware](https://www.github.com/vmware/) | @@ -23,6 +22,7 @@ * Ashish Amarnath ([ashish-amarnath](https://github.com/ashish-amarnath)) * Carlisia Thompson ([carlisia](https://github.com/carlisia)) * Bridget McErlean ([zubron](https://github.com/zubron)) +* JenTing Hsiao ([jenting](https://github.com/jenting)) ## Velero Contributors & Stakeholders @@ -31,6 +31,6 @@ | Architect | Dave Smith-Uchida (dsu-igeek) | | Technical Lead | Daniel Jiang (reasonerjt) | | Kubernetes CSI Liaison | | -| Deployment | JenTing Hsiao (jenting) | +| Deployment | | | Community Management | Orlin Vasilev (OrlinVasilev) | | Product Management | Eleanor Millman (eleanor-millman) | diff --git a/site/content/contributors/02-jentinghsiao.md b/site/content/contributors/02-jentinghsiao.md deleted file mode 100644 index aecbbe21d2..0000000000 --- a/site/content/contributors/02-jentinghsiao.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -first_name: JenTing -last_name: Hsiao -image: /img/contributors/jenting.png -github_handle: jenting ---- -Engineer \ No newline at end of file diff --git a/site/static/img/contributors/jenting.png b/site/static/img/contributors/jenting.png deleted file mode 100644 index 2c7d190678831f014112dbdcd2d0fe445f7099b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20668 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 BdUgN+ From a7ebaa050643b44a79abff5a77878a339637aff0 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 16 May 2022 09:39:54 -0400 Subject: [PATCH 155/366] Update wording and add more useful links Signed-off-by: Abigail McCarthy --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index da18b502a1..a3a698c682 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,11 @@ The following is a list of the supported Kubernetes versions for each Velero ver | 1.5 | 1.12-1.21 || | 1.4 | 1.10-1.21 | | -The Velero maintainers are continuously working to expand testing coverage, but are not able to test every combination of Velero and supported Kubernetes for each Velero release. The table above is meant to track the current testing coverage and the expected supported Kubernetes versions and configurations. +Velero supports IPv4, IPv6, and dual stack environments. Support for this was tested against Velero v1.8. -Velero also supports IPv4, IPv6, and dual stack environments. +The Velero maintainers are continuously working to expand testing coverage, but are not able to test every combination of Velero and supported Kubernetes versions for each Velero release. The table above is meant to track the current testing coverage and the expected supported Kubernetes versions for each Velero version. If you have a question about test coverage before v1.9, please reach out in the [#velero-users](https://kubernetes.slack.com/archives/C6VCGP4MT) Slack channel. -If you are interested in using a different version of Kubernetes with a given Velero version, we'd recommend that you perform testing before installing or upgrading your environment. - -For full information around capabilities within a release, also see the Velero [release notes](https://github.com/vmware-tanzu/velero/releases) and Kubernetes [release notes](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG). +If you are interested in using a different version of Kubernetes with a given Velero version, we'd recommend that you perform testing before installing or upgrading your environment. For full information around capabilities within a release, also see the Velero [release notes](https://github.com/vmware-tanzu/velero/releases) or Kubernetes [release notes](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG). See the Velero [support page](https://velero.io/docs/latest/support-process/) for information about supported versions of Velero. [1]: https://github.com/vmware-tanzu/velero/workflows/Main%20CI/badge.svg [2]: https://github.com/vmware-tanzu/velero/actions?query=workflow%3A"Main+CI" From dcf056235ce239b1180ef71d0a27d0679f310d93 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 16 May 2022 11:08:24 -0400 Subject: [PATCH 156/366] Add resource limits testing information Signed-off-by: Abigail McCarthy --- site/content/docs/main/customize-installation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/content/docs/main/customize-installation.md b/site/content/docs/main/customize-installation.md index 77b7ee148d..721e7cefba 100644 --- a/site/content/docs/main/customize-installation.md +++ b/site/content/docs/main/customize-installation.md @@ -100,6 +100,10 @@ At installation, Velero sets default resource requests and limits for the Velero |Memory limit|512Mi|1024Mi| {{< /table >}} +Depending on the cluster resources you, especially if you are using Restic, you may need to increase these limits. Through testing, the Velero maintainers have found these defaults to work well when backing up and restoring 1000 or less resources and total size of files per backup or restore is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. + +Due to a [known Restic issue](https://github.com/restic/restic/issues/2446), the Restic pod will consume large amounts of memory, especially if you are backing up millions of tiny files and directories. If you are planning to use Restic to backup 100GB of data or more, you will need to increase the resource limits to make sure backups complete successfully. + ### Install with custom resource requests and limits You can customize these resource requests and limit when you first install using the [velero install][6] CLI command. From be040fca39c83f01c75a622cff8edcb8299f0866 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 16 May 2022 13:27:51 -0400 Subject: [PATCH 157/366] Update cluster migration scenario with more details Signed-off-by: Abigail McCarthy --- site/content/docs/main/migration-case.md | 68 +++++++++++++++++------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index f9ec0a57e9..c6de64f7b2 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -3,52 +3,85 @@ title: "Cluster migration" layout: docs --- -Velero can help you port your resources from one cluster to another, as long as you point each Velero instance to the same cloud object storage location. +Velero's backup and restore capabilities make it a valuable tool for migrating your data between clusters, helping you perform many common workflows such as upgrading Kubernetes versions or disaster recovery. Cluster migration with Velero is based on Velero's [object storage sync](how-velero-works.md#object-storage-sync) functionality, which is responsible for syncing Velero resources from your designated object storage to your cluster. This means that to perform cluster migration with Velero you must point each Velero instance running on clusters involved with the migration to the same cloud object storage location. -## Migration Limitations +This page outlines a cluster migration scenario and some common configurations you will need to start using Velero to begin migrating data. + +## Before migrating your cluster Before migrating you should consider the following, -* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [restic][2], which will backup volume contents at the filesystem level. +* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [restic](restic.md), which will backup volume contents at the filesystem level. * Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. * Migrating workloads across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered before migration, including the compatibility of API groups between clusters for each custom resource. If a Kubernetes version upgrade breaks the compatibility of core/native API groups, migrating with Velero will not be possible without first updating the impacted custom resources. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). -* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [restic][2]. +* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [restic](restic.md). ## Migration Scenario -This scenario assumes that your clusters are hosted by the same cloud provider +This scenario steps through the migration of resources from Cluster 1 to Cluster 2. In this scenario, both clusters are using the same cloud provider, AWS, and Velero's [AWS plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws). + +1. On Cluster 1, make sure Velero is installed and points to an object storage location using the `--bucket` flag. + + ``` + velero install --provider aws --image velero/velero:v1.8.0 --plugins velero/velero-plugin-for-aws:v1.4.0 --bucket velero-migration-demo --secret-file xxxx/aws-credentials-cluster1 --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 + ``` + + During installation, Velero creates a Backup Storage Location called `default` inside the `--bucket` your provided in the install command, in this case `velero-migration-demo`. This is the location that Velero will use to store backups. Running `velero backup-location get` will show the backup location of Cluster 1. -1. *(Cluster 1)* Assuming you haven't already been checkpointing your data with the Velero `schedule` operation, you need to first back up your entire cluster (replacing `` as desired): + + ``` + velero backup-location get + NAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULT + default aws velero-migration-demo Available 2022-05-13 13:41:30 +0800 CST ReadWrite true + ``` + +1. Still on Cluster 1, make sure you have a backup of your cluster. Replace `` with a name for your back up. ``` velero backup create ``` - The default backup retention period, expressed as TTL (time to live), is 30 days (720 hours); you can use the `--ttl ` flag to change this as necessary. See [how velero works][1] for more information about backup expiry. + Alternatively, you can create a [scheduled back up](https://velero.io/docs/main/backup-reference/#schedule-a-backup) of your data with the Velero `schedule` operation. This is the recommended way to make sure your data is automatically backed up according to the schedule you define. + + The default backup retention period, expressed as TTL (time to live), is 30 days (720 hours); you can use the `--ttl ` flag to change this as necessary. See [how velero works](how-velero-works.md#set-a-backup-to-expire) for more information about backup expiry. + +1. On Cluster 2, make sure that Velero is installed. Note that the install command below has the same `region` and `--bucket` location as the install command for Cluster 1. The Velero plugin for AWS does not support migrating data between regions. + + ``` + velero install --provider aws --image velero/velero:v1.8.0 --plugins velero/velero-plugin-for-aws:v1.4.0 --bucket velero-migration-demo --secret-file xxxx/aws-credentials-cluster2 --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 + ``` + + Alternatively you could configure `BackupStorageLocations` or `VolumeSnapshotLocations` after installing Velero on Cluster 2, pointing to the `--bucket` location used by Cluster 1. To do this you can use to `velero backup-location create` and `velero snapshot-location create` commands. + + ``` + velero backup-location create bsl --provider aws --bucket velero-migration-demo --config region=us-east-2 --access-mode=ReadOnly + ``` -1. *(Cluster 2)* Configure `BackupStorageLocations` and `VolumeSnapshotLocations`, pointing to the locations used by *Cluster 1*, using `velero backup-location create` and `velero snapshot-location create`. Make sure to configure the `BackupStorageLocations` as read-only - by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. + Make sure to configure the `BackupStorageLocations` as read-only + by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. See `velero backup-location –help` for more information about the available flags for this command. -1. *(Cluster 2)* Make sure that the Velero Backup object is created. Velero resources are synchronized with the backup files in cloud storage. +1. Continuing on Cluster 2, make sure that the Velero Backup object created on Cluster 1 is available. `` should be the same name used to create your backup of Cluster 1. ``` velero backup describe ``` - **Note:** The default sync interval is 1 minute, so make sure to wait before checking. You can configure this interval with the `--backup-sync-period` flag to the Velero server. + Velero resources are [synchronized](how-velero-works.md#object-storage-sync) with the backup files in object storage. This means that the Velero resources created by Cluster 1's backup will be synced to Cluster 2 through the shared Backup Storage Location. Once the sync occurs, you will be able to access the back up from Cluster 1 on Cluster 2 using Velero commands. The default sync interval is 1 minute, so you may need to wait before checking for the back up's availability on Cluster 2. You can configure this interval with the `--backup-sync-period` flag to the Velero server on Cluster 2. -1. *(Cluster 2)* Once you have confirmed that the right Backup (``) is now present, you can restore everything with: +1. On Cluster 2, once you have confirmed that the right back up is available, you can restore everything to Cluster 2. ``` velero restore create --from-backup ``` + Make sure `` is the same back up name from Cluster 1. + ## Verify Both Clusters -Check that the second cluster is behaving as expected: +Check that the Cluster 2 is behaving as expected: -1. *(Cluster 2)* Run: +1. On Cluster 2, run: ``` velero restore get @@ -60,9 +93,6 @@ Check that the second cluster is behaving as expected: velero restore describe ``` -If you encounter issues, make sure that Velero is running in the same namespace in both clusters. - - + Your data that was backed up from Cluster 1 should now be available on Cluster 2. -[1]: how-velero-works.md#set-a-backup-to-expire -[2]: restic.md +If you encounter issues, make sure that Velero is running in the same namespace in both clusters. From 3db40a58a4ff89485e5a3e936318f71004896cfc Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 16 May 2022 11:30:40 +0800 Subject: [PATCH 158/366] Make velero completion zsh command output can be used by `source` command. Signed-off-by: Xun Jiang --- changelogs/unreleased/4914-jxun | 1 + pkg/cmd/cli/completion/completion.go | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/4914-jxun diff --git a/changelogs/unreleased/4914-jxun b/changelogs/unreleased/4914-jxun new file mode 100644 index 0000000000..84db5b83f5 --- /dev/null +++ b/changelogs/unreleased/4914-jxun @@ -0,0 +1 @@ +Make velero completion zsh command output can be used by `source` command. \ No newline at end of file diff --git a/pkg/cmd/cli/completion/completion.go b/pkg/cmd/cli/completion/completion.go index 800804b98f..ee1c285093 100644 --- a/pkg/cmd/cli/completion/completion.go +++ b/pkg/cmd/cli/completion/completion.go @@ -66,7 +66,15 @@ $ velero completion fish > ~/.config/fish/completions/velero.fish case "bash": cmd.Root().GenBashCompletion(os.Stdout) case "zsh": - cmd.Root().GenZshCompletion(os.Stdout) + // # fix #4912 + // cobra does not support zsh completion ouptput used by source command + // according to https://github.com/spf13/cobra/issues/1529 + // Need to append compdef manually to do that. + zshHead := "#compdef velero\ncompdef _velero velero\n" + out := os.Stdout + out.Write([]byte(zshHead)) + + cmd.Root().GenZshCompletion(out) case "fish": cmd.Root().GenFishCompletion(os.Stdout, true) default: From 44199db79d035c9cd04f4a576b3ba359af7dcc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 17 May 2022 10:21:35 +0800 Subject: [PATCH 159/366] Enhance the map flag to support parsing input value contains entry delimiters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the map flag to support parsing input value contains entry delimiters Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4920-ywk253100 | 1 + pkg/cmd/cli/restore/create.go | 2 +- pkg/cmd/server/server.go | 2 +- pkg/cmd/util/flag/map.go | 36 ++++++++----- pkg/cmd/util/flag/map_test.go | 75 ++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 changelogs/unreleased/4920-ywk253100 create mode 100644 pkg/cmd/util/flag/map_test.go diff --git a/changelogs/unreleased/4920-ywk253100 b/changelogs/unreleased/4920-ywk253100 new file mode 100644 index 0000000000..0867b6935c --- /dev/null +++ b/changelogs/unreleased/4920-ywk253100 @@ -0,0 +1 @@ +Enhance the map flag to support parsing input value contains entry delimiters \ No newline at end of file diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index caf94dc51f..223414d63a 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -98,7 +98,7 @@ func NewCreateOptions() *CreateOptions { return &CreateOptions{ Labels: flag.NewMap(), IncludeNamespaces: flag.NewStringArray("*"), - NamespaceMappings: flag.NewMap().WithEntryDelimiter(",").WithKeyValueDelimiter(":"), + NamespaceMappings: flag.NewMap().WithEntryDelimiter(',').WithKeyValueDelimiter(':'), RestoreVolumes: flag.NewOptionalBool(nil), PreserveNodePorts: flag.NewOptionalBool(nil), IncludeClusterResources: flag.NewOptionalBool(nil), diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 5aaee731c3..be59459c14 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -132,7 +132,7 @@ type controllerRunInfo struct { func NewCommand(f client.Factory) *cobra.Command { var ( - volumeSnapshotLocations = flag.NewMap().WithKeyValueDelimiter(":") + volumeSnapshotLocations = flag.NewMap().WithKeyValueDelimiter(':') logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel) config = serverConfig{ pluginDir: "/plugins", diff --git a/pkg/cmd/util/flag/map.go b/pkg/cmd/util/flag/map.go index 4c50b6ee82..1b4a6e21c8 100644 --- a/pkg/cmd/util/flag/map.go +++ b/pkg/cmd/util/flag/map.go @@ -17,6 +17,7 @@ limitations under the License. package flag import ( + "encoding/csv" "fmt" "strings" @@ -27,25 +28,25 @@ import ( // map data (i.e. a collection of key-value pairs). type Map struct { data map[string]string - entryDelimiter string - keyValueDelimiter string + entryDelimiter rune + keyValueDelimiter rune } -// NewMap returns a Map using the default delimiters ("=" between keys and -// values, and "," between map entries, e.g. k1=v1,k2=v2) +// NewMap returns a Map using the default delimiters ('=' between keys and +// values, and ',' between map entries, e.g. k1=v1,k2=v2) func NewMap() Map { m := Map{ data: make(map[string]string), } - return m.WithEntryDelimiter(",").WithKeyValueDelimiter("=") + return m.WithEntryDelimiter(',').WithKeyValueDelimiter('=') } // WithEntryDelimiter sets the delimiter to be used between map // entries. // -// For example, in "k1=v1&k2=v2", the entry delimiter is "&" -func (m Map) WithEntryDelimiter(delimiter string) Map { +// For example, in "k1=v1&k2=v2", the entry delimiter is '&' +func (m Map) WithEntryDelimiter(delimiter rune) Map { m.entryDelimiter = delimiter return m } @@ -53,8 +54,8 @@ func (m Map) WithEntryDelimiter(delimiter string) Map { // WithKeyValueDelimiter sets the delimiter to be used between // keys and values. // -// For example, in "k1=v1&k2=v2", the key-value delimiter is "=" -func (m Map) WithKeyValueDelimiter(delimiter string) Map { +// For example, in "k1=v1&k2=v2", the key-value delimiter is '=' +func (m Map) WithKeyValueDelimiter(delimiter rune) Map { m.keyValueDelimiter = delimiter return m } @@ -63,17 +64,26 @@ func (m Map) WithKeyValueDelimiter(delimiter string) Map { func (m *Map) String() string { var a []string for k, v := range m.data { - a = append(a, fmt.Sprintf("%s%s%s", k, m.keyValueDelimiter, v)) + a = append(a, fmt.Sprintf("%s%s%s", k, string(m.keyValueDelimiter), v)) } - return strings.Join(a, m.entryDelimiter) + return strings.Join(a, string(m.entryDelimiter)) } // Set parses the provided string according to the delimiters and // assigns the result to the Map receiver. It returns an error if // the string is not parseable. func (m *Map) Set(s string) error { - for _, part := range strings.Split(s, m.entryDelimiter) { - kvs := strings.SplitN(part, m.keyValueDelimiter, 2) + // use csv.Reader to support parsing input string contains entry delimiters. + // e.g. `"k1=a=b,c=d",k2=v2` will be parsed into two parts: `k1=a=b,c=d` and `k2=v2` + r := csv.NewReader(strings.NewReader(s)) + r.Comma = m.entryDelimiter + parts, err := r.Read() + if err != nil { + return errors.Wrapf(err, "error parsing %q", s) + } + + for _, part := range parts { + kvs := strings.SplitN(part, string(m.keyValueDelimiter), 2) if len(kvs) != 2 { return errors.Errorf("error parsing %q", part) } diff --git a/pkg/cmd/util/flag/map_test.go b/pkg/cmd/util/flag/map_test.go new file mode 100644 index 0000000000..6210156809 --- /dev/null +++ b/pkg/cmd/util/flag/map_test.go @@ -0,0 +1,75 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flag + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSetOfMap(t *testing.T) { + cases := []struct { + name string + input string + error bool + expected map[string]string + }{ + { + name: "invalid_input_missing_quote", + input: `"k=v`, + error: true, + }, + { + name: "invalid_input_contains_no_key_value_delimiter", + input: `k`, + error: true, + }, + { + name: "valid input", + input: `k1=v1,k2=v2`, + error: false, + expected: map[string]string{ + "k1": "v1", + "k2": "v2", + }, + }, + { + name: "valid input whose value contains entry delimiter", + input: `k1=v1,"k2=a=b,c=d"`, + error: false, + expected: map[string]string{ + "k1": "v1", + "k2": "a=b,c=d", + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + m := NewMap() + err := m.Set(c.input) + if c.error { + require.NotNil(t, err) + return + } + assert.EqualValues(t, c.expected, m.data) + }) + } + +} From bcef5e1d5e98a39fb0e7b693f052babd5c13b866 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Tue, 17 May 2022 10:39:14 -0300 Subject: [PATCH 160/366] Refactor to simplify Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 53 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 8fdc014da3..57f8c91517 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1128,13 +1128,13 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } } - // Clear out non-core metadata fields. - if obj, err = resetMetadata(obj); err != nil { + objStatus, statusFieldExists, statusFieldErr := unstructured.NestedFieldCopy(obj.Object, "status") + // Clear out non-core metadata fields and status. + if obj, err = resetMetadataAndStatus(obj); err != nil { errs.Add(namespace, err) return warnings, errs } - shouldRestoreStatus := ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) ctx.log.Infof("restore status includes excludes: %+v", ctx.resourceStatusIncludesExcludes) @@ -1247,17 +1247,6 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // and which backup they came from. addRestoreLabels(obj, ctx.restore.Name, ctx.restore.Spec.BackupName) - // Clear out status. - objWithoutStatus := obj.DeepCopy() - if objWithoutStatus, err = resetStatus(objWithoutStatus); err != nil { - errs.Add(namespace, err) - return warnings, errs - } - if !shouldRestoreStatus { - ctx.log.Infof("Resetting status for obj %s/%s", obj.GetKind(), obj.GetName()) - obj = objWithoutStatus - } - ctx.log.Infof("Attempting to restore %s: %v", obj.GroupVersionKind().Kind, name) createdObj, restoreErr := resourceClient.Create(obj) isAlreadyExistsError, err := isAlreadyExistsError(ctx, obj, restoreErr, resourceClient) @@ -1273,18 +1262,12 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } // Remove insubstantial metadata. - fromCluster, err = resetMetadata(fromCluster) + fromCluster, err = resetMetadataAndStatus(fromCluster) if err != nil { ctx.log.Infof("Error trying to reset metadata for %s: %v", kube.NamespaceAndName(obj), err) warnings.Add(namespace, err) return warnings, errs } - fromCluster, err = resetStatus(fromCluster) - if err != nil { - ctx.log.Infof("Error trying to reset status for %s: %v", kube.NamespaceAndName(obj), err) - warnings.Add(namespace, err) - return warnings, errs - } // We know the object from the cluster won't have the backup/restore name // labels, so copy them from the object we attempted to restore. @@ -1292,7 +1275,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso addRestoreLabels(fromCluster, labels[velerov1api.RestoreNameLabel], labels[velerov1api.BackupNameLabel]) fromClusterWithLabels := fromCluster.DeepCopy() // saving the in-cluster object so that we can create label patch if overall patch fails - if !equality.Semantic.DeepEqual(fromCluster, objWithoutStatus) { + if !equality.Semantic.DeepEqual(fromCluster, obj) { switch groupResource { case kuberesource.ServiceAccounts: desired, err := mergeServiceAccounts(fromCluster, obj) @@ -1381,8 +1364,20 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } + shouldRestoreStatus := ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) + if shouldRestoreStatus && statusFieldErr != nil { + err := fmt.Errorf("could not get status to be restored %s: %v", kube.NamespaceAndName(obj), statusFieldErr) + ctx.log.Errorf(err.Error()) + errs.Add(namespace, err) + return warnings, errs + } // if it should restore status, run a UpdateStatus - if shouldRestoreStatus { + if statusFieldExists && shouldRestoreStatus { + if err := unstructured.SetNestedField(obj.Object, objStatus, "status"); err != nil { + ctx.log.Errorf("could not set status field %s: %v", kube.NamespaceAndName(obj), err) + errs.Add(namespace, err) + return warnings, errs + } obj.SetResourceVersion(createdObj.GetResourceVersion()) updated, err := resourceClient.UpdateStatus(obj, metav1.UpdateOptions{}) if err != nil { @@ -1705,6 +1700,18 @@ func resetStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, er return obj, nil } +func resetMetadataAndStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + newObj, err := resetMetadata(obj) + if err != nil { + return nil, err + } + newObj, err = resetStatus(obj) + if err != nil { + return nil, err + } + return newObj, nil +} + // addRestoreLabels labels the provided object with the restore name and the // restored backup's name. func addRestoreLabels(obj metav1.Object, restoreName, backupName string) { From 6b9b13790eed55ac7ce5b29e5c694feee9e5b4dd Mon Sep 17 00:00:00 2001 From: Ming Date: Sun, 15 May 2022 05:20:43 +0000 Subject: [PATCH 161/366] Add schedule ordered resources test Signed-off-by: Ming --- changelogs/unreleased/4913-qiuming-best | 1 + test/e2e/e2e_suite_test.go | 3 + .../e2e/orderedresources/ordered_resources.go | 191 ++++++++++++++++++ test/e2e/util/velero/velero_utils.go | 117 ++++++++--- 4 files changed, 285 insertions(+), 27 deletions(-) create mode 100644 changelogs/unreleased/4913-qiuming-best create mode 100644 test/e2e/orderedresources/ordered_resources.go diff --git a/changelogs/unreleased/4913-qiuming-best b/changelogs/unreleased/4913-qiuming-best new file mode 100644 index 0000000000..406319cc54 --- /dev/null +++ b/changelogs/unreleased/4913-qiuming-best @@ -0,0 +1 @@ +Add schedule ordered resources E2E test diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a2d98727da..a8be501ea8 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -30,6 +30,7 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/basic" . "github.com/vmware-tanzu/velero/test/e2e/basic/resources-check" . "github.com/vmware-tanzu/velero/test/e2e/bsl-mgmt" + . "github.com/vmware-tanzu/velero/test/e2e/orderedresources" . "github.com/vmware-tanzu/velero/test/e2e/privilegesmgmt" . "github.com/vmware-tanzu/velero/test/e2e/resource-filtering" . "github.com/vmware-tanzu/velero/test/e2e/scale" @@ -102,6 +103,8 @@ var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) +var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) + func TestE2e(t *testing.T) { // Skip running E2E tests when running only "short" tests because: // 1. E2E tests are long running tests involving installation of Velero and performing backup and restore operations. diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go new file mode 100644 index 0000000000..ff141470d2 --- /dev/null +++ b/test/e2e/orderedresources/ordered_resources.go @@ -0,0 +1,191 @@ +package orderedresources + +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//the ordered resources test related to https://github.com/vmware-tanzu/velero/issues/4561 +import ( + "context" + "flag" + "fmt" + "math/rand" + "strings" + "time" + + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/pkg/errors" + waitutil "k8s.io/apimachinery/pkg/util/wait" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/test" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/velero" +) + +type OrderedResources struct { + Namespace string + ScheduleName string + OrderMap map[string]string + ScheduleArgs []string + TestCase +} + +func ScheduleOrderedResources() { + BeforeEach(func() { + flag.Parse() + if VeleroCfg.InstallVelero { + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) + } + }) + + AfterEach(func() { + if VeleroCfg.InstallVelero && !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) + } + }) + + It("Create a schedule to backup resources in a specific order should be successful", func() { + test := &OrderedResources{} + err := test.Init() + Expect(err).To(Succeed(), err) + defer func() { + Expect(DeleteNamespace(test.Ctx, test.Client, test.Namespace, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.Namespace)) + err = VeleroScheduleDelete(test.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.ScheduleName) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to delete schedule with err %v", err)) + }() + + By(fmt.Sprintf("Prepare workload as target to backup in base namespace %s", test.Namespace), func() { + err = test.CreateResources() + Expect(err).To(Succeed(), fmt.Sprintf("Failed to create resources to backup with err %v", err)) + }) + + By(fmt.Sprintf("Create schedule the workload in %s namespace", test.Namespace), func() { + err = VeleroScheduleCreate(test.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.ScheduleName, test.ScheduleArgs) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to create schedule %s with err %v", test.ScheduleName, err)) + }) + + By(fmt.Sprintf("Checking resource order in %s schedule cr", test.ScheduleName), func() { + err = CheckScheduleWithResourceOrder(test.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.ScheduleName, test.OrderMap) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s with err %v", test.ScheduleName, err)) + }) + + By("Checking resource order in backup cr", func() { + backupList := new(velerov1api.BackupList) + err = waitutil.PollImmediate(10*time.Second, time.Minute*5, func() (bool, error) { + if err = test.Client.Kubebuilder.List(test.Ctx, backupList, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + return false, fmt.Errorf("failed to list backup object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) + } + + for _, backup := range backupList.Items { + if err = CheckBackupWithResourceOrder(test.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup.Name, test.OrderMap); err == nil { + return true, nil + } + } + fmt.Printf("still finding backup created by schedule %s ...\n", test.ScheduleName) + return false, nil + }) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s created backup with err %v", test.ScheduleName, err)) + }) + + }) +} + +func (o *OrderedResources) Init() error { + rand.Seed(time.Now().UnixNano()) + UUIDgen, _ = uuid.NewRandom() + client, err := NewTestClient() + if err != nil { + return fmt.Errorf("failed to init ordered resources test with err %v", err) + } + o.Client = client + o.ScheduleName = "schedule-ordered-resources-" + UUIDgen.String() + o.NSBaseName = "schedule-ordered-resources" + o.Namespace = o.NSBaseName + "-" + UUIDgen.String() + o.OrderMap = map[string]string{ + "deployments": fmt.Sprintf("deploy-%s", o.NSBaseName), + "secrets": fmt.Sprintf("secret-%s", o.NSBaseName), + "configmaps": fmt.Sprintf("configmap-%s", o.NSBaseName), + } + + o.ScheduleArgs = []string{"--schedule", "@every 1m", + "--include-namespaces", o.Namespace, "--default-volumes-to-restic", "--ordered-resources"} + var orderStr string + for kind, resource := range o.OrderMap { + orderStr += fmt.Sprintf("%s=%s;", kind, resource) + } + o.ScheduleArgs = append(o.ScheduleArgs, strings.TrimRight(orderStr, ";")) + + return nil +} + +func (o *OrderedResources) CreateResources() error { + o.Ctx, _ = context.WithTimeout(context.Background(), 5*time.Minute) + label := map[string]string{ + "orderedresources": "true", + } + fmt.Printf("Creating resources in %s namespace ...\n", o.Namespace) + if err := CreateNamespace(o.Ctx, o.Client, o.Namespace); err != nil { + return errors.Wrapf(err, "failed to create namespace %s", o.Namespace) + } + serviceAccountName := "default" + // wait until the service account is created before patch the image pull secret + if err := WaitUntilServiceAccountCreated(o.Ctx, o.Client, o.Namespace, serviceAccountName, 10*time.Minute); err != nil { + return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, o.Namespace) + } + // add the image pull secret to avoid the image pull limit issue of Docker Hub + if err := PatchServiceAccountWithImagePullSecret(o.Ctx, o.Client, o.Namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { + return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, o.Namespace) + } + //Create deployment + deploymentName := fmt.Sprintf("deploy-%s", o.NSBaseName) + fmt.Printf("Creating deployment %s in %s namespaces ...\n", deploymentName, o.Namespace) + deployment := NewDeployment(deploymentName, o.Namespace, 1, label) + deployment, err := CreateDeployment(o.Client.ClientGo, o.Namespace, deployment) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to create namespace %q with err %v", o.Namespace, err)) + } + err = WaitForReadyDeployment(o.Client.ClientGo, o.Namespace, deployment.Name) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", o.Namespace)) + } + //Create Secret + secretName := fmt.Sprintf("secret-%s", o.NSBaseName) + fmt.Printf("Creating secret %s in %s namespaces ...\n", secretName, o.Namespace) + _, err = CreateSecret(o.Client.ClientGo, o.Namespace, secretName, label) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to create secret in the namespace %q", o.Namespace)) + } + err = WaitForSecretsComplete(o.Client.ClientGo, o.Namespace, secretName) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", o.Namespace)) + } + //Create Configmap + configmapName := fmt.Sprintf("configmap-%s", o.NSBaseName) + fmt.Printf("Creating configmap %s in %s namespaces ...\n", configmapName, o.Namespace) + _, err = CreateConfigMap(o.Client.ClientGo, o.Namespace, configmapName, label) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to create configmap in the namespace %q", o.Namespace)) + } + err = WaitForConfigMapComplete(o.Client.ClientGo, o.Namespace, configmapName) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", o.Namespace)) + } + return nil +} diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 449f6e0a50..fc14335c85 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -28,6 +28,7 @@ import ( "os" "os/exec" "path/filepath" + "reflect" "regexp" "runtime" "strings" @@ -157,41 +158,49 @@ func getProviderVeleroInstallOptions( return io, nil } -// checkBackupPhase uses VeleroCLI to inspect the phase of a Velero backup. -func checkBackupPhase(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string, - expectedPhase velerov1api.BackupPhase) error { - checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "backup", "get", "-o", "json", - backupName) - - fmt.Printf("get backup cmd =%v\n", checkCMD) +func CMDExecWithOutput(checkCMD *exec.Cmd) (*[]byte, error) { stdoutPipe, err := checkCMD.StdoutPipe() if err != nil { - return err + return nil, err } jsonBuf := make([]byte, 16*1024) // If the YAML is bigger than 16K, there's probably something bad happening err = checkCMD.Start() if err != nil { - return err + return nil, err } bytesRead, err := io.ReadFull(stdoutPipe, jsonBuf) if err != nil && err != io.ErrUnexpectedEOF { - return err + return nil, err } if bytesRead == len(jsonBuf) { - return errors.New("yaml returned bigger than max allowed") + return nil, errors.New("yaml returned bigger than max allowed") } jsonBuf = jsonBuf[0:bytesRead] err = checkCMD.Wait() + if err != nil { + return nil, err + } + return &jsonBuf, err +} + +// checkBackupPhase uses VeleroCLI to inspect the phase of a Velero backup. +func checkBackupPhase(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string, + expectedPhase velerov1api.BackupPhase) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "backup", "get", "-o", "json", + backupName) + + fmt.Printf("get backup cmd =%v\n", checkCMD) + jsonBuf, err := CMDExecWithOutput(checkCMD) if err != nil { return err } backup := velerov1api.Backup{} - err = json.Unmarshal(jsonBuf, &backup) + err = json.Unmarshal(*jsonBuf, &backup) if err != nil { return err } @@ -208,41 +217,80 @@ func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st restoreName) fmt.Printf("get restore cmd =%v\n", checkCMD) - stdoutPipe, err := checkCMD.StdoutPipe() + jsonBuf, err := CMDExecWithOutput(checkCMD) if err != nil { return err } + restore := velerov1api.Restore{} + err = json.Unmarshal(*jsonBuf, &restore) + if err != nil { + return err + } + if restore.Status.Phase != expectedPhase { + return errors.Errorf("Unexpected restore phase got %s, expecting %s", restore.Status.Phase, expectedPhase) + } + return nil +} - jsonBuf := make([]byte, 16*1024) // If the YAML is bigger than 16K, there's probably something bad happening - - err = checkCMD.Start() +func checkSchedulePhase(ctx context.Context, veleroCLI, veleroNamespace, scheduleName string) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "schedule", "get", scheduleName, "-ojson") + jsonBuf, err := CMDExecWithOutput(checkCMD) + if err != nil { + return err + } + schedule := velerov1api.Schedule{} + err = json.Unmarshal(*jsonBuf, &schedule) if err != nil { return err } - bytesRead, err := io.ReadFull(stdoutPipe, jsonBuf) + if schedule.Status.Phase != velerov1api.SchedulePhaseEnabled { + return errors.Errorf("Unexpected restore phase got %s, expecting %s", schedule.Status.Phase, velerov1api.SchedulePhaseEnabled) + } + return nil +} - if err != nil && err != io.ErrUnexpectedEOF { +func CheckScheduleWithResourceOrder(ctx context.Context, veleroCLI, veleroNamespace, scheduleName string, order map[string]string) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "schedule", "get", scheduleName, "-ojson") + jsonBuf, err := CMDExecWithOutput(checkCMD) + if err != nil { return err } - if bytesRead == len(jsonBuf) { - return errors.New("yaml returned bigger than max allowed") + schedule := velerov1api.Schedule{} + err = json.Unmarshal(*jsonBuf, &schedule) + if err != nil { + return err } - jsonBuf = jsonBuf[0:bytesRead] - err = checkCMD.Wait() + if schedule.Status.Phase != velerov1api.SchedulePhaseEnabled { + return errors.Errorf("Unexpected restore phase got %s, expecting %s", schedule.Status.Phase, velerov1api.SchedulePhaseEnabled) + } + if reflect.DeepEqual(schedule.Spec.Template.OrderedResources, order) { + return nil + } else { + return fmt.Errorf("resource order %v set in schedule command is not equal with order %v stored in schedule cr", order, schedule.Spec.Template.OrderedResources) + } +} + +func CheckBackupWithResourceOrder(ctx context.Context, veleroCLI, veleroNamespace, backupName string, order map[string]string) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "get", "backup", backupName, "-ojson") + jsonBuf, err := CMDExecWithOutput(checkCMD) if err != nil { return err } - restore := velerov1api.Restore{} - err = json.Unmarshal(jsonBuf, &restore) + backup := velerov1api.Backup{} + err = json.Unmarshal(*jsonBuf, &backup) if err != nil { return err } - if restore.Status.Phase != expectedPhase { - return errors.Errorf("Unexpected restore phase got %s, expecting %s", restore.Status.Phase, expectedPhase) + if backup.Status.Phase != velerov1api.BackupPhaseCompleted { + return errors.Errorf("Unexpected restore phase got %s, expecting %s", backup.Status.Phase, velerov1api.BackupPhaseCompleted) + } + if reflect.DeepEqual(backup.Spec.OrderedResources, order) { + return nil + } else { + return fmt.Errorf("resource order %v set in backup command is not equal with order %v stored in backup cr", order, backup.Spec.OrderedResources) } - return nil } // VeleroBackupNamespace uses the veleroCLI to backup a namespace. @@ -320,6 +368,21 @@ func VeleroBackupExec(ctx context.Context, veleroCLI string, veleroNamespace str return checkBackupPhase(ctx, veleroCLI, veleroNamespace, backupName, velerov1api.BackupPhaseCompleted) } +func VeleroScheduleDelete(ctx context.Context, veleroCLI string, veleroNamespace string, scheduleName string) error { + args := []string{"--namespace", veleroNamespace, "delete", "schedule", scheduleName, "--confirm"} + return VeleroCmdExec(ctx, veleroCLI, args) +} + +func VeleroScheduleCreate(ctx context.Context, veleroCLI string, veleroNamespace string, scheduleName string, args []string) error { + args = append([]string{ + "--namespace", veleroNamespace, "create", "schedule", scheduleName, + }, args...) + if err := VeleroCmdExec(ctx, veleroCLI, args); err != nil { + return err + } + return checkSchedulePhase(ctx, veleroCLI, veleroNamespace, scheduleName) +} + func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { cmd := exec.CommandContext(ctx, veleroCLI, args...) cmd.Stdout = os.Stdout From 0e649b9d3ffbbed0f7457933918f364392c71d69 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 17 May 2022 12:09:49 -0400 Subject: [PATCH 162/366] Fix typos Signed-off-by: Abigail McCarthy --- site/content/docs/main/migration-case.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index c6de64f7b2..027083eab0 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -3,7 +3,7 @@ title: "Cluster migration" layout: docs --- -Velero's backup and restore capabilities make it a valuable tool for migrating your data between clusters, helping you perform many common workflows such as upgrading Kubernetes versions or disaster recovery. Cluster migration with Velero is based on Velero's [object storage sync](how-velero-works.md#object-storage-sync) functionality, which is responsible for syncing Velero resources from your designated object storage to your cluster. This means that to perform cluster migration with Velero you must point each Velero instance running on clusters involved with the migration to the same cloud object storage location. +Velero's backup and restore capabilities make it a valuable tool for migrating your data between clusters. Cluster migration with Velero is based on Velero's [object storage sync](how-velero-works.md#object-storage-sync) functionality, which is responsible for syncing Velero resources from your designated object storage to your cluster. This means that to perform cluster migration with Velero you must point each Velero instance running on clusters involved with the migration to the same cloud object storage location. This page outlines a cluster migration scenario and some common configurations you will need to start using Velero to begin migrating data. @@ -36,13 +36,13 @@ This scenario steps through the migration of resources from Cluster 1 to Cluster default aws velero-migration-demo Available 2022-05-13 13:41:30 +0800 CST ReadWrite true ``` -1. Still on Cluster 1, make sure you have a backup of your cluster. Replace `` with a name for your back up. +1. Still on Cluster 1, make sure you have a backup of your cluster. Replace `` with a name for your backup. ``` velero backup create ``` - Alternatively, you can create a [scheduled back up](https://velero.io/docs/main/backup-reference/#schedule-a-backup) of your data with the Velero `schedule` operation. This is the recommended way to make sure your data is automatically backed up according to the schedule you define. + Alternatively, you can create a [scheduled backup](https://velero.io/docs/main/backup-reference/#schedule-a-backup) of your data with the Velero `schedule` operation. This is the recommended way to make sure your data is automatically backed up according to the schedule you define. The default backup retention period, expressed as TTL (time to live), is 30 days (720 hours); you can use the `--ttl ` flag to change this as necessary. See [how velero works](how-velero-works.md#set-a-backup-to-expire) for more information about backup expiry. @@ -52,7 +52,7 @@ This scenario steps through the migration of resources from Cluster 1 to Cluster velero install --provider aws --image velero/velero:v1.8.0 --plugins velero/velero-plugin-for-aws:v1.4.0 --bucket velero-migration-demo --secret-file xxxx/aws-credentials-cluster2 --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 ``` - Alternatively you could configure `BackupStorageLocations` or `VolumeSnapshotLocations` after installing Velero on Cluster 2, pointing to the `--bucket` location used by Cluster 1. To do this you can use to `velero backup-location create` and `velero snapshot-location create` commands. + Alternatively you could configure `BackupStorageLocations` and `VolumeSnapshotLocations` after installing Velero on Cluster 2, pointing to the `--bucket` location and `region` used by Cluster 1. To do this you can use to `velero backup-location create` and `velero snapshot-location create` commands. ``` velero backup-location create bsl --provider aws --bucket velero-migration-demo --config region=us-east-2 --access-mode=ReadOnly @@ -61,21 +61,27 @@ This scenario steps through the migration of resources from Cluster 1 to Cluster Make sure to configure the `BackupStorageLocations` as read-only by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. See `velero backup-location –help` for more information about the available flags for this command. + ``` + velero snapshot-location create vsl --provider aws --config region=us-east-2 + ``` + See `velero snapshot-location –help` for more information about the available flags for this command. + + 1. Continuing on Cluster 2, make sure that the Velero Backup object created on Cluster 1 is available. `` should be the same name used to create your backup of Cluster 1. ``` velero backup describe ``` - Velero resources are [synchronized](how-velero-works.md#object-storage-sync) with the backup files in object storage. This means that the Velero resources created by Cluster 1's backup will be synced to Cluster 2 through the shared Backup Storage Location. Once the sync occurs, you will be able to access the back up from Cluster 1 on Cluster 2 using Velero commands. The default sync interval is 1 minute, so you may need to wait before checking for the back up's availability on Cluster 2. You can configure this interval with the `--backup-sync-period` flag to the Velero server on Cluster 2. + Velero resources are [synchronized](how-velero-works.md#object-storage-sync) with the backup files in object storage. This means that the Velero resources created by Cluster 1's backup will be synced to Cluster 2 through the shared Backup Storage Location. Once the sync occurs, you will be able to access the backup from Cluster 1 on Cluster 2 using Velero commands. The default sync interval is 1 minute, so you may need to wait before checking for the backup's availability on Cluster 2. You can configure this interval with the `--backup-sync-period` flag to the Velero server on Cluster 2. -1. On Cluster 2, once you have confirmed that the right back up is available, you can restore everything to Cluster 2. +1. On Cluster 2, once you have confirmed that the right backup is available, you can restore everything to Cluster 2. ``` velero restore create --from-backup ``` - Make sure `` is the same back up name from Cluster 1. + Make sure `` is the same backup name from Cluster 1. ## Verify Both Clusters From 864cba69e24e1d773c022dbbfac47db365cdef46 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 17 May 2022 12:14:33 -0400 Subject: [PATCH 163/366] Fix typos Signed-off-by: Abigail McCarthy --- site/content/docs/main/customize-installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/main/customize-installation.md b/site/content/docs/main/customize-installation.md index 721e7cefba..e82002eec3 100644 --- a/site/content/docs/main/customize-installation.md +++ b/site/content/docs/main/customize-installation.md @@ -100,7 +100,7 @@ At installation, Velero sets default resource requests and limits for the Velero |Memory limit|512Mi|1024Mi| {{< /table >}} -Depending on the cluster resources you, especially if you are using Restic, you may need to increase these limits. Through testing, the Velero maintainers have found these defaults to work well when backing up and restoring 1000 or less resources and total size of files per backup or restore is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. +Depending on the cluster resources, especially if you are using Restic, you may need to increase these defaults. Through testing, the Velero maintainers have found these defaults work well when backing up and restoring 1000 or less resources and total size of files is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. Due to a [known Restic issue](https://github.com/restic/restic/issues/2446), the Restic pod will consume large amounts of memory, especially if you are backing up millions of tiny files and directories. If you are planning to use Restic to backup 100GB of data or more, you will need to increase the resource limits to make sure backups complete successfully. From c46199c8808ee2e37adb9327e5c15dc093665829 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 17 May 2022 13:05:17 -0400 Subject: [PATCH 164/366] Adds informaiton about resticRepoPrefix Signed-off-by: Abigail McCarthy --- site/content/docs/main/restic.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/site/content/docs/main/restic.md b/site/content/docs/main/restic.md index ac75f0f9d0..617b3e7c66 100644 --- a/site/content/docs/main/restic.md +++ b/site/content/docs/main/restic.md @@ -36,6 +36,10 @@ velero install --use-restic When using Restic on a storage provider that doesn't have Velero support for snapshots, the `--use-volume-snapshots=false` flag prevents an unused `VolumeSnapshotLocation` from being created on installation. +Velero handles the creation of the restic repo prefix for Amazon, Azure, and GCP plugins, if you are using a different [provider plugin](supported-providers.md), then you will need to make sure the `resticRepoPrefix` is set in the [BackupStorageLocation `config`](api-types/backupstoragelocation.md). The value for `resticRepoPrefix` should be the cloud storage URL where all namespace restic repos will be created. Velero creates one restic repo per namespace. For example, if backing up 2 namespaces, namespace1 and namespace2, using restic on AWS, the `resticRepoPrefix` would be something like `s3:s3-us-west-2.amazonaws.com/bucket/restic` and the full restic repo path for namespace1 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns1` and for namespace2 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns2`. + +There may be additional installation steps depending on the cloud provider plugin you are using. You should refer to the [plugin specific documentation](supported-providers.md) for the must up to date information. + ### Configure Restic DaemonSet spec After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the Restic DaemonSet spec. The steps in this section are only needed if you are installing on RancherOS, OpenShift, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. @@ -169,7 +173,6 @@ kubectl patch storageclass/ \ --patch '[{"op":"add","path":"/mountOptions/-","value":"nouser_xattr"}]' ``` - ## To back up Velero supports two approaches of discovering pod volumes that need to be backed up using Restic: @@ -183,7 +186,7 @@ The following sections provide more details on the two approaches. In this approach, Velero will back up all pod volumes using Restic with the exception of: -- Volumes mounting the default service account token, kubernetes secrets, and config maps +- Volumes mounting the default service account token, Kubernetes secrets, and config maps - Hostpath volumes It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` annotation on the pod. @@ -412,7 +415,7 @@ data: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 - runAsGroup: 999 + runAsGroup: 999 ``` From c6d568ad1248154151999eab8a6df3092fbf65e9 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Tue, 17 May 2022 23:02:28 -0400 Subject: [PATCH 165/366] Make updates from review Signed-off-by: Abigail McCarthy --- site/content/docs/main/migration-case.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index 027083eab0..afb3a3e2a2 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -58,8 +58,8 @@ This scenario steps through the migration of resources from Cluster 1 to Cluster velero backup-location create bsl --provider aws --bucket velero-migration-demo --config region=us-east-2 --access-mode=ReadOnly ``` - Make sure to configure the `BackupStorageLocations` as read-only - by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. See `velero backup-location –help` for more information about the available flags for this command. + Its recommended that you configure the `BackupStorageLocations` as read-only + by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. This will make sure that the backup is not deleted from the object store by mistake during the restore. See `velero backup-location –help` for more information about the available flags for this command. ``` velero snapshot-location create vsl --provider aws --config region=us-east-2 From 95ccbb617a24f57438d33a1d24298cd5080adb84 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Wed, 18 May 2022 23:20:00 -0400 Subject: [PATCH 166/366] Update docs for flag to skip TLS validation Signed-off-by: Abigail McCarthy --- site/content/docs/main/self-signed-certificates.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/site/content/docs/main/self-signed-certificates.md b/site/content/docs/main/self-signed-certificates.md index 0bb55be8cc..ae4f3f854d 100644 --- a/site/content/docs/main/self-signed-certificates.md +++ b/site/content/docs/main/self-signed-certificates.md @@ -46,3 +46,12 @@ Error 116 represents certificate required as seen here in [error codes](https:// Velero as a client does not include its certificate while performing SSL handshake with the server. From [TLS 1.3 spec](https://tools.ietf.org/html/rfc8446), verifying client certificate is optional on the server. You will need to change this setting on the server to make it work. + + +## Skipping TLS verification + +**Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premise environment. Using this flag in production is not recommended. + +Velero provides a way for you to skip TLS verification on the object store by passing the `--insecure-skip-tls-verify` flag with Velero commands. If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. + +This flag is currently only implemented for use with AWS provider plugin and Restic. From bf467e3ac338d790e9f2ed1cd165968c1117ba9c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 19 May 2022 16:48:34 +0800 Subject: [PATCH 167/366] Unified Repository Design Signed-off-by: Lyndon-Li --- changelogs/unreleased/4926-lyndon | 1 + .../br-workflow.png | Bin 0 -> 143971 bytes .../debug-log-repository.png | Bin 0 -> 58712 bytes .../debug-log-uploader.png | Bin 0 -> 62774 bytes .../maintenance-workflow.png | Bin 0 -> 80375 bytes .../progress-update.png | Bin 0 -> 57729 bytes .../scope.png | Bin 0 -> 32284 bytes .../snapshot-deletion-workflow.png | Bin 0 -> 122200 bytes .../unified-repo-and-kopia-integration.md | 320 ++++++++++++++++++ .../unified-repo.png | Bin 0 -> 38955 bytes 10 files changed, 321 insertions(+) create mode 100644 changelogs/unreleased/4926-lyndon create mode 100644 design/unified-repo-and-kopia-integration/br-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/debug-log-repository.png create mode 100644 design/unified-repo-and-kopia-integration/debug-log-uploader.png create mode 100644 design/unified-repo-and-kopia-integration/maintenance-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/progress-update.png create mode 100644 design/unified-repo-and-kopia-integration/scope.png create mode 100644 design/unified-repo-and-kopia-integration/snapshot-deletion-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md create mode 100644 design/unified-repo-and-kopia-integration/unified-repo.png diff --git a/changelogs/unreleased/4926-lyndon b/changelogs/unreleased/4926-lyndon new file mode 100644 index 0000000000..d5c23db47c --- /dev/null +++ b/changelogs/unreleased/4926-lyndon @@ -0,0 +1 @@ +Unified Repository Design \ No newline at end of file diff --git a/design/unified-repo-and-kopia-integration/br-workflow.png b/design/unified-repo-and-kopia-integration/br-workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..6d935c7d043d293cfd27aefbe91f3a48aeec9936 GIT binary patch literal 143971 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%0Af6skY@TEUZdQs^ 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%0Aa^9zEVP)2v1A1`tD;79AT-Jv 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 MLSDQ~)F9~p0i_KJH2?qr literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/debug-log-repository.png b/design/unified-repo-and-kopia-integration/debug-log-repository.png new file mode 100644 index 0000000000000000000000000000000000000000..640d12944659f2690918d933cd79483651feaa62 GIT binary patch literal 58712 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/maintenance-workflow.png b/design/unified-repo-and-kopia-integration/maintenance-workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..938eb45d24211ae1b07fef53fb3b41bb7ed864eb GIT binary patch literal 80375 zcmZ^L1y~(Pvo?@J(7*u_f&~svf&~u*cMb0D5Zv7%XmEE8F2UV{OM<&Q!QKC1_xt|4 zVej2#ABHpCJvG(URd2mjX9J}^3B5qXL4<;WdLbgrF9QYj%pVF01{MA}@JXvvVH^|` z;*v2RpOgq6A4JO9!ob*69|}r1FeVO8E~*#PbMs-8j~50ZLl~h6VFUp}>Wj(E!xkVa z41+-0>6@-4gsEsL4@PS%_bwztEGh9Uf<^lgq|?z13HCl#qneU(;yQ$HxEXReXuL`5 zb=}+;qP8&@s(S{-8ciN!&?*N-PdZ&5z^!SZ<_d#xfvg3EeHGv8)g31`y}XjalnG3NVRRU1+jP> zAp#Nm`=R`(Tm9rHax96IvcRAvh$#$IYUgUN3^Zh#(AfZm>(vn-qR`YQBCr@(<7?S# z%}xwRNGB`<14BiZ6TCK3rv@TaLg;(SD@jh-hZQd6?X+$&`C~nG4DSn!{v7Paxd$7Q z`x-q1if=mcggzvZumTW@}rnE z7dP) zmAIji8Pt4lF+iJX{1JPWB6I?r-5tYf#@Iv^@lfenjbx6bPbzB;hbB zD8+t388Uh3Gtb6(x%kM_D}IvZA=RUv@%sG4-C_vw7J2_>!e?+v`8|?B2chN5MyOAI zuREM9p&QXkJPVikEQwt)n_(8cT$bVv;Stm2q>!Tt`=0;cwUW}#1IZzZeo+a0#~YJX zAbpmHDF?TMSpdi6d+pnmX>3F`^L)<>g;yx+YU{^V%+`x7(Nkz!;`%pkNb|w?f*u{= zboE#!As@d42X75W*}0>k>zwP@0|S{;T-mygc{dAu?DP#r$Z~;62>9AD`zvF2iOD0 z1LFaYOEeu_9AV%0y|-t$*cAVim=U2G`6-5A;AeiDOme!QctJJ3BFF^BFa`nIVnJRW zR(@^%fGk>JkwU%{Yo@hyj^a9oNQ$1oN;tX*;)vjgyA0(v=QeeMI5Y80I7ZL(+SfIe zW7cDgVU6dixdsd60J35o>xrt7uRqnlsb;yzJMc?t(n?7d)TkdMFNAaQEedf-w1_`c-lszk zw)S(IN*P#oEy2qA^convr;Ohl&<3P~@cAv{cYNxQCbr2k%5u$mp4HvOuN!L>WR+l* zbW|4T)r%LkPG(1jDvt1tN;YDfc@$+7)nLQmyTR^2;=uEPs)4!1jpA8O8({z}Z&hxq0-Duso-eBIapeUfY`oHs^4b|(0 z>Xr{33@uG5O7Z>Fki5@2n!Nd;)W+QAerIKe$R^(b+m^{n!g_vrW-zm&T^U#D_56GD zX7j?iPxWrr4pzhVTs!yv{$GXayLrOArZ+15DlUfhPZ*Eaoc51?AL%%gI^AuJubOQQ zo|K%s@96#}KAhe>NjB^Kq)VXFa$I#1bA^9#*J`y{v{ZSWaSL-7bjx-(ewTGed6x#l zg3W$D28#^G21kQr`l21=2Z{mN*__q7no~P`m|}7iuylccOG2Mbzh;~#pGQ@xkHSvw zI26kowvjj#Ghl}JOna`3T05*`u;aD=0~_i`T33znMnkl7w0>eXBIAf)B2>ZVu;Z^Y zFVEi35UHTeWAX)xgyes{0`I<7#hJxmp)?nmi*ZbGv~sn9p&=yG)@<)_eYjHHydY;I z$0S=&*J{?<_7+33!!)2`uL>v+sM%6NCwt4htu^vm1FJNVC2~MwA%cBi^RVVf6VVl< z@6D5uTPAnO{(~WhQ6DnSU?;Yd-lHlhd=pI{2~ZP~xE0 z;&xX<#Pe%ir(xxIg%V;A|7O?0b?WVu zlTF-k$@RQ*L$HI#z4YyB6Ni@zPT^odzeyPrE7S0K*YCZ{z%$uB&J?#FH_iKTzYkc! zR_RR>Rn_O|`)v|-mMNL$hf*sUxc+x2ER(Zi2gX{AFA1Q9UJ7a4CZC?nXAug`hJFtn zf6%$^4_&QX75FNpYrbk3m6xB(+FZBGe;8Pe85X|j8+u+nbJbZ5G`TEGl-_(w{^JZEXsiEGbQ@M{>F;y;8Wwe{ zx)I0mtDPg0p~+B(dRt}PVcWR&xi-IGP}pR*ClP!Q;xUNqgR z-`~XDdmFA9t`EORokaP7oQ#Kz)p(?HAABD-5|_p2_aJrC9jb^~>VDO+8CKX~)p1v< zU)sH3>R5PZ|L`%_!HjsBSaQLzncz<9RQhJsOR`AvO`0wj7lHaQ>_yH>;re8(1>qw8 zSN+hhB2M%R{FI)&&yyozT9fXCJ@8PW_MSs^UPaxV`Eo~Dcq70(1d_WPB3MDEf5Y|6 z(}(iac&6n-Ix<3Z|F)+AlI*?30rh(;5AOlpDJZKWn>LbVB#|8-*Tn=Hss!X&N~)`y z#iXmNX%enS0P{2Pml=X}6mD1Gys zVnwAJfV?{TN+Jdl5>W4eYj`LaXmqG&z!fy`FORRjTo7)&!*p4rs#sDyz!&pwqR!KsfP1nMV zMqAH9N1w*g%<^#x6uToEaA~G*s||59Gc~tibL1fUYXlo`{rE905#+BSwk8}zN)l2K zJ_~Do2s6!ln)gJUh!6;b-CECpO@?3aKhuG~IEajFZ7tboX&oFKXdD=5EUXP_=~!7= zY2VY+($iA|BdBeh%x$$Dsm*PO|6b(3>+tK_=vo_F+8SG!Lmt=F*0Hd&*<($T!9{okh98XNo{(;n~qJ?*de`ulP0kBhNM89VBm zD)Sqg0a^t_<7A>|VgKtf|GM)(g8n^I-bUY=&%z9tXv_IO+wz~u|GfEh;$M$c`kzO# zzGwL7BmZ&dA2T0cflbod7^Nr9}53_H~;z+*iKGFcG~}M z8k~rPvvRypP~1=={5*1w(7#h)ljY=byEHMw_xE!?<}~nd^WuO6aL_<<$RHm^>Gxc( zYC>>XKGCSvF6|dI zX1c;Jb7P3E_s7L;rg zT>EY~jIUg##dF&1#vTk3_WZ|Bs|ma#KVD`H*h_)X=v-Ok@RBcMOUxB!Q0DX9kafyG zMtXv7{kX$hY<=(kN7(-g9>575ri{YJ%bRST)8!T^=C4{zYtw3{PxRjbe@{7dgTb%D z!>N&`d9$B&-`;ldfIRi=AMgG+I!pX`ofiI6RbG||wd#0@iG)Neoq-slB-XPnj`1dgq)&u8Ks(I)U|U6RHA{E?)78v1 z!rvrGLbI0SA6f;Tdq@pqPGGa8`e91t&!<46!;8bW}^)GXAx8q2rHx28$m?0A%!%;BOyuir-^5=jzhwTVxz zq^kyv^u*K;;i1EV_!#tkwy~=(eoqrueIBQ8IHk8(Xgr=VDHUmQK18!sdn9-gq*R)-&$7xRNv0Q07F2fE|Xf!$=|E?JRGF#dgL%Azkv<_g$ zzO~3dPeUsUg5yB(OCl7TH#)|gQ(bVzM=L+dolXptt3JnYo7(No$d{-$Yz*KoTz#XO zT>bX@FtlOAWA1#e&L&6jD~^h-{q~5!M6qfv7dU9TTtDi3Z?^wzcQR{yVQ;pkzbAsY zV@+4+W;o>|xkMskL?o$LUo5q%-oZi>z1a-o)!C%xdc`p3sM(izlu}I=^SPf+?e*^8 z+?ub>3=2j6>3uvw$k1Vde6re({cF8ZNSu}qMd}UJL-w0PZ<^G~w7e=mkBg?d9*b;G zmZl}MJINfhxLlNk;B%-Pl*;CY-d?VTg}rRC+5BWYQIt>}bay%`NpCQKWr)A_uv1tH zT0vNUIP5@Gf#L<-pA2&ix#miy#m+Z4h~>-VoHl2RM2RLc8lS?ogB4gvqJ1ijN68Fb zZY~aJU(=}lyrEUECpl=oDXD1kc(|*;$b-gS*{XH$a+2MYgYq{L6LX!5u ziuWLmUO&k7xR-oJ^lBr?V#iH3_fyRE*{;}lf&BWYB-g9=RuULw;y+FOk+F$-BS|ro z5Zlmyqmzkm4cyndvocuwJ=wm4N2iRl8#&HqSR~bD{Xw@2`D(9bu~G?_)3p{5R1hga zwZ^=6x3q=k1uk>6)oNF70)t^?F!_+r`mV>rO`7Xr8{9~tBCXdYtL;|!sB?|QBB${} zV;!cer8|A~nx4FM+L?T)|?;q*fs*Z9EP#@Uzdl1uXwd z`EkY_MTJa`X#L$3^PNBZ!LKa;1;K8jow&L$6DNb5EjNW){(qcx2o!>&?LfvdlC+mz ztdf7uejs_OCE`n^k)_DI5yk z&xr;=3Sq!*`Ns8&UvDOu1&GyZ0H&<4F_4foFZlg_Qqx1EKbAV>3L<>;ooqFg)#co@ zH=ZsF8J}G#?8?UvjR-jt&5f7frzCuv^?IMk?d1`vX0t2X2V^Qg0{6=d^Nw^LAKRlc zaC;EEcChcU)-p08;)t97j|$!H&}*!s0!@#*D$MFB?Q7Uz{d(K&bd4v64ws-=)Gp-KCL{pYf7+aXCz>5iOI)3^aA0MBTT~?co z=!g3o$zb_t`R_UchO8+c9b&6YCU;QPG^97X2|S9z_@LU`{q1>ra9%41@Ya4M6AtHY z@Z$YxK3gr(V3&CPz4Hp3r_>EMAxv@#J6i^o*!mtgQj-%3V?M*JUeBuFnKlziixLYJ zOYLKg&xwTxhq)ep&k;zs46tfb+daD?_#>o*drD#)#(xMw%#q8NG2X@Px@yZg38rlh z7l|UAg!|$V%%L~Wk`(~6O#GqCvipQ)BFb$%}wv6+eYIMCS4v2=n+o zmLG-h5wP@rBAUy?JvRn?vrHBY+RZc(O`y*y!982@srHUCX|Ka_34BXcgn*{x@Ox@` z?v->)azZWdAJBtjDGtO=hN9kGpj^X)sqkJ2hVa|c0z#n*5JM`r`l86gxETq{kbE&g z_(P?tH6`L{oa$l3owaPWpK;LOOtAUU__5U3=~7V?K7>&FQuqe=jt?uf|NNrqekBmb zkj!KfJA^7mdYq0ws+Q2gk3M<8d1%=`JbkI8?-UF@l z@yuo_Z{lHpF-3r}Pm?T~f9>ajmEehFo1E>a5j9Km@TrzM;`^Tf0AeewnEbdyYuim-Zkds0A4hpwPx>2fx%NDvp( z{ltef#(N4;`~O;pYI_!;kKT2rj8WtnAyL+--xE$a@H?*?JUSF7s2X?#lZeiIX{Ouzx&l#GiM8Wnp4~r81KInV;{R>9#z12jNys z82qHZ_5ePbH%A_~Uwx<-1u>tTIIszb{WI$+3s2|$oWmy^C!} zM*3%#@i*$T1nw!JXlpYlFxQ-V`*qD=x3P?-FI(H9>9dWMCT zHsv3B0#F~M>@kB9MvC%q|L&-McT!)j_)kRcCDf`L%STL9Yg-$AjqBffGpZf-#}chA zk3w!)cn91eaM+Ku+%rDOFyN_R@W12XpuJKunpS-x7W(0v$IKB1$yX=&gB89qjvo8I zsw>zpc4g7krS8vUXpwZu$_idiP`sArh2Z3!K~nb{5HjkdzUdL&nYGBD2DH=w(hCV8|GSO^OKmCnAJiFUC;)wn z^+mrWH6F>1WV8D$lEhq`EgZp1Y`gxsI;(GTZS!e@;eY@jR9@R!MASurjfB`VE)R`R ze2#bBVFYm%hQp#0MarZe_qQ^Qj#jEEH-) zr=Bg0makBp_>w}3+uJ*nll|ppt4x68a=RZugs@!~v?12%cB4#}Y5P#AR*PG&^$@nT zwMBBc*sEK}mCC+sK^*w>WV`%8?C(&%#sv2N$KPz(V;zOkM*(l$mDS z>GmiRCpdMoR5R`oEGZPLknT>F#;Mg=i!3xcRTX~LD~+1P>GS@>dObnxf`G&OsmpPk z5rp!FjRe@>F$Er_vv=|ZFR78V#p^@pg5?Vp#?yS@Q2V$ZZiz>~NsQ7I!6IS07VUl( z-`S}9=V<}17x;knz^jXyJ0)ACS9GD4v(;uZY!?Fz!@@v<)3|~QJvlOz@)2o<(P3XL zfzz%2F^sdb)Z^`zA z!m;^b2a{PVkl(P$L26y82@{PU?(g*1`(l8s{pFkaO8!9f=7|^8aZiMRfx*Y{_xi8_ zp5O&J8P8T1M^mdZd-pHl`h!KPl)mfGTdnY40V&W~a&RTybyt<{pN?zE>Jhg60uz(m zx;br+kSf=hr(^>p+haB;5=%9`TH-T|Cz;5YZ+ozyu8IaE#M#9GoTHA+(NE+6m@71Y zu$aUC+%GiR4zs{CybnM!iTOn$i8*Gb%H&3*6o2n|Q9yj~5s=o!0x!dn=;iW{=7VuET0N zU7oWxkRTf-Pbrn|xz-ocUrLb*I0qpjzkYowDX?FLrn$j@p;TRp0EE1e!UK) zL$CM`ef6niKf{kpQdJjcwZ8J1G$*Q#t6XQT-^T95U^p};OSCqar05N#F)MumR2FI)b=Cq6;X2cq0!VMw z>nkfpxeDpU1DIXOf?T;P@AW#_mb?ltZ zcI0_lvH4Yt0qSC`SXD8SToT_ri2|iVpySySku=0xO*%8QeYYW)v>HXZzCD25V+MYw z3tQ-oB459~I`MWwG8xOu;Cd^Q{eBSHrVco)3GcPNLvWbh4Xn;RiBW*75N+M<%z(CA zYJ~F|6HLcQDA^O;&-fh-*kGg zFJSc%0QJ3n9Q2x&CPy?TvPii?uue(b#EbP!joB>bx{VXezxJ5SODRwRCkBNW#t6{i z8Jw*8@2T&6l3w2d#^>=E0-;a|*r#t?O5I~v0KsLWa$`9JBz09#{K{B-j(amq9HHQ= zlg(jTKcv?am@RzO426Z)KiN zS*2%aV;caS<2=;ni-57yyG=(x2-Dko438FY6ax0QCx4#(J&B^9_|K?*2!bxH!Q({X z?Fzw$C3xy>(IH-281O;&`Rt=HtpM47W{dZ4>;P=sC(o6iU6j6b{y@%J^_?H{$+6~! zNhRdAG9oqvHS$2={p%rzOfWC~nKkH?P=Eksp&Ll`#6tr?i4S;y;1CcMN9yfNaK-eb zpO_-a5;m|#>hgffzm!#oAcZ&M=mbQ`E~6ag<9|&}C4Lmm1eASswV7r{6(R61^DzXF z@k|dkv@urkm|*%-=~{I`6ewo=FZ#7B3Eq^^^Hc84 z)m3~YEvNV+Fh5odkRRWTwLAEA*#9NK%1@gj9%y?wdgODhI-|I7Vq(TnWF}{d$WX!H()`K6@!D$Ouek)d~D@90iGo$ZXztBOIzgtDTv zF__QQLXVl)$NUj8gkHV%qq%)&FlBh^-@>^E5PlXjZXhfF%k^Ed zdqMb9bjrh80j_-=a6RX*dLsWNj|8YFA_2R2%%BJ0l%t}^B}F4iL`-QCe*DW=nm{nj zD%*f-+#|&nG!9X~dH!<$si``{@N|VtRTxS-0Yo-|u_XXMbKLK)!+x`TjHT72!KT-H zpHxjtsaWzmSq{IQQnlLj_S zp?BZ&8CH%61Y?Q}CNk**X+Y#h2lM$@u{atL0A{WO9Ah-#P`*qz>-WcsbbNWSe!SkV zjUTrCDWC(0x_$k$Eky!A#?p`t5NF5p4vQk2Ln*}S?48=bG6mkw5lMt#KY`safu0O1 z#z29E1aK4dCKFVK4-dB|l1&S>R=OOaC3d?LGong0=IW0@n<33k9|(qzX$$rHN3z~> zz0u(y6+ny%K*f$62qUooM(6%sfXT_0<8mDSaRf zV3Xuu7f)f6SLKtw0HOwOOz(@Q+JJcZgOY!VuKoxn2UyTX@l^I{C)=4yJm zM5ES{hwM{w9;_mO=+{<$1P?VTd1>teq@*oSplQC^z`a`bC(ye*T%J*>vENhKo~3vALMo^AQ@wQYjMwp>c9^uHKGv&iXMU zp;oPasf540gP^?==xdY%DE5S3zR2fK)Aj`E@V6oZSUw{&rUanQkXoL>p$G%iH3^VA zKBfh|0PXciJ(qEP_@ETr6oSpr7pLi-Pwm`Q{1OHole5;9hzBnehe;G5;=Xm?@TBv2 zwi6-H!7Ius!A5BD_@u%;z-D$Z&PV&A z#?uioa|cR+hesF+U>PR$r6*}BP!*2!WKuC^H$l>VF%U4@`u1W= z%jwRz*n#&Yy*GWP^<$MxT}pygzb~4u&|IBfC13TZ-15m*v!GcDQZlV`0i5Jal`X*i zjWoNth{V6ouz&W$YGBcyBIB}E;-&6G(ObKnF?!1-Zo^c7okxV3j|VOL$^n&oR_nC@ z{NR8+Ao1_drFz zL{oH8B(mtbECC;O4AfzEkG!T(gNe}3wM~gY1XNZfx*!0+Q!^M0gkz9@>KQLoT>tS} zGdhg(S~$(*u9Vjg(HUbu7hXvhW)Wg3E+@mk-a2jvB-{1HhQq1jXo^`f_O>TFAvqs& z&jZXG9PyvJyjpZ_uyhn)^Mfc#bCBDm=0Nyw!ew)WQiFx1&F>J}a>eBi1~s9?4niFi)!w|?>j>q(Avvpx03ka`ffcxX&GiDrDG27|P4_(Qg9C)ulGd^J}>%C>9cu&fJ-SuE-}C zn_G?4U?E?liA`jRABxBM{Q@0(jcQVXHr>-D!MZT-&9ONG>PVwsMaa^2#=m=7zJ@>( zka5?@5HvfQvOV3JDS@R3CzQQ+AEf$uDn|e>2l{+5ZSlm835Yz6a>(dZ@(Hy^%elQ^ z9^Gkg8dr-1X*t%Dq3D*V(1Mh9sEXQRg(0#H#4 zIRGtBn1HM=`>`KKG@3%&rk5)8sZ;z-3Ps#|cXMHvVG9UbT+NML%lvxqzZe3dg|JjJ zer^9zi>GY$uayO1cp87rh}8fQ79Aa3$0CD@x(jqEg{JFE^;dyhCj}}^S?3PD3>p~j zE)2zoAs&>aFlJNH5n^NykdXI+P>V-EbylYKX4X&N1F%MzFHi@o&^Gzg(m-yZM8aJ& zqU1YV@c)A$k)#$t??(&|B>$~oX)-{0(t?;r2muQ9&SyIT3`a&q;3YnRA}zz5#~}bU z4iaB-o5PHz@_l?;UbM3M-#{d#V5D2jQ~I9idVAC@lfmm3|GEqP>0xO73IgElS4$sn z?++z|UtN_M|1vjjE%erSzTMfq*6db|$1;ki3&MMq{6W`3h2ouL^3IJ+Wi@2@!wjP5 zhmP+aXE-7N7?ZXRZ2BA+tBIynDbrG){99ZP{(Yai9t9ohUt?&Y7rL7+X3F#dsqgs! z)O)w7QcnK&TaqkIg0RB7N@;070-Zohu#aMG1OrAKj7eQyCx>YW2hAxr#S@|b$k=oL z25VK0Of3(o09w0E^BmVt1CVX=0#KogtXXgt`#e!({J-x#pvv=OWOfe1IAFMWy#zXZ6Nbw|vzd;3cmUlX3A|AQ$dTY;-lRJgQ0FCz zmt*s&T=n0~69f$Zo7GynOMD&+Uj4--=ee;m@3Ec)wlNAa6{fM=02TsFNf%m31MiBv zb?Zf|Y744Sl4=w^aX||-Sp$3s7TwA{fyb@3q7_v)84Li4FRs*U5XgcFp;<7d+%DVi zq@yMl7p-nXPbx;sL+|F?d%6^zLK185?^B6i&)u5o9Fc&Ty#U0tFdRV)r?z=&2xRC( zAwWmON!{H$nP$~l3+fiY?mltO9bDc!M*5ua&erO<2^v(&B;`0{34YB6Ldr;)Hs61J z0vHRV#hU#6wXF9JLlnv=&sJ$Yn@&N{XYXkbS{|Ay zBDMl|l;9Aic)1a5q-p6*-}b8nFsZzq7cJH^RWhuU!G)B=KWbVE6y27FC)_H+-9+*l zNaalOF%T3Vk5ou2z%R@#=mHK1+X{$t1V{2Ub%>a+U%)u#Ns*lxCHKk#!*qP0^awi% zPqhQCZ^p&UZ(gD9g}nMf8)uifL6O?`n*5Y&$$1@3HTc^qoutaVSjrLeoW`&Qs5LUcL|f53 z#onrk%_nRr?0l-HO8UzWK~zAM>){8~SwDu-Bs0bGxirT-@6S(ZbEQC#uR} z1P+vUgkQL)5CS(l#o+2~Yw`Qj^A6oO8ue&vC-mNp7Y}SFOSdAcUBbK9Kp)QGP>cIr zeYE^0K5MPwCN5`QjT~*N2Fm_CDIiar-2+Y7q3bA_?EPu)Vm~e|N1;0sel@BSbBTJ! zE|2uf!P!J^y^)q686Q6rOtzyLTQZe=_`zuf4aQgyGWq-F9iBu@nn|`N% zkwmEE7A^IF;%DV%Gp4e36@iys_&3^_w~{2n*~fl8&GWNs>z}@osDBw4`QBZ)V>ET= z@*zQJ0Fc|_d7blK0kTV$e{`{$&XB%>o0KTag(*4GKk(?& zO&C}`_KWPH@9a+-thd`?oo?1cDFiv4N?Y(aD<$2p&vjYdf0uE3%zk^wR=la-nbUGQ zs2Z|fZg*0joOdBSzKho$`KE#3bmf<;KigG>y2EH@H5PUbA(f0#kjef;`F1orj+}BV zd?kl6pABWZd?=MQXV&vPqey+0W&6%VktPz&;w)0qzqw%dU_PT6Z?2_Vy2V=e&BeN~ zu?lq>=lOKN4|J>|P`N#!>>LAo&ODCo~w+e@k^WW~(6OAz8qN~Q% z4n{HyOjT+`<0?#U1f6zs&gqXDevN#$Vjb^7Ilie(JEJ?@4cq22DM2|$y}|v(Tp}Td zaj+^)+I72(RwhW<1hfpcb{GLbI-@)c$V$L|oSC4)kB|Ig>Ci-zi-Slid)@E3I@w2} z707PCZk-B+y4~#60NGuk&^fJUv)ZIj{QxaO)e z!bb_Rup2d;ntdwLe)+gM1~Mb*of&ttPPrVQ7N|f&A?>VEW!URIUB550)7ap^r>8X> zYkd`oo=jxG!o(gDpho93_QY zh9oHq4S27XlD1{)Wz3ue5VinXqra+9QUB;Rf!?t1gVQ%R)ZRqmFcpH*-R?8)S$f0% zH}_{wmK;Tsl8OTl!mYswOFxcRW!ilaU~Pu=hmxm{Rs;eYUQsFtM-U0<0o6^JBBk%$ zuh4Q<{ZZaT0^DpbK+NmU)mj~oD{A%{rnzC}(CZ{R4aC#QlxlELivq&i)KRk zUB;N_gK1HGaOZjLZ=5X6n|Vpk1n-G*NO-(EL!a$jdN1ZuOb%0x+?=82DCLWeoQ$KD zJI#xFf;Xj7+`( z@pN&K&BvOeC}_Se;of7WY_UZL68Rn5a`Sba+zR6~7OqGl(s#m(JZU}0ek~?Q%Lk&_ zOb0*3605hrT`P*D?p}QvkcBz>R*p}U!eyg^_>z*iqa~hby>J3`ez85rHWfyozbE#r zFvgRU%!lh*1Tn4%LVN12O2BuwggR%#J94nd}n4)nsjm2qofS~ueUBOPj!&xSj!fT;)#1k$^EUU zF1Z_nRD`T1nsg|{3|YDW{fsbwle9^*hHsC{-mxW_)lU2~rU)5R5J^nvS3Dwg%+__J zP2n~j{gib~Dh-7|5~UA(HMr}<64xdT;;Po%?l*=tjdUv?S!*qFt0DOEc95!KMWxR# zKfPlLCW}Ja9HuB-Ha5%Xk0}iR{cz)rOJb8COK}(uEw;XFr7_cIJk#$$q78sJTl?vsa_7n=5|r2)R8lCg^9ip zW$T?2n{~=>O3(WXNKf?7%CBZ3?U!s=SHiij7V^s(WYyHa16QJY( zdSDv?BZy?N(70L&rUgO6YghTyssI&8Ou5Id)RL2`NsZCiPM{+Hw*P_DY#SgxlcOo5 zdB2G#Wcuj=1qs`QMfV#C&ihNC zI$R(<)|nrv)yG!GnF2Vpo4bp}U1p+8cYyoXTWa;H$Eo#m2ikUzuXjs#t25j>{81;n ze|`hVsXlL5Bof`O5PB`oj!~%0BcP6T+=Xkg<$A~~4p94zmn*?4b1SS^-W5PoFuljU zYh3|5suV3hsjp45>y@pGIM=T)>`uq=5;VGAp5sxB_yA(l@Otynehb&TDW|+L9vw*g z#1W2N(GS_7{j2&2C!#qHd7iavbRh&$f+bz-&0|;e3-cR8UEwaw+9ca`PbFIafqRO*Edw8CTFujo!L2MWmqSK zx;MUATfI0IMJ11;A0wXKpZ_a8M*Pk=1l`dkI~+YmxP@kp609Taf4&!qoKVsGg-NWk zA>Vj0!l!Q~AH9oqLw;gtlst@Wm#ZkVB(_9JjHZAt;?-xy=&ruZTDMW0tL^^f{{ZArrEPsGd^b36OB+z3# z?sTcy3DRo&5&WV7qofWk0H7xlXqwk9dH(a|9N?=vbAp4Hl|3}U(FGKvzJN&w);SmW6kPkSLS;5h2G!1464}hL`;k&JB>hU5s`qHBHF;B(H`wgn61dyjDcJ=v0v?6W0yPw zN0$f3u+AUD(0>>e`iysQ$`}0-zIX-mJyS>P8e=+#Mrsnj?%{CuGrk6q!*iPm?lpK4 zi5*AmRh%i<{g|@&1dyUgl-65@{Wt?n$9}h)^WI-sLgQj_5b7@wmgatzvUf>u(*_deEA>;q^>gao~~2(Tifb*p$lkM0lXO zolZ7-(>(6;sRQl92FGHqmp%4zY>Wg1YGf#1;7%~;_hsWl;jmtikBaA;4*M+K1U0Un#G**z`fdC$_Fw?h|ENR*ljE|OBg>xx#R*>6XrTYWJa08( zUKnUxsV3ufUGe$u6u^miAv)&rdy39by20VVzQn)Eo6i4o$*BUciy6C@nBoEQBq1Le z&srmmx?pw0vqiq4gsA(*Gw6W3t&rOc-ot&~8OyJ#^}&4$nn$8-Oz&1ov@Q}05G66u zT#^6^J)|bgn`~WVyjZIpM_)cseXrJWJNnYl~TsQ6%EgV|85b_L$EWrDEGg$x%dhDicVQgQDy2`Z2|$ z68hd<|GI>=7F7l@81!bOw<0btSwGx!usWTF?^`6EDn}dH0o8miyW4=d1{YQOe4|XV zmfho-nBYhfWl^`=g3uvnwYrMuh0R)=U58f}yHXN3)jMQ%I5lDK(@fl^7Rt;@D~l6U zA1SK*(iaD-5$&Dgzeqg4TY}$}MYpxX7GnCoQsYJQ!{3hub7HahGkzYU*u1O<8itId zVK2n^Y5j7*v2?XzI>-cSG=#O_@s~8Y@B`jf!4#+^sbx_2Kv-1?A1_jtMJfP_Vg5S^ zog{NA%dDQPFD1Oi-cluEJ;416D8D(~&gw>B{yZjJZ9e}FNX{Z2`^B3H(-FE}XL62( z*=vQY0L_wByOiE@>?Z>ZBS2=Mj(@!f5LJIQk+}y*f*N?DWkQm~W8TG-Jd<@#rv^$> zQdAPmO)eV}i={6{H#M$QDX&JIs2Z4-=Z4L3F1pE* zwea>0tgL!3ck>ICWZgWGP^@guBfVPuYATJ+C+;s8jK#;4Jgo@c&T0h^~hmOt&8eq0L4a%Cpzm}Xif_&J(QmZaG8_~qck zVC?xJQNv8ee~&hTqslBp)|o+pmlzA~$$@wG0YL{sfO6+*Vh|?q|L({wbCkiVA3^Y# zoo@#-bxRv`-ZV~s@j%OT2K^Ep%@)amdo6UPw&6ekeG9)t)_qWagH@r29zX@nUe+pr zu&Q*}HfwG&65HW-lP&C}_L&8Lj_O+>)I6uPWzFyD^RzM3sRc?$yqOwx=Av8Gy0Pl9 z2^ll%G1S?J77bPV-%<8eL>8=-?n1SnVCYDpf-${IsMKoT%So3xYTx1&`Abp^Kl_N$ z^*{)Z)c3{j7YV4=y9(`sFQ7J_q&Av&O-GHe$XU1y(H#JZD%sjI!2tC8f}S2w8)WlS zQ`}hlRl22+-|X8WPjH1cDZen2U7Ki7vsMvVsz0%)rSaZ;Lyhgu*xO7~_6=wlcwybt zh}ArQgA>k?X8>#P3jQY8KNZMJZafU9`5qat03v91tYvDt1>TNvTrM80S#LNZ_JO74 zwc-n*{^|;mY)kh5J~9u|GOJx0*0K#nr+hJ7o=QG4^g|M_!_Exy*`@sYkPn?|!3*Kw z?N<{r;ug@-pNk?EellBFJfrdtjxoufN1z3TkRac@@|EbV`{0-6=sN0HtdQ z)<*9JVPXzWL}7kZr|}WoNJyw20>UDzmIyP)qG~my9+FlgSwWB+6== zA3mf+u2upjj2udF8aOT-oLTZf zQb$7{52mEhgCF1fEp)ByvV{Hp?G<0$o3^HxtL`c_pI-=#PzbdkOOv;~?plmlJb0=^3NIgTlSSA6N~R&d!n-?*jg&i}-Usw{R3nrAc_SQcB<2s&qv*#fj>V~&K^ z0?ICfHGnKRSVk)tf4025U+{442D&sBOX+Vmkw6ecI%)a^_jVQE-Y~Q_8mHqmw)i&S zOJX2h6!NWWojC$X+w^4_xuWn{%C#YkV?^5Z$j`YjK?DWbOI}2w-%2VBBO)ku` zSP19VPp?I3-pjWc7L`<8tZ#Xx&>z;lqqo{tmX|#3^O$O#;_w)5em2rU1GF&7jyTPQErC`+a2{w$W@kU0k;e-$?(Xj|DI6ku0bd9bUTk)=TS@>5 zbS5Tf-gpo-HebSxk)4U+ByKED@p#4sZCGUKZ`?}Hnha;@;`XMtXV-!gKE?#7<`Tt! zi=jKPeWi4!GqX^8G*;U`yeS+chfQ$$(e|OOr@_(JTvPAn;93I*4F_!x+(rw&1Eb-m zx$LvH@z0Ybcp?}i{nZH!PXe=#`9(_vAZc5q0~D@-MzUG8cfl1tkBBC{hh7-+|7beL zusr`Zj%VWz%eJ}YmRt7fZduE=ZM)^Rux#6Qt7X@+t>@~09M8KQM{ib_&hv}U&jALM zY1u#nCo&zQ4W`^uun!}ul1#P(uEnqzm$TVM14EPyAB%T8tQ-6!-X%Wj_S!PtD; z(XC6vStt&J<7Ec2N#`G*U&ZP_4=r}~KDmx;oI3CPWH23Z?cJFvxN3PT#T`!((XklW zAaOQ%(Ca)HK~1GDso>W8P4lw+nO@ZwkGk<}W&S3QsLp7^F?r{H(_43V+M~Vv2ru>{ zkO3fOf45iz+}AhZrAkot3T+9rJ zE^OVIU#bQLGc2v^c(E4t>N`2r6R?1qyr(EE7g%2ILeEd;>zoc$vd)%mJL^wfDzwUt z?y}VT;Ej-s>lfz~Itcr_YaOKWlq0CFB`Y4fe33!xyn zT`$4rQw_1#1kdfyt;P*wKiIyF_k*Qg(=Y4O$(}-Tecza0bhZW+$rPz%7+Se2)s?kv zM}A;^*^PANm+knuo3EZYQDe2n+G4Y_Ubjd3V53G_JhP$c-myMdi9&0iH7^jQ6u>_QO#zp_96 z-&;TnWzcR3CHXxQ<@O0ut}4nt2-wc&u1pP}OymMGfp|N@vLH}Zu!|Mr5#Dh#;y5{6 zq${d**L3`l5c49WynK%ek}ibNTsKIZ^D+>*Ty8v*KKZUw@Hm_0^g2MS8^0*zOQl$U z@T<|)N}F6_bS~jj)(=UmE&EDYcaQa>9OlOvOG{05w>-s{YM#Z# z;*=_hF_j~2ddUdZvl_OI-u7u~CCiePy^n{^^a=8!Fj`GSozIt}mq4#sHW=x{H8O%6 z1KKFXe~CI6BuG~|qsagl>vxAdFNbtZJ{|8LE2g6??)LoH#ykrTQc(_XjAndGaQfjeav|1QAYK6H zAwX`?e z%Tv}5Bf;G9-W2igs4PPW#(*_SOn!@(cd>?R=Kxx*d$y)lDC0V^4kUn8Yz@<>{TZZc;){o{qd1 zDs>Y^_Z9iCM1?J6x zTSkG1h8qS(Id&osr#mU+ky!9ueGD@NwbpmLnFa2m zY@fDSyT5hx{`u7AQe0SyJ-B#^`ITT)=YxdP5b|!p+3e!(?TMC3iG`}SfP#_$y;f_% zcgGTEY*%=zrSGnFS6&ye~9 zDiLD925_+%+4v`Q6aA1OErrv04&iyy&(5@Eb_;3z8V~K<-M6E;rT$Azcx8719ykp8 zdbXTrjCNAa-dTK|1yK&t$K&%LHQDEH4~H&8FP;vkI~d`(LLK>XiJDt5yaSYy4MO{h zr14D*3FZ-VGD+JSPmt(V+C1KBk&|^l9HrwJ7CZ!Mb~7#oevm5b=9H#t0Be)^(E{@9 z@2*ws99%swq;Jrn2yyEE?uP@z=+qs5F9`r$0Hdv9B*C$+M^WW#6_4xD zz8lh}It8*OIwyu2qiHn3noNu#UubR68@T)BI4Mh3;mekFUX ze~3PQzi~2|FL9yvqBAS6;b&C=2S$|5)lwYs#bHnoy+_1d+=9Hnz}F#6L4ZI!E5>RB z=;^<65WZ1uHR8|rYUp`?1-v4~_U^yMKwUjB>qbQnOzi>x%(JW0l2j}5U) z0`iKT-JR6`w}6_P-q9k3FEtzB#}RM9D&w%T+yWzXODRyan%y=)!ewC!3s?beg_pR< zKtK_m!3r=PiWAW~iIRZX--3&M;jsk@3sA`VKnBbNh_WgKv9@0)c-=0VPFOD_1Ji`; zWVYbVDWCuynYcNBo6G`~A~e)kdp$s!O#*hrqh4CY?76F%JRKmUiuwoV=pk=DnXT{d zN1x|Zk>5ur1JQuSY>7JyD8)yetYLlr(D0vc`$Gvc`hLF0et=ZK2HHuCar7w0B=Ltd z4&Vtbw;ciot2b&tc4GgJlXELy;Z4>K17AHw7CCAC*4_HbmUkky?R;sdp#8}= z?hYhAl1;PSC4HVwxCJFK?t%aShk~N>G?3Jl1{4^%OdjQhNFR|pLp`@)FxnpkKF**R z+5Vn&w;GZ{En60%Nw_9WpQ7*h&vnUxALUS zVWnZ^9(NqY^yLR$S~%d#xQ$_dvq9Fj^8k`xVMDoioXOO(IQ}57-dH>!N(}*-aOWqsJxrrc5b=2^sm~O}G?-c* zKQE<!U^Dpm$nqM^zq+M;~F+t;@Vl-j}2j{YVfW zM$euG6$rt^h9ZT9w~*fMB>;J(y&>YVVn(V#?KAaop$8Y+`nqPfQW1(|sTXAejs81^ zT{)&;(M2a+pyFwqE7p@#=;+2}@DWI5*X8!!VUZ$8M1}T<$-b8%*~Htc>V70qNn@U* zATG$u(Xs;(VMi&&Pmsv@(t;k>Q$YXUf4R{XcT58TdCPH9kQT7K64!m-bhOYmU(H`O z1S}(dD|AFz_XJuQ&ZO06z=z}8-*m^`Z(C=h?AaIH zGtXJY5KJDOOMCWFcV0sP1o@@c%gutr`G!d1YO~ec?|4UIMvOn^9PgbxfKBsrRUgQB6f+ClSNnq` zL_5g{;>pxVabdr{cnG9bo^`LM)x;H(Vn2scklbrHEEl=+R2b*K4celv*=Ug(Qg}PS z;5+V)Py<+nU>E;^Yg~0%3f&&Icwr|*fhH}9JE!b@Bbal}-P4`-xX3cmq=lrDpxHvh zR{?!u+adX_hO;ZSg_(@h`)qQu=&Te6fg-~IbKFiYo9#0~yJrLQ?NpD!7~&=BIqdKa zCZGo}Z*<+_a#9kEwPzg2NUNPyq22tmJmTGZUEULDGQ|Pvr|{MOWPyzI+KTZ&h=@k$ z*DzGkf>JBA+gUU!ND$80?%?SlR|ZdTLgA=G zy1QN;0agu8)M|g4xST&^Dxd}>rdw%0B0_@oS4@JdDGsj=43B6QUXr?0cLNJVg7tGw zvTiy`fX(WmiwNgK+lyoXT^*8y%@b~HRYg1ZN$rzFSazwL7FsfBCS3My=X~RGz;4qb zS`0=-z^P{>Bvd6ID5eepHM078bPib?>+ge$HS z3?WhV1AzY=2$KTskNJ~X{a#6IQ-YF83=RWy(?UTs*<)&mJ5s5)+YX1tS^;^-aOg$4rj@_YNC~Yo1(NS zkOZX{_zzf9(h!MzC5KwJj^Ete6*LfqbOgPwncT7xz>uX-JQ9ld;el6eR$B@vUq!6| zWknR!t$pr*DxwR5#jZ%hV?=121tfB5JG-j2kfFf1z&{xCEkMlO!vA|8ae5Z8(XD!f z^$t;iAu-0_5LF??)r#oRt;O{~t8-y0M1eP0TX;B9FbmRvuF4xMiy4Lqc?2z&q!1 z3qQX4hAYgm&+jg1p*{Ux`86uXtNn&Rze?bypU#pE6A2Tr4ES?4&b>Zo1hWYh@h=38CV*t*euZnaTHLCmO8-}=es?sK)Gq*(xPkuGMOoKQCk$I30vxoSf*oT& z{BY6F2X(&cgK z5%o2QpKfL4XU2wP#6>9H-Jm$22MIy}@q=VQ^*K7w2dN>l8nB&2;`3p(tGKI^fu=gc z4}}J8w*w0*txMsFI{$?dHf_5rwYOeMIV|vKqO@}i12>GzH#7_vf-PiGM2zb-w{6s7 z?dBSiQX{?(iv{MXnv%C|VVC58p$1lk^T`deIhq^|JOSf$sht6Ex%t?{#CL<7QLZu! zbZMGA@o}rGF70zn5jN7-#E9H!Fzuir!C2$Zq3p~gIPY?#GgQ<55;2&GPwss?f)=v= zTP31}Sw1a)L6Hy+qm8)IYMT9lXfgz?NM>iNAu!q2v8fVfKXA}zLDu%Ep%sYe5)uGG zxp`=pitP|INSs;fr6BfCcFi)0G|@N)f8k|`M%LThYk?QHgyP!DJ5i>c^Tts0dzh5m zaTFP9S|M>-lLWiH& z0HlmyfI0*;ShAf6PWm3`;N1abKz0?Uj?gF-Gz@aS0lKZA-Kp!zm;#$L+_%urOD{Sa z5)5m3%qAvh@VE+1o?Xny6vu#kw*WY5HenJ7a8%$O zK9^%UU@HBSs01W!q(e2iPlD~l|2kVy=0e%Lf;<3wv-!Oz+z3AiB7?$`HUhQMKLM&% z0Q%m2IxwY9#IlQ<;VuQ%D5DK*&6R2>+EV7ikYEB|6;=16^2WNMUP2jhYVjU@H<)xY zpj1SH9XoY{m-`6d^A3j#rb)qHHv=O0*fu`~h>$%C_Av+O=+{`!cBZj7|MJ=pY9(bj zzv?$*}b zEwIl#-YvR*n@I79@N*~Wn(-QQ45+kvlHoTBJc8!kjIYER7-^x@9CU15EypJIyJtiE zIgCJi9C^q!aNT_gXAgW!S%|06r!Uw@Fn%K125ihjlEe7J-l@VE(P=f5;mG_(cMLQ5 zX;!w)ELdjsP87frHL`0q&oR$%CbS+9BkrR*6ftvort3-wV~+v=1E_zBfEwL4wy7u^ zLQ{W_VHFTY(yTTN_E`o12ol86cEGfmjtfIwmZ2zklY}M!Icikpz$pe`w#$NO6*8xs zw+t|a6!6$$uilO2D+4Q-05N@%kH?&>P&0+{rMw~^4Og$xX@7h^f}&`)zJy!Cti;LW zhN-tFT@(Ks-e>)`vDuW~@Qj{%1$=xCCpjkteic=ltu$3Lo793Z5wM}=;nRcSUVjUD_%sx$O~NsX_hU(%?4V+nkbT%9H{q<%#%mC+ zR2W@E_&=fN^Iim6-&oCPfee`ER0xu-laAQd40WLD1#ISw6BZjDfNrAkG8XV4@+%!= z3OPxE5M=bF=L%a_TN7(ft6w`GxRDx8Z(E%OpnJd@hzly3*k!&gug+AwYMS;TaEgmY|^g#R-E7Je5J zG2JNMK3bg?C%!0wl`oq_9|z1)EK`F(fZrX$}WgWHth18m(BD1Kl|&|#Sn zS*=R?sR+vY8-gI-Y4EkLVK~$|YPt_Bri%V31W-S$!}~7Me4I>lUvv0L7=-cfoJ3Tw8YzVZ(! z9s-OQwP_>rixgBGky(v7+xc-!b-^}?pk+o(ZC!GQ>CfWW0*-r8Xa;X1jFtZ!FVxYu z82~70@zu6alxklco)n%hB6%dYz7#@zbW5CE7N`tiJ`jBg{ce^-&?9KX=u*=?JZK>u5DYYISRv)GOh@Vl#&jQNRT1oLG{VWCXW8TgU{h7K zwl6MWx4kd!qEZ#?EKPwXau`13ieouC8$08;qv^H@+ZMV0H%6EcF%JJC&`=>Tjy&QS zLcb$?NC#_?A0r&uCMg2unrWU3w_)diEqV8y3797q%2!J!q{~9tZ3LMj*(W3HUWF7; zAnoQKwNK(jhGI`D)-#1gg`(UYY1)<)QYEn*N5O#0TFO2KpQ5_*wjuFhNZONN)$_hh zzk!%U3iN!KhD*xbSx}hOF=I2U>)J@VwSGyvJ739XSlPsH-l*cbW@Qguvle8yL~f~s zfxx_y32Mi>3J{SeOjQ<06S>}GCjJ?@#q0y99nLqPLfJ5pI56a*gAl@pBy6`Wo{2nea?+nIZPs7GQ!U)E;`3)eC#JL3F z5V5qjzrcuo*hWwmT9IJqLgcnP787im78UMzxm|wrdY%H>JEF+)I%$uSbCDhTP}bM* zh}xiIxSw-7)9}yt@75+Uf9FvX#)f=?{Wk>5@Wz_=Y!7p`ujYPhao=ncyV%Y+iM;V% zNg}3@$WMgH!AV!}uB5Z?DHOHwc~R%T+tc;-uRix)VQLlS1A|Ly9rT0ZMj_#fwriO3iP0v7aiIOPz7mUe8joD6F4a4huSw4FC7>xv`J zd#vUYA3!5S>tkyA8n zWdnAEV~Y{{IBkCiuM!)n!Zf@D;?K)+eXEpyeB)+Es#6m}NRUG>r zLn~>IyuS*$w#bs{m%xJO&(+7M=3sG!77BC*?TrEl-? z5p0#?4v?qAT!eXtMMY!?Lbd10C2Eg;THYicjD=lMzu-dOO@Btn56s}d)m)vjBjWZ# zkN|=ZC2p;u7s3B(dDor89}PbBqeIx-|4Y2#3tITBsvti2E;UrL`<%CZ$|D^pvHU`~ zU%^M`sj!luy~xQBbEsQkgJQtEez*t*g4lG{tsSQV9nLQf?zNtj4?Yt((y8nj$BP{b zK6rvsF(gZj1tSXy70@BFGdsm5dwtao+e=@86Pap{S8CK{G|HHD^d5v0sN`uv@6Hk` zzo5po^K)5r|0#I0qgVUcW;0lqBRRTWOEXlSu~4wanU(Lnui?}|zx_D{SC@pw_RfNW zHkLRk;uGQM(J!9_-_-F$(!mZ!Q$> zMQ!(cX2UwUX%+j+=z58Aiw@G;4T703oy9A0-_(bxesw&}E|N5pQk0jPrtW*7$3yjx z4BHBMQ9UHtX$zcbJb2ASupPlqVpM)+^bYl+=XJoH+Vy=q$VlK4Q(t|%GF3eAAzo<} zd%e35=5;-6QY&82rg^hXVsLDR*=NRN(Xi}%x6RgUqg@)-Z}?v8odTl{G+yE>QBE!K zzJ!I6Tm>MZyS-PkPbu~9yjQ%Ly}=wly#~=@UQpl_wle6>PQ3k4+)hvsK;WI~9K`zu zg@NFz(5L7}4FF~l#Y})ES2Zj;tsB>7Ft<1%rY_I+9WiUlI9K)uedVBjbc|(oZCJz< zkvjNlf^$QGGIm!`LYSu??kvcuP}_~9n*!0EK)3l^JpeB#6es&@ToU>svT=ekrR+*MQUo5*Ou*+8Ef9*N%0ZKI+&EFC@ZI$tPF26K89JHYjPwA#QJ`edk z?D|p`h%XXwY_!{&EQfr(yt4}H(Bf!v<#ZR-B!-X8rl3yF1whcq&33O*$0`&KdqJ%E zXZ(7@SKjNX2SVO7t&J6_rKzkVxAK-$5+zP5Mbcl|t)|WWFn{oyn7uWq)M%r+pGE#~ zP!w$QIv+rOJiA%>fpvN0N;p;@R(NtS%L!>C}#-bFT8= zq;4pQUD4K}+660d*;Svl?J*a=dtb*gn}!z^*$yse0`lwYLlaeL|eX z0eVfJGCan}M|$CJ41c49tdohr^hAWG4&Un&Frl2W)0a75KtqY*Ow!VjpF$3NgtPGA z*_-O4LpY`uuofLeIJYK|S#>)uJqh1OZJc!c=LSX-+ym($g~gwLUh^Wtg|#W%tS5eI zZ+cZ)Rf(u7S3;#HL)5IXJA)|r!zRYkyEH7hnWH#wv zmk}<3KGI-K5D!m0K%V_vV^ z$FkpIKJXFeuwjQ4?dUf!0Hmmcg`YaEqLV78#amG9qAI)b0@<1w>`^N!UL34DflaXg zFL6^Tr3Kqc+r|7T-~i5_es!bo1tcDT=>(#6SsxUfy$r422<8NctzifXp%0N5-FWjo z_?+K)I4LS11oGqp_!$Z>MMb@!qPT;k`Ua^4Jkx@@IObs(v!&maSk}mKE^#7(q;rut zp)ri;t4;Udu)V(nQy;uGrj4^qnadnDeJl!z#E6?rf3>f1-4eWXu~8~g9i2tA&hJk? zNbr4PDe&c#Cw?Oo1P?-jTeotQp5Ej4#1}{oUkt6sJhp+iqF6^1QOYzo#6ltrsd>{eCch!fjn! z`$9LmoCs_PQ`QKm`-CF(n=Pm;ij61KLSxJ2LOE}bmDo(mA~ZgNYVG<>th6yCbE1{* z!5=T>n$<2>ms@r7B}mn_4O_js z-(zvq3xThpdcF??ileVO89QUeMl#rMET87UV&PaCss4No6?*hPY8gvCBj#GxteS~M zvZu0@v`2+TzTU|;*XOoZoHW|}B56`Juwo_IVOtx1EF>EI4=+nb0R>X4>LNGfF@%2i z#g=ri#5CeSV`!bqH2+YtsXkLE)tp>#Bc64JeG{Y9@ZdI*Yho`Ivi9Rwym!M-ra)b!#vHhE0Az< zqq4;cZBZt9oD^VfaxV6nI$ag(vw5C~^mWPgdk&Z#7*WA-kO7z+Rt2AtBQZ7qRIPqv+*0u+Rhl(ogGo8sOyirqD9s zD>}^Mat?Dx9K>bVo7{SqAkEN!9;|6sJ7ET_H%4^Dso0PbT?#Pgt>{jM?EJf*+5l3h zr3>N_k!YUS3Te-mIAgdAUoxuSVqT>4BH}YV#ztS-D40CM(1V`nhst8Zq#5b-hr}cD z9hC$&IU=|=2rQ^~Xr%xSZKfy_Ip!!{fT0C-P_EwwIi!Y%v1+I&QqK;LVFjxD7GMbD zH1$vzpc#^GrX!V_8&W@WUv7^kLMZf)$CRVgU#w4PDw-+~D3s#(NZEQfrJpiu=)9ik zYwGe`Sw9+1qmT%#Xt1~|f+bB2UN-8HmWlVLyv^Gz; z5pZ}k48=a`Relq0)I6wk@HLt+VheamV*1ue7WzC`Lp1NB+v$?^T^?TqTGEH?Z5Eej z#pIfCa%hM366V^&o#+FLZUk-p{?tY96Hoqbl>leEE(xncYa_A%ytB3V_>%LsUAnYd zy$vVYcC)ac9ueiq>0-wZn&@L$N*-ex9k`wADswMpQsz*e~#oI^{hJ>Y`0qd<# z5G(w96__8vIS(Zux6fbb`?CSBxDJ@U8(m%eoIS{6BXHOGTp*QO))8vGRm}Dy@AJ&OT&c#pUvs5jqdxI_`{ z{k6)={7FnT+WW?ou(H}Req`mK06TTW`kCA)eKr$)18c(7F5-%KW&Jm^qQ0jMZb%XF z+pEIt0Lq>Ny6W&uve|CYti&R0M;cgRB5}<8XIoJek&9KL6{r1bkeCA^t&r?1A zR?27(s6qH5O>%sV6ZJP^>}!9}CJL-#>?ddPN9pWdhEv*Wz8)%Heesxnd#uZ!K)swk z?4K>Z^DVl_^g0lbd zN7Q#1tNrq^bl=?aS}2s*Su9zsVx~pe)g1l3Zs@x}Yh5>jt2 zsC|aTWzA3hk^nolW|YN7Eg_7d1Ow#{Q4>v>jUPnitTcqujr7mbijyg#O0-1_3YC?r zX%r|C@?N_m`@93_+{6{z+Ak!8fFpN~YQT>x`Y?6c#^;4jJ^tPAJOCiygWjm4BH)uoYl88*=ddM(#QM=xKdo?tZSf@UHd!o5?g zH7#+XzE$=!|7qYsY^}zv&$0<`-GC!}|9~xAlppd_wk`{t41Z-H_X<~4@T8#b&ouME z8Hj~&fEwH{{h2O!!$1GScdn`HL2t_w(%Wid*oJ#N6V|+Q|1$N56_dN&mLk4*541{E zcf?E5BGq00PK2{9HjDj&MqIJpgw!nUjiW^e+@G-vIdK8M*NYe4e)s>Hw3nR4N@BFd zs2;(;8)b0*rgX$|oZ@&J#7P30M7%PadZY(#B4Sof`!ENGtR#Wlt_PGK4pV>KJ8NVI zNL+pmQLuvR^jaKr(k~a)uKvRoJhXgyfqO2D zpf{UiJgl1Ms8;mwYvRg__<8(hhhD{Ty=q=vO|bEZdPKd*yz~~yH!!sE)?QF`Ufbpw ztbD}xx4(0`cePkDu;Ez7L16_9`$*RYyPy!oXq3uGx;--N=GS;V8tHX<7fTo+X}8^D z#n%AFCa8Uox~a%Vpo8ib>EiQ4REXSMM_Sp6mbD=BG{RjM9>HU1GcF_RIrpVOc#9VG z=jS2{Rn+cT98;b3qdlBTSi~xpH{JJr$ZKC-v=~b4q#HWfA5v9vAQ@uM8A;A@gbdS` z7H@vOtIR3p@uc#%`)qRPROd0zLoo9zoX&s;J$Ov>O+hG%sE|0xF&fbgeV={4FG0PS z_ZV)_9XT~g1$QFrgIVSSnj1$d>%a<&^2N+o@y~L23)V~ise_LJNOmM}J5s+@8Xt5n zrNu5y(W|5Cl$Uu(_>0!FGEZK)Ec2HZV@^`U1R&U`w~dlW9gjQepZJ)Z5e7m$@n)uf zd5?{>12Jkt48J4jfFcb2Xjnsxu;?yJI$t}n{+?=tv4NIoDWlk%moDhY=|F(aN6A7+ zceiMoBh8ziKG0JcMlQ@jSY{BUZ$IlAi2fMkZ56LTz^YS+UP?3nQS<@8CWie1a#=q( zgg*(4&D%V;T{K=nqEej+qcfb>I88*6i!tA3snae#x@Q7<(!`!SIw7no--DfZ8{4!b zhfCy8rr^5HvueS3m-A_XF}Mj{>DSLcjgu&xzb)JVJ$Ge=S0lfh z>5S*jvbW5_7_Ssl)QpQ9nfTV7aK$%o^L4_vI(&}WEp4ePhfY#+_jal7Pny^jC?$=r z?ptRE54|4Q0P_)7gQ<);#2F2c;fb_QI25;!_PgnLO|x@4u(4F;$J zj5gp@1I14kbGiH3o(l6^9Eu=g$RD3?&(`&zn&iKW(`AG)`duQuxzS}4^Erjh!>cYV zvEF&No_~5l*ez!cMC!11Tj14m@_O3|#%R7!sJsONKgRpso0X3(-w7 zx{M*w+gZi7??hTqLJvDw%0$&5rdy1;Xg|j|<)te0BeEn}FdgvxSp$z<6d?EEF0_ch zCrH5pMu+fju#vezG?G1I3&a}nU6e{KGTsIjri8CjI@!rz#quZ;<7vqo)ATtj3h!23 za{x=)Y?^{xVy5APf0#JL+o!aTr~~;8{C;2;D0Xj}#G^{LhbxSqMlSq^u>?K4+fFB_hyBfig2HE`N5|V&In7G_lU_xk7d4f( z-f<$EBEa%OS%Mg4h&*M4ijNXz8(>m@-Vh8r&ILH7$+LjJP1BSJZvlm{Ns|`DMItrd z@Ul1<^pPeez5sg^OjgP6#+rRQ2Wa zq@W_vbVC`Xei&#coput7A_SG%hM+_8RjmHQC|Pa#rCJ=Zr59K0<@=1f&l6_^ADz|_ zqd>Gqt0_=>nV>4|)&t#)EX*3KMIkFgtG8@s;YCNYqw6S6huC#&)cn?st>k-7t>T{Dq;q{_`(T}t(FIOWq{f**P*`A|72 zv4X6Z5e`ERgL4&|rq)6dB_E^2;9VleF{yD!LpmQIHjfWF{GFRrKQ^iy`4bcO<`Qv= z<(;#08iWbfjX*OKu`1H(;_Ya1rcxN7a%B8$BUi;8EcE?*=S0F%wh;=J)DA@?VGlDK zq;uo3y&dNhmr4u7p1s%iCL1gH!BLo{wD|LEQ&>IGoouK7#qP0+a(>FoZsORebZ|2& zv~v=1D?a{|*fP5N&oz*fyG{*G(EvUsAQ6ZMID$_WRHRH0vBM^Gj%iW@LDpnuGx^eU z`NPDOQB9X^-AC;KI80zNtzRI~hs#hU(j&4*u{7bv3h#f4IzcOV67xA8o9bNO>HZYiMc1qxNv97mhBr^5>Xd3 zP~j)A+ee%l8>ACGUDnrT1rCK9!<5e!_yGV!nJxmPlmZtUfY5s+J|U}56OVp5S(_TW z7Q*h-id$*sKiWrwnn(&3(BPiHNKXJlfF&JvAHYvkjwv6>+YB!srh~)Z=z)@G01z#P z=6%kf21NgyA{PLz1EzTDS?wBC6N--%(Iz#Q69>4Oq+4jZ#h8`tbq*%ln&yJQ>%WRj zpSTOGuw3JLe{QfDNd!Ga>PJmG^&VrPmL_JQ*!cwnFOz_S4n^+~R}@9S`{)sR00s%c z9YzVx2m;hl1uh3*%{}UI`DoX7>%Nhfh?6vB#S!7gQvk`L-7S zREJ{odq4y{BJ@MWHjwBbXeQChIIrotM;f6U&k4Dm!FUhMtwlS@L$b%33U3?ZCl>oFbsy z>v*?pGinc-k-YketlL9-gyYCa5ld_P+{h^u`;hpVpCS-apvrhLO!bZdXCjzSrH>85J z06*q7tR72=6nihQkcKBJY1EWU9{xeQUf*3w1$Z=?$o<}ifH})3A>Ynm;(16H0w6k3 zL=neTp&bzzUD5)fjjazbyM>1R5nJjpb{JTgQ0P!pIf0h60!Z)NsNqk3x9@F&fI&=} zm{$N}u3XlID}9{nRSuM4dUWD;khWo%QlVvcf$lgJI^?0j_;*)PJUQUAbpWu^KkzC~ zfCfI<=>+$L`v{Xz{o#Zuz#4To7<0U&N}+5KQySao{s>@-Jg%p|^zULj7|2VuZCB-^ z53p=RqNm(I_0Q9=1@j*QeNqYCSqfvnQU+rfq4NPMpIpQ{i0xMWIDAU|OnqX`pdR*= z2(y_y^jM9=NUQ7}WLRFoVUzw(Uk|V6wk{wQ`q_qfj0Esbjc+PsdxY$kTj~H#%6p=Z zj@Jy1)y2{g5Q8isT!d_6pzVp(Ker84=Ld(we|*lnCR1kvg0+~q2(Xj54}hhJ`kU*y zsvm_|x5*5hRW0tOg&%n2^cz5tqlP%sFCJDmf$qet;4udZOC!U)WFd!?a$;P1%Xus1 zj^h=_3-bkQs?92+C$98sQNfPl3tBKCs5Quc4ya>qO)BxyK}GnTAdoS^XpR_!2lhbq z7^8C}h-to~$K@V3-n@Vs5GF^!1$0VNcm65BInPH+Vx_ZGy4RN$u-}652A*R>`XE=( z09@K{Ni-Nap0mZ;FDZWjjcAlQ2D!E-q8ehv5NeTXLJV3-oLxF9>R**U#Q+IQMwp0p zhgb8EU-kI=F;EVo(?P-0+KEuluxIplX<|_#Y*>jZBg&mXTWmb+8cPqvQ?vcL(nHNTMK1;%Qb^AU zkM?c@>Auk*U~Bz6`0YO414p&oP(@_8LTtvthjXhwDJCIOR;lK4h8V!suzZOt?M)He+HU<9l5j^yKIrfT6Btiig0-JQWA zZ$_^9DON0g3MB^I>th?p)E`)C|7BFdoYBP%&qwt6R4aQee>C7-tTn|wvamV{zI5)Z z<3)UnR!3?yqV#MUwytdJ9#cnhb!w%jvng=PJEC=9uX-X!h4B^e^(Rqc8~yDj=7JUP zy(&onFn4du1OPFHadeJs9`1BE>`(Q;5_t5mgCZAUB^rW8GeAOVE3UK)#N_f07Gt6g zc20~JA+pJ=y4{4JguShffi#lN8oK-C_nr%_5M2GN}M;Yw?u+BAAq8Ux7w zxjN{`^gkA6OIFO}j1kzEDH+cA#xtcCwvZHwr>q zH2+jLLirY5M#}d_`Kw{m#cDuclg1Ua)9=Q%GR!BbU~$7<|H}w3saK^iXQ&_Ij)#bD zDiG;<`G<~dV|+xQ&g3UdHhr=%6LU7YAj|Xw;P(l_fda4q*pYvL6Ah?*(6;dstI+0o zneZVcPJpAKlG!kl0-^S7q{S<9>&QtkPUFK~0$t;htHj|9j|$~8B!?1^7%2^3joSvLWPwuc^O$to%@I zuuPPu@V|k1=G*c?4?#YX&7jyUbn@?8?6vP_Ld$A{IAGJ6d|et!+dlZ)NdoFfbt|cz zWf0*3@Ke?c+VP^c|FZx*-%r_qu?LPs73J^4pzs07%6Ahx0f$y3m_o`B4XrMP{Pl`^ zQo*2uH^E302fk03u8MRaQd01#GRBy}T8xGWn_Lzgp@t7geUKFM>|7o|5fE`vEdFYv z8=kanw1602jvPK=mkg2Q7Jy0~8Xm3T9> z$FwNm_wrEh=8(S*q^y+Dn*UmZn+6352DCg~^ias}_oo;!7J|v$4l&qdvMkjq8R^1T zDkWenpJp@jrbXz<3>mndXa2>=sVYW@Gx&hf~SaJ+XzL0X#8$`+RVG z9)e64h?jw=jh~vL46?q0F{=N5G>ioXV{BvTI$jB~YfzW3JX>vY2niH}aubDLlUKoz z1X)_+LW~L1%9)%)DgN|}Az4hk0diy-13eUCI0W1WeJpflhnGE-k_O-|wTCrS37zEK|x7Zc1Ad?pe@j9iS?9X4TvI6qE zDFMRz!0>m}#6I;6_(SF*^g%;<#|}cbL1;**tI&lhVPC96P*(%P3#1M8^P+*%%Sw|i zoua=X&Ll6@vH^8CtK>StB;VcD9#38+A@52fBC|$MqwmEteu*lds*)jE{0F>d#N+&2 zW5)Vg=#l>s^;Kb2bzQimY>@8mZUiKyk&>=Ww}5nqbT>$Yl9CeAAl)re(v2wHooDj> z=RD^IZ*Z^0+H=i0#&}B)%s%#IV2J)axA{8B^N%ysEr{lbUqE~#)D3&*2oR(t<*F&P zg379}!o_mU258W8DcvAR6@x|I0EK?G>NB@L=vHvO2VxiTM9S7or~Y}TzxLTV{j}jM zhwKYZ5Tkf{hSLb>9agtA_(g}UweNR+j*p^A|y-n3k{2u6Z zKDjM7dmJeN;4FT zSmbF*=Pu~w>0ug9iFGdOqH-I7O!&?gAv2%u&eTquDF9Mcb6^|*Kd7n$)_k-WINgo^ zI{WhfZVs*pM`qC=Y#e*}R!HP_`)iAL=THm6X1nj5Ab(M3*Go+yV5~;7#V-&WJPUPU zg;sTTAqy0ca`;lsdN!xToK%V7ke zKRkfmX7RVk!Wxm}z~jRmSu4z?K@P~ao9#l$JJxOWtPW;CF{e0PGYI$g6!9eEU-$Yu zN+clSx}88%B=Q4coJ_h?2H{wLQ%4iU7r`@8b_D~_X7dIxD#-^fkpwH+$#Kw z{yIA*3p=`D$(yAXteVr!ry2B`Q##*?7x<={=pj=QR`ECK06QY5euGzWV9n#l)>1b>{9OTF_Oa_g1A1^ufz$Ue;U!WaAjka0@ zv41i`uaWp1R(M)))pPViU1h)?LyZrcZ54ziVT$c#`tDWcB}n&KLBm?KK1ioFrh#(@ z-dy^7*W$G)3!d;P_0*VSkgCGS=Qv#_JKMIzCam5Rsxej&hxzlqme8=kN3HisSOoSUG-|dAVT;gH;&no)m#ab zJUXTbPNF%U8^}ySQj!rSu1}Qo6FB05_1J2Q!R2=S(+oBS%i0J&(x6Z|sgFxl@I*Lf z?<7v~6g3C!0H{J0fL(DONN=G{ceHi;G@6G@AMm;y(hCdgJ|8Q|_Vle}Yh1Lo&XtfR zHyGu~o_wZq;fG5xt5G145hiXBQ0rq3q{vZYKkb7( z3wyW=W%RJvnal?`B{L7_%e{sVJqPKY9=@F7Fe6-$tK}@?>JT@WA%{*gP)}7-b6pH+ z%6j0!Qebs*R~|W0bfHX`s-OZd3MP5#6_Vow&%+=8@)i9M70w;nZ^DRqtT4;}){#k5 z7GOpEH6W1n*WB82Jf=vaFPK20qQ(-##%VB@HeU^zXX+(A6_u*PVA-IS0LIJ)|5W2@ zBZhB)fr7#k)$Y3RA>wx?`jf~AQJ(v3|3|MMas1*9f8>ZZJQvwf*jQFSBcS-^ zS^f5n67e2rF(bKShu<~p1iw59cI*OyEzEg-Kq+srZVgrAx5hjPh^h)fU4LA%&MQW2k~rmk+hRPO5Gi&n3* z{y7Gt#ox|R0pc(n1ijyxq|xDy<=4%j*bZ4B<_xH35?KAaa7;f<$sI&eV~Qd0fA)g; z;q|`IzA)Qqq82MG0G%1mMKUjjaXwlsj#Pk_eBj$4l~OVAKHPXMXgkL#rxMJIghnN& z0z9?n zj}k`UXKCnF+5{1Nqd%W>Smh$#YdcgMlIkC&0C*%IU$K=g_OIEuZALh(7;q~a9xi#t zEOe-F5occn-I%GkFq1_=&A$FlA5wevjH1s;8`i#JXRV6^=M*Dc10_s6yr)Vh30y{e z9xd2_pu)_y)+!mya3*15oHZ%*7~JnP^uZWTRt#a>@*=Yv0D-id1vi?ej%HG1k=@-|AWiN^SXR`DDBU&oFt<_-?Xc5XL8vMI;c)SfvmltyfjNKgzM zMP%VVW7Elp^xmdaP8XvV9mJ=RSe8fHvXY0Jq-pf@X(qWy<$_5n{KxpX^ zFj;Jnbdr2a^-9SgEXvZ9a9AoMCI~CjCF~|`T%%(&;%G1Q0isxpMf3LAn;g4fN(kdi zq1?+S*@TAri8bnD{ktd);ioJFd9M0656t(C6~XE0!+Z6ElDR;6cn)m&nwAtl#k@Q$ zYBfhU`Kbw>?2y2*zWAfBom>W&yvx6&u1{7HX0HN_ z>h1vTT2wAfz3DS}D-wYv0(M0i)W04~LMO&{0;w_MAaj8HK&+e?M#ITm3sxfn1uSFu z2LV)(L*(DoGptfEzd$ap_2JTwX>j@z1t6-E&ma!@khYp#TI{)(&-P$Y^d{XZ%)rKU zxYhgop;q~(!6e~a0ct})p=_~0j+r(57z=7T`mBCz5hywQkZm$9Xmr;Ck;tnIeCkOT zV5)VYwlKUftZph;{+OBqF`{AX{p;W?V=%w4S!`gdx1Cp8YGOBRwfe}H%ipQy`B~JI zD`p|n?5cuk({F`z&y6AO1;&+~#2U?f*u7`s@#Dl*6Btf+=R=<^K!yb^RJ#G>^T}_J zIJ92A}-Q2E|`C1?{44rf*Is*7MWuA*y zfDFl#^3Fsd0;iTZa{qfLLC@n{vY*V#(w*)I`%y{tVTNE)A2h0xfb$;-HtRa#%SWQL zY~aroO0S&FI{yx+?#)WQ@?;_ zMy5*>21f&Y1A9SW{TO0G+q~#M-{Fs@0QpRf72FWxHy^!yF6Mxs1v^JQf771_gf~& znRtBDQtfN z6bLww>F?Z+{=lB$=?ePOcSUXd*%TG^U-aOhs0x=RVuG`W0H=;Ha-U=INL6XmOx3S% zGsD~?XSb=d)9S~a2W1P=|@{kdf$kjE{^oyV=5Sej4MD%;Sv1B%E zZDI}F@MEKj?|57wFDvIA<@AzpS6S2oXZ*3a7{iA)5T%;ngp%;`_S>bmRN7(n1^( zI1pA6TRn2E+`}M}lwIO~_E%9`NCkD(=S(rurQE2aeXmtis8qce|5psC3U1XEhYuGU z<5eh}A3$C~q6P8DM^vI$y$*&>6#=ZX!0;w@ms4!rKM(BYP8R>_$#1vY%_EDu2b*G@ z!7F&_I=?5-TPAT5j!i6&&}h&i2de1Us#6Ri1XmTTCq1kj(TVHEN>T|T*n-)|!oJ}0Ut?W&xYdhT=eN|W2H#A_;59#d^l0*A zH9F^V^D7~)cYuZ!DH4j$3QVAT`qsW@y70VUsW|k$6Q41^*fY~;_pNSJ#Km0mWq=Gr#n8f2>PBP z9IRAp-o~CTFaFLG^?MM%wlxz7yYx+duS~+=Dc-;JQZ(^$VhpAKPpQXE_ zo%8{BKdcqeTXGOI!A1%X7qR?dJXai*?!M(5#9ItY5tVDa*hNBE%T+W~D1_To!O6vFIrgRdyGc)2 z2xb5#$KX>xQf(}41^rPh-0fLdl$pj3S z-by4U1F^Rp87tE%FvysyGU!xecHSb; zfxP7K2}G)-$w=VyK5DzN0#Uc;y*SFUtDpHClz=q><_O(PS4b#fcwt_eXU%F$}BXdZT+AE1KD@w9LJgVH-VkRD!QF0 zA*h?E9~n25738oY&;mt#ZzIZfK&jaK+ZDvYRBN7ik|{4{K?Qs9Zv9Zh{{6}+(`TprA`S7!Y3@J&F$Wfym7Z;3$iO|!bZ zGdoILymtn6S3`DR^1%`uzvU1u7WKc&PU-b7mt8LEYFn7Paj~?T)8j5N&$!zzeO+IG zQ4H`<3tM~xGrn;woWE+Bt~=Jd>DmPpBLqM5e^A7H9caeLeInue+-}&GF5>;`qp1B- zGn1X6iDCQ&A!C!r!JFj7;&*Z!t`1CEKO0x{-f7zK6$C1aj+Q>f*D5SXYo&{R{Y5ZV z@H+mY;65~J>QtKB;L7YKKc3Mj(6rLb|0oHPXA~utP6K0M3g#aO(gj)-x|J)&I%tdm zD#HMwRlgBKM(A300=abgsBJ*HT=QZRzywk^JyIm)233#Z){Q$zI=`-55>iZA) zu7`jyK^gNQ^kXg*&SZu`7`jrU4!`_yG4U|$U}xwr{L@C8**J3Fb5&I}vqTD_@@QNp zY9bR8CQ?r0fC%_PEEI;CZhSV~WRM>#4$_=(Loq5dff-t=-*aJ?FKc33?X>y!6Y?-U zZWl-;4?m`W>z<@xx*-d#_h_2{E{MqFEOhe830v~d7Ii^eO(3oon*hUtY=jF`zfr*b zN*+|(42;R!IX@@>(qH(@7}J+i#YdsX^O+2t+3Zc}31(%)o@`nTUZnKdlnm^cQB zK3Vk=hs|5{=*!y%X=r@qgDa^L@6M9b4LVsMUJ9RnhXp?;WBaOQCoLUKFj0 zv3hTq2a1(QzcSa8Oq2}0^u#(SOOD6OpSGS4Wk`jssG{)$WcbWwRvXI@C$H$0|D zqjXB`DD8CeR9UoD!QN6dVqvypmKhz7*`4F}?XO^4x+N*Sr^nd8m5<@h|5&8(zWug{ zUXrOh3I8rVAyp{C5nqJMq(0#yIiNNWR=9n-6X;h?h)T;Co zI3f=mJ_JZ(Ab80qS2EsRC0C51Y{t88Y*S@cpKHsH3|sC*@mj49sBdfAQ`mpcon6t9 zy+Oe*B-48|m%ddL^x7zC#dtJDy!w%6hM>(vIPy@vqFl@~rrYkbNft71w@2BZ=B@h{ zs|qI&d`Ho0p-nc7UMiJ|3?As;^Tx%oxKlX^&5uzfKf!iMM9j%zmTQ2>pMsA^ZD-jq z6uk%HJDsN_z=RiDVF47itZqvk2_XaWz}q6V*Tj=Uf+++M?KIH%r(UZkSJwt|CcjfY zSi02R-+<+|Xq3dRq90=bFiT7774=*t8Oh_^x-|SUk0)qND~e9VQM3VAO`V8{ofrZ$T-w(XcVK#E{ws13!QwZ`%8BM- z0_y*7MFRn{AebBRRH3rP`AA4lkM79Z9e_ln$mJ3(4#5>?F?lhoZi|MCRE#(L%MSN! z-AuGddi5|bbkAc-D5spD(DE?`x%&*JC9Trj#^j-f=nuCyltb|6u+rrc;&w*hSE~&h z_?@l~KdhG`9!J?qr(yL~o&edNz!aiH3rIeIc*Wo}^-_4wIA zJr(38w7tbwG}UgPx1$Gwr39bln+kj>c15nk!N7dDbcGl*O5=9PC5RWO7lH= z`G*}h!{B_sR=<4ZrE|5Z*T!5P^bR&V0V9NsHBUuDy;$*RBlNX$mN2_-Sa$?eA(_qH z0dY8~1M+abtb3awy6wM?ZDDXx{t#!k$`t6oDodYV06~C*ZOws8slbgwD0i+3=nAre z%#;9Aj&#ZDI-OZ)9dl4s&2zS2WzAfmGh*LMdFmN^gpPCwSGdYT?w3x9D^OHMskxkQ ztCqfKRJf1dgqOw|&Z2@Yi#5IILqDwM8p2?qN!DrYAOWl?`!8W>cbA=k@Xo`6;t&fo zd?iVh0C6U@0?aZR)`F_RJdD8@G7_ClViSvV+;~VRmd!aaYA&O$MU}2^#Q`|#ii|p6 zhTNTlh$t11E%k?=zO_&c;;S<-*qkg4Sti8mGkmVTM18=7gbB_`9HzET$bCN{9S2p6 zlaVV#$H4p_37|L#Y%O?4sdIDLarM;)iZ`QX%nhdjYFmx^iQ1Lwh>Z96;lXD*a(2mn z*7HGij`5nD_{o3w87=NCvDxiv`XV+n9}~1}g}Vkub|(AIY$HkN+&ZV~Vsqg9FuY#k ze+?!zi^#Co7xLTZWXm|KEc>GHShA|*9Val_`3gvtq93Qq;YTzB@mk5so8BXQyft4| z`h@gKF}o%E1D~gCvSice%tw%O|G=zzDBSyI-|CjBt%nXE;gMtL}L)u zm)!DxaG7+7ttZ+F2+Q*ESBKTP?7cR-z+lNv88R6@nK>^u8Bsca(9W3(`<|-agCx|v zz&9H!TurtoByAt)7b*a!vYR`X%wc5sDv$c+<;EW|m9X;RHNXNZl}nm+tDKZvY*Ed9ni(ts1{dM}&^P zwc`F^zHxX@xovJ(w+A(;Ukm!31#nxajWLfeH+W{<${?PSFml+OQ!#`Xy+M>tXd5<| z6v0G>P`rxL#XMz!ISr&`gzWseTW?gNr9I1`?9be;hPWblWwp{tDWBB`;)f_rCQD$? zcgiDO_s3zqFF)PwTu;zwRLUESx$CB}8}f4;Fl=S4y|mPk3nsmGBqiLGwr(g}NR%aj&FW{(4_B)v!|Fl5whvUA?>*+esy^^z{f<9oXrmW>B~X^@_hI0q7b3 zRbGqHFyTr<$@4-wHBW&tO#~NJY^h~Kx=*lL2FN&fA`0cjau%DH>LxDR!mXhcc)3BG z$Jnj-ivH$P2r8G)wgXU37E+`$)?%|-%a#NGT3ujbmC{s6U5%Rj>KyTvCv{Up5avka z^}sczdFK-<Bn*P zsvL+Ld#Ssjc6C#`TEuARZz9Z}H-nCrvfBHT9%UOLEU}n=4AFZ^ds#x5PP~^PMA}W) zOH|NrqYED&?Lu>CDJ(#-g_G0Z$UOklz zzS$~q{u$=e-umU~{e{HtxLd$+sm51WMbZo53Zu3VJi3xFIVQQ9KhqDPvsR}z52c#1 zOQi9QkqfSe1QiB4i^{_;^I%H&f%RU zC(N>~xz%=1c(*eee(#Ky;!GF*Dx2(ZcY8TmC1SU;1M5)bKTnpXTegH4m6FK+uBq-X z3-t8x@z!(o%VdPCh_8vW{OYENcwwlI7z{pPQwvfercmsKA?umh_+ZOm}hNHnSTv57Hf9O3j>RSJD__BG3^WbO$5Kxm495>FZK8 zMxyatEPG{crXThIXY)e;Z35H%PhybMfv`qvV3jaqTPSxOcyjeX%KQM46EO6>(7aLI zrhv+>kmE6vLntq}-*;hNlpkdgHPA@( z{o-WS14K-@2RHX0=Jgo_6KU{|{c~7pVz>XQ^@3n5XPxfVE9XQwOX0{LVej(O*t5Tu z+h)aJ7+CCWR`=X7>ct5siyHO?35}%gYAYqishFGl4pm?p@NAgRU^Ysz`;KF*J89yMKF@Id<RCFw8nQ)=>hzLF4bJ9u&KmAMtg}jgN?> zaQycNPTp!okQ=6OI8Az&-Vwq5I8o=o7X4tL_f)`<=GR+U89rZlZlY~)(+pj?`&s<(vW!Rna8Z#tPMsnNIM62$hF#QnT@gl_Fo#)+RJ5&6Y%s#!M ztQlYTuqZ7iXK+*kCVfokv0E5hOiM{V5^Nx#G{|ol9}gh&u{Lc|x(U3%a^&DWWK(g6 zQeYY|R@=YCUZ z6OL%3Trq6CX&OU=40@4Zf)l03h`A)57hzH%LEEf~f({3+_pL0Cwsm0hZ&Pc&fw1d9 z5KLzujP9~LTW&}vd$!Z4pUGM&E+4|0Qk3y+jm1cRY~YL5h8mpV+eq!G80LsS3gMsUYjZ%@M)w* zz;Nv`nvB%;$C^5g5_7}~Sfptc-H(WVX|F6%%iDq{d9DlCN`Kq5OKru(z;qtz_lNpO zQNFEQh^Gup6#T{tty!sU*53ZkiCy#9&PLW;2%m`RxAv>>&VwlLttQy~@Dsp|u`N8o zmStgqLyideUBOd|o{n>@WHlu&`TU9D#Ho@114@Tb{>98*8>^uC@cDSa|LK;^tyz@B z?ORe-DO)&u7%RJ4ACS6-)v+Q}0E$PL$M+hcWy=^z&8Ghyazf)^M#4aqVGupr%IHd^ z+|Gig;g`qFYM!0nhdd_htE8)6zHHDKP5xoB-VU)y=2BoMcoCM{TlDs zwzux>(-~jRR24bepM@(MeEH2WLG)Yq-wZs!Q$mx2R33LQm9i z|H7RfG;X+Xo>3|nE;4;z4V!;w(CnGY{zZi9q5w4~mYyI7m5G$9;F&G`tRMIV>2$V| zQ6naN8LN~&ZE0PXs|IgY8_&?DIv#y@MXOovb>D%7)553kQd zJrcZyZV*zy9pMH-q>UeLFB+~`0Xq5LI)Qy&Cvbe?(kck|;xNN_Eb!&CAxo*nas>=4 zf0Cm2;jAlHwf|8ydu<9nT$K_}ALRwPZr<{KJMZjqej`_dt5IR{u~dzDIMZqao-B?w z`8!mgxp5MnXT8v!r^$>+K7(0NV^Qj2@w{Mk{%qX8<#$0EIh)KsC5l2V10$?<#@_K0 zzwLEP$3VCP4il!A;Bdf%ST#(F<(3FjBf^%6MELWQ^9`({kuh0WiDDzddG;sKDfDyi zj$I64OBcg00hCW+G768S1cYk9{w?3f4cvz8^E;-B=+*`su$@VFVD`&2I;`eT4WK)I zF+tJVKE1UI74^M`q5Jo)h^Y2YOc>mX9a!VITih}$`F>}y{j7sXz@#vr;d65C#trw~ zbWQt!Z;;$eKvs-`(I?01c(1MX6X8G~!h`N}pd^o@IxqiB83lU9LNNbUJ^hn6TmSPj z-xP?SDD}SBJ@IaJKU|UnI7#g>z#vIeIE+LL@9S!)*I3}dJeWmImmL$*xr-O;xX-g7 zEp#9V+1is!RGrZemG+C+aZ-By;)eSlBV|!%3#W(% zzrm9QP1N&h$!1OeIo-aw6!LB^qUB$bPO?3eZ;0~|^ zE&x+X&(sF?0O@LJlvsOlg18bsRDQxMr_3M0dD z6AQzt2O324)LQPBa#P+XiOq#FOm)0A(tMM?xP5qzXmw zZ^7>}?SZ}d0Jo{(O;C!TgF!g}h+|E+^eS|Dy@3fhM2~J2b6h;sfxk~no-MqZs$dpA z5+)f5I-KC7-r!BD>WOfwqiP6t`&a|wDS2V3CQ^|w#J?SHuaO||jz@k zaG`HI?tba~%-{i(WK=XlPBGRDE0?JvMKCTLN@l{@`jwOOOi@r>69J0nAL!azj15%* z5`VeO`Y9vSE1zkY3!S{j_W*$15^m3YZO2?{6`OJf{DG5gs>p*VamZoLv1%#+S8QzH z5>H*U>khCt2y{T~hxF{saY)$*fQJ_9@_S z(Br~+Mz3rTuIn{k)99J6OmE6|_uMG&+~I_IB)GwBepF;DecsQ+%qfirUf~}O($;5U zEpsgRE|qC>IKhYNG3m3q{28QDl( z&o~}Li;snB2eQOXz^*TQ)OvJbF=OpE$y0QX2;!%<*71S%575cMgKm(CGIFB$v}giX^J(|9*aEkiXcT z_==3b1me?GV~Kf7z-t@goPaT$n+M#>v6po6c)mVGxS=w1=TcMUTsU?!)9U~h1&*LM z+c>0C*Yls*tF%lPZyzKQV{AW9PxRK98>UIyLaxoP+P*SkwJEo8DXB3&2<12qLrw`Q zm4(lsLLZtu6>b~AH07x?K&DAHK>BFpWxIk9x8n9!+CJA<{!&u;;QV(~4#ZpJ0zQPd zhMM=)p`yxY?jHaL4Ez&%rr4>*y)qx9lru~arUxugHUFh1r#j;N{%(duzz*aa`o zkjx7u!zzoI54t?aV)}(f;os; zl7S(US+6YQt7lT#4Hj05a9V|vdqTWta9Yn`F~!)zVPRBtu4758g|j<=)_Y5sHJp<1Z5Wu>(`vnAtd4jSqQLrM z#IdnXcLGojM8HB#&IK$bX_qv1Wk&yp zPeLB{uTK0{79{tVK0CBC>8N1+L)ts5!+MK|8M?f-twGG%`O}L?`xnL3!A*`|z5H7$$ zaP!jl6<73(OZCN6a=m@te$a~llW+B)?spT4DCQ0F6SHT#_#MD7z|l5nY7ct}*zEd; z$-|`(OoJxR4AB-m6zR@Tm`9h3H-|xz9vhJ`^j$JBgyN7r)hG%$5JP~=X3(6xc7tSw z-oH=yD;T7$?Z5Kt{f<{*#{hRs>-Hn{6b)6-X%g2niGObQuW@un@bbf-bhh{C4Z}ks zES~6bP5^J2kc^veW^}IMRf+>U_->QyPGayf8JVp?vQ4@Ea6QmL4e>TK$oc9o)mTL9 z-+@<;lyN{lD|lJ3kQ_+6iCeGRLUL=va)ipFYX^x*id!nm4)ERB70)a?}%iZx6>8!QZi$cY7GQui_(rJsWNzE5R zq$A*#u$dnEex%<CJXd2N?D`o0qnTh8!V4e8{Tji0~T>{eEXTt@Rgsr=8+ST_LlCyu}1riobpxDm-P z*h@nliCP|5)lBu0@yW5A)#7PAoFX+uFhZ3F!G_e7g-Vojd#V@%Q0wJ9GhWS^3ZT;V zgN836x{>372TE+n&#w6`x#_ao7dKUFEu2WVmng`%?dKPPT5~(;oK?R&yGRUPVOJ6# z1Vg_Y3pv^lPnF*0jrQL6ni$E+Ahjd0K-n>{ABn5W6Ws)juW~T(gTf6XTqG=Pt4-U( zFWRujyGa@HCM#1eqkzd*QDV;_O5FYNS`tP6IIk?;e7|#&>8woevBIuKo?wiks^2Ql zMR$)sXT~Yls+!6BBHCLW;i1zSvxO-vU^w1|QYy+?$l9V8W${y8T0i=wveNjbvGy$g zZ{Vn!q#_|uSd)zurVWVHO{`tbC4W#UrCvKflV@_e{aX;`BUX2BN zt{WpMlMlAvm=cg>@b(^IBiIPB9+!yvTszJl9ZtwlxE&+J!261Q2GtXGG|z75aGVYo z^as@@&@VXZ0!EA{f@jUY%2n51kA+|88*j#vGW=#HH=es>R)ph@pYmZo#=L(V3ygWQ ztQKI~-4onmzD#c!5i44}yM{MM4h7>rI^}Hr#F=#4{FUJ!IWnUZyWTXtqSV&W9UylHi4CH}n3eZL5~__}QnlmbdzIS_u`jB{;R%|~ zT1Bq*O|nCs`vuKHZQBQ3qcO0VP38${%f(+Y(eifR<^XO$TCW;(EHp)DJ-SfEcq?QJ z2j8kZBa%*ylHxt#Ct7O|zA;I_dKC0WO+HGjfCk24Ym% z@`2JY)iv&z%GWJ(-m{7N($xM)m8A}C7x>~t>Y(*QgnhTaW(KL8c|VW)rDq{oHoupv zsVHKAZ7DAg5|q%1ESIvj@#1PXUi5LU4`-x@(hFPfvH-+MnjurLbdeDwAmYs1fAS(k z=p(&= z1L3N>Hdup~o?xvFvazB-ArrZ#2}nd{p&T?PdhM3SsGF9`HPWBUS{@7adodq$QViDz zlrV*FZKl4=R#0PFL&O9$ModOM@Q#vXKgJI%nYDcYn}0}`;MDW;H57+(>s5pW_WicE z*E(wcbDLjJV!uU!0+HItr3R5N=9>FUPH%H65E^+gXLjlm8EitGJ{dOHX6*gKxhS{D z>+SL-3SpaJ>s!c@r4E&n=YO3l!TQ}4S1CFCmCZJI@|d&Qw9*nJ2{;GFS_FsRs&)Zh z{wXeiIsB7fc)fol4lI(L&TlSNw+BPRw;#J->NKs!(|+@_Pxi28^d9oHqC&ZKNjY(rwq(r-S4 zq$nr?)@O(ZmXe{1_Kbed%j$YcL(?$g*S4}R@mVPirh-U@asy)Yd z#2r0xwn9j65X^Tc+142o)@Ud+ zsg~-F{jo)6j(TlR^8EPT#c3c0ZD^sSskJ0cmgrdrP~g>*z93YwAmBu}S4C0$S{0<1 zG?0GQZfl~$s^3&Ko1dMkAa25Hf;08%wIR_iqBLa;)_5VSPm32leShPmt~?e54*~nA z*R^B`R%pu+;b&>Gtu|_4Qgh0SYirdl&dz`r30XjT19>kDZM)uxuBkK?hFWP#Q*L0Y zG)~SrFwuWGe?u;$q}$DhP-UnOW;?ztq^oM^N)V6FqQLf!G!a2nAm-@nc11g{p0%3q zgm*BL{Xd`rAhCC!ofHpUhTM%kWmsmPWKQ>cudn}6$>%MJ7%WIImNe#?Rz@(xY+Yh=XEj@YWRd&8OVeyLZT5tvvNi5M z8l(yzjN&`98DIA?Z1UPg105VeFbEm~=LZ7gwuSBM1%=?2O6KK*ZHMy38q>t^e*|37 zj5jr1BPt8TJPgI@A@B8m?kDX>ll)!}{uhd3kP#p~`8C3zG(6Aj=7PcNeYy5cA1^RL zOspad%a*rQx+_MYZp2wohaKksc>!3VaZlS8!ur{W2~0PAf(ib%F9G+zVP2$Oo`e)7 z+&9u&cl8Q*!qpE}OwyFNm}2jU>7ms@U-BG<(6xUY49rudY6i!*Eh3S7cCNfgZ_!1E zN3DMTVFfb}D&+uUW)>hf0xdV-?Cd*!Fs+{Bn#69W!92GubQF(yFOJ%1hQmi>@4U$2 zz{wWgF1r4h67yH<8~ppH&ghCdW-#bT=A3;d_%HoO@J|V*UPFVeC-s(m`-=r`Jr0Id zyF3?@MVsE8gEf94Ty|S#?MRZd$9{iU>jPRF=vMDbkPO=SX{`3cA9mpLJwI$N%fskq_eeI}r3ovt>MdGPh}6#e+o9{vd< zj}#B~GiE^Fc>UPAVlg7ueq4{{A!GDEySqOaMO)**dTcK#C&Z{3y`Ef`Dst5iI56&j zgDx2$*w3sGVbR0-UcN(-%C+*^a6XnJYYodEk@CKu3&&*#kIRX@MB>+axQfCmpO zLZVY#Ps^=9O>?0}0`k%n_k=d$jF2nBXT^;)e->Ppa^3rM5Z}3gO;SLM6)P-u>Gi$>(-U1>V&>=-Q>zGdc)yN zJr1fPG{_jXRxvLRA5+B~4*No5J`@#>*oE*_qXc547+CxWCn%K981-W7zx6otujzSk zJ-EGKY@gXvb^dWpBp_gVI>WZRT1$j>I2E)L2l-Zm!h{fX%0R64M#}^@aE&r|*u>FsM(bI-V%iAQoyO<~ z%YykE%lh~dz}u5Im3_oZwtsl5AuC*p%NE+m^V9!IM?NcYDvWcLZ4qPKe<7wIrthOl zeNoTOol(Q^&G+_)&&^ld+q`SX@T0pO=VqXHAVM~)Ut?aQzx z4gZO#?@U|&yQx;!9xwY!pt{HIpde(B{V{DUr-Si;e~SW-?uj$)e(maSL7}Vifw;z6 zyC0A95_Z`7i-QH1kd@4PfrELE0WOC8u}-?dIq-c zW4u z@jsh{JBMGej6q6%bB>K$_i>7Kj@E<)mN4g2W4c4O)$mO`Ml_72J24Qxq>7rso;xU< z|C)iwrKibb3bU+uuzzr;$oOfe=)39D>E>8l4QBlTk3M& zwdAlf{Clr%J9s6T%J&2DXsXEcqW?B6OO%Xx$5gF4lU}N8o$2M7tHa6C^jTj9>C_90 zafA2y(P@?ALsS2@&SpFAcCs>zR3{_^N>mLMkPg!zI^cXOp z^Xw}cK+Rym(tK+DkaaB*TmfrR2M9<=sp1Rg#zo`4v zyi3~lU91qJb{y*T!$fm3_s6%ylSksW9(Up&ep|rtR%(aMj8Vt}QFwmbuXic!q$2KN zZ)5b{hR1y=Vi1}(rBPDTz3n@qCb{Lg>ES5VFZCh6-H!|Ww`m*~zh6U2eUe7y#=XQW`{+cAB?fqnt(p!t8pOQQCnL4Gme#ED@*xyutFvfnY53b{S z?Vce0ctsDiD@#SHJ#kwOf91z8iplPNxUWY6wN`j(U#Fu%QFJXTM>f7 z8lisB*Xe={-{jxC7|U0;WUpFkv|jB>D(BfU*$Ufy$Y=H4nul}W?-dYWHxd+O34_)j zzTO(iifojsx~|5@z4)P6oS_}LAjxPp)PBCorJx!zR7?7hV=X)IL{5`avHB3dtHB{I zxe8_70%{2MD8oq~W%b0?H&R>(#Yx3fWx>NsNixHM9wrSuerG+e7iynEv~L!9z7J}( zXHMt#K!kj1>ugcAT2y>IKHjOxKEOnbg zv9%U{Cv-(#SXlOy%dj|G@W+(>Y}&zSEYQI->9=$G^sX84%CfxYj;6r7>yNqEbKwEH z=m$Mo0xaM;?Q-Wrq&VTKNSkMPrS>x^2L2CS-yP3o`@WCfWoBk?LPl1I>|OSjO$ecs z5wdq?h3r}O-Xocn>=m-Jv&zbf{LZ_c&-cII>(w7m_4M|>@9VnG>pYL+IFA!g`5h*w z))PGk)@a*-huJst2VOL^m+9;nyL{WGY;@fVp%Hbz-qKP_ll_1ccbD&qWb{VFgQvgy zJ1}_fT}qn}r>%~Ne={q)GW6ZT@q2eEcJ)%AP3q0ISg%d-pu`RugW<}HJlAKmuZmv& zF%xF_M{O9G7^OQw9M<%XY+I84m$hUgwFr6OQpVqRuekf@~5YAGD zXLR9xm+$rQO}pJcXqG(lv z>wZFQEM>)8zdDfi@`3(!DC{QXBJ@txwv47%3vJeaSE;t2;gVfsHe$~uiNgqjJPsX0 zYXTu*nO=M*^m~%u)k{2~I{t%|36))}{;DSKaHj5))~b(bGhu_fO#%yCZI(|O5%&EVY7BdzhqsRMb<(dz^ z8bwDpvv$WVmI=}?Z52=FKJPg8t8`DkN=bdu#vb}oQoSEf>(6jbkxrhWrrWa_#g;!c z3C>BkzP@**ty%jcQ9+*f#c2EZXZ6q4J2Izpy%#05VlkCwUi$u++4cQI_MV%kccHPa zz_AKvA;9w&6@RhJr9sCElxdSo)Wg~OuF{GeyS|g_)$<9oOo!7fEgC%v%c-xmHD~M7 zFV1z+mC6JvaJ_c#o7Ba@mE3S$^Nl=YSx;A@WF}dvOxO$;saAr4_j&>g4Q1UHV(4Gi z8KYVOkz`jU%S%zXja%5xj$si7TdiX|x8Sn{P<6aO)e)%Cn?H}(z$BI_cwRX7r)_uN zX=L|H&3UIwbZk}AA1&NB`yz<`5?Q>rHqoX}l;Y-_0q`lmbtg2Kez}%6L z(oIf$V=lMqBJRQqp%;<>)10@^3kK7LQ!rRnz)~u^#$vMF4K5QfIW6_8$%zC$8`+g4 zized|Y*eTP>-g}Q^+XX_rRb8LJDl?<3BAi^G1=u})Fz)F#}eE~&L) z;f-9iq@dP;_mO3tB9*yB2Cb`Vw0Bo%;#hUHI1+EZS#GNG9@w4B)@ia*ELfOee|k{; zbd;~jf~D_8bo}78$h{R6^}{QDWY&zoB4-xjeFsU-?voUUvf$XbjtYvM&bDV$U2Xqs zVvtDWTGCP9AqIv^$v_1E+MI~3yx*r(&b0FRWuOh`EA2v^f_&(zd~8!HNQzER$a_N^ z=@J@7cuv&}31slA9I%49&_@2dm1TtqEUyApd@{k==GYcYSiXR{X+aS7O8|L3^-7h2 zYDp>*EB^@vvzVtQ5tcAUdFwY48l2`Si({>Ze0`g{T!C}7qPYZu9JJQ*D((gh)C5Mt z(G^^-w*D;A7>wH6aUJK6eyV&cOCCEv&%efgnS+$$<)z^KE1DrHcRpWv<<;6RmeyZmiyZkFV*O9gUqPCr`?*UwYmwdC!k^&jH>PTeyo8~;-(Ui?1f zaPqg;cvXgtIA@Y$Zgh`3XB3@Pp`TW_O{}U=g~`(yL4hO@1LfCT70^t3M27PNmY{3(@6Rb>AAo0wW7n zS-na{W8|c{RDAd35}hej5p#%%mx^?h;yK<#pV`q>l)>~oR$~SH6}z##ZLg4DH?*>) zh8A7>se2LEH-SyNA>jn`%Oyq&SAoTT1SGgy{VM#vYOh~(0AJ$Be9Ah(WHXF8x?JDP zd-P%R@k*v{%X(pvOd~FLJwx16FfE&cc3lUMiq|-tebX)CQ{uw#U}EUANN+8=OY|8V zJqXkBV-LgA^St`%dIEcO?4CAWV)Z>Wccx4M*vja^4kZOk;C z17H}11I9vpLG8bPyc;-}dbGUcxUvEoh}f|f-}80yd{MbfEUeqYTmfo2Bsk$x4{3%^ zeXmMCtCR7&%cm}>qTa!S_lE&@-CaHnw{lkGTWW_$=!9n%MNtLO1NqUg`N%(S<&0Ez79X$}Xda|>5O25$*12GnU%7}O7pA{S zzVtY2OcZn4`5MiA`*a5v_J~_Kd-w0x<@EksA9@pN(fqXI@=dEc-eqo#c*47lE(PM- ztry(D%ZOF05}(aKhCG&473X&p^-cVKtLe8S^NtH2o_{i)bh*%;B5K20##?3r{#sK*+3r=;k1QrMT&=sCT1K0iAQ^#}v64Lnot87@&xk*zQ(Y@!!V zcV*H_az6#2Q6;9H&0#0YpyUL?!XsiXyz;fcyvd$JYSC{|tD+nu`lG&`HC|y0^!tx; z#(qc#UYbaM_9N-i^_f@L7^O+hv4g-og%DhUHr%`{%B$bxuaq?wKM#97B^W3iC9+jL z(#s~ixE1FS)-~CZ{AFZAk?u#-OIK;|!LwCOE;hy@XA>4K(8 zE25H*|NdjC5?9=!Oo5F={fe5fRjGrJj1Czd&Om$L8S53TsjY>1mduWpdeq6dc zu1|b>Wuf!&@?M$m8E5h8wfVtk*P3^DT26@tLJP=>GbDX|68GwMK2fu+^xuESlGp%E z=7P4ukHg8~eh9;IfyFAjLI&yc%1m?y828lS0X5KEc2WebTKX!j?nm-+t9E z`)xL#Y2}O9>25DG_0kyn)pw>l*Unq^9i!t|dgJ>-?AV?0O<1`$^ag+53Vf{NoY>|N zud9Nk!&?67ZE|453Z00H#kP4Wa-+C-8oB0 zbeYFCyVSkwfyOQd( z3OV~M?%%pz+HhMq(tg0I*>qoU&y*A%oL?P2c_oQM5o!NFFE+LjwlD}syRBh-m8OYV zUx+Qmx5`3G$$RE)+*xX3d?Ryw{l^xt;!a#7s`Df@^mc!71$lc?s9`pSd0ua~Mqqc; z%y+AUAD3mX>&s&FjTKm2(rtPV&I`IKNj{aS?woyZ%R7=ixhA)=bc^)d2Bk44?y#j< zag}aOMs+VhmoKJsfQa)-kn7yt@cGld0rlw?uj3~h;CYhvq??`br_S+=Q^(~4pnaym zyf3w$N4HU>HeBw{VS^b6_`Yu6uQxx;XCR!P6A|DB}PE3eV-?M#Ge_wRBR zD)cJcpwQS=kNJ92QZ2gZN!qPHJ2Af1>JrBvB>o&NG9;Qj+p4kf&M%x+r{YOy5(Z7k z*%sV4Dygd{KutLSzgyc$_v~taUUx=yx>@1I&@TVuv`Q?HfJJ-5^?LxOe+Gv?W;!qy zKoRL|OE3;M+(9dr8c)-90JsKyRD;aOX=WpZCQn(iXUQQGb5Nq*S^E_}x{*RcxI_nsHge~jvz z3I0&TqT;s-%Tkq%6>{7BDwhNb$>OcXjnes3XyB%!yRweBNyr9*PLiH=m`TI2VfnT6 z$S0Yf`Hw|iz7qzO!wt#$*4YAw@m1yhfppsbb7=gYPYnJjdGzf9HF&L=xQ^;FwlK$t z@nfZf%(AvmXsWjq!mL~p^aeL?!4*C)hOr%R5tn#8KX32xC0mqHt%IHD&n9Qv9%u}a z@x&i9eYOQ}g4(hAB@C|ffikSvNnW^Dag{JQcp z!LxV{3?>GQU>OGTSta`{h2Hy4;ET%V`f-WmI;9K+RrAd`uaT2q8#XPcyAiZr+ih(r z3lQ(A`@uh|mv9(7#;rSPpQb|II)aa6U0@6^TU9U)(oY9aH&%fe!==7Sumz+Nc#fr$ z_?T(XqWkz;Ai<@oJ`!~?KE|Mz*tw~;eC!#gr-@#WQei@Wv(DhIiBsD}>5xAjNZx1H~vdKZ(egrc=)34|V7lxZo) z!i@xZWQhQEnO=kxv48#wZ?8yit{waNHa{hyc_{>%a*t*#KieN*hFX$|E*BBz_PD5| zwh|VpMC2UPQ40oA|J=SiNi_u9!(*H1lsH<9*qma}LmfaRSw$+$tV)ADUmj_8un8zoCrIu=A!(bRmqn-Sw6wH0=q2+rH(WtKMyBG$q$0 zjlmnX3S1LI#f}!O8J$WiK2b^EHm!HE_%HoN>B z_YRho@-`9n&PgVtL-(0tp&>JEDfk47&h$bVLw+8f_d+))lqLzWutAQA8^G`z%OTNc ztjtW3F%Fb$U4!YWiM))7nV#OfYgdo>+F1;Bf~pftJ=wAR60N{_H=&K@%Sg?`nOoC} z=|0?4RWY9i_f{yR!7s{_QJl}(nU0Z=kxZ3&*?e*m$(_p1+p6P)Jhv+!Hm>EG3w3)0 z$yNtEYT5~bD_f0`o$4cax`aOY#t%x_cU!eno3E6)cZ#n>NYx-~=4jO@jVhFbI2_N* z{t`LIaRUCx?Lex_d_S;uK)XjL(&-E6AHThH4!kUcB}0C$)71i=d2Mne^O2T9O|O_D z=W}93{7qv3!3bSB;hPi@^W-1jUdzi6T0ylihwXeuava1tc*D=#1WsLJR|l+L0YgF5Qydi)Bn`nIma9b%~qfh>su z@K6^oCOLn8UZ-^_ekjT}U}$maEWA{EXKCLja$Kz)%q3`0f&zA^AnvPj+!=2UuJ>J+Y-??m~>SYk{{3axP6oAiJ)5xi%7DA1hY zr6SYMM@~GDZ7VDSsoT3Ph)C*OqA$J9VOBwa2Zzknl?tr5FM=YzEd=w2@XR>loD0e) z>&66^M>AeAv{o_pS=@1{p-RHUIQ^j{+DlpP5s7v+#(vSF2idA=>iTkbL+?7T+z z4lUM8^8D_A@M2`S={(dRWI!_UW4*E? zUzSjJ4w3ef1!K+uG;U8r^Ev*;(eC<*26LwMiQAV0{&-H?&!n)n( zjOe{CdU(r&p1=x^;xNZfChTfT-o4U>i~YLMpMP-5Q; z*Q#L$?DUDC>?V0BlpagwQx9F%Tg(_qOs%2heImVARF|2NfAR4S!&Cs{m-bts6Cc=A zWrfyOB!6m=Qj|8XbNmKxsNFeqk>WO}EbR-%26`<>pO{{m4d1h%%Ge7jY;7Xmp3_Wn zSgoPfaeq$KYP2R#ZemsT{=o2gjd35Tzb>|1pVCIX_gY~Wzw@Pe-V=ft-+)_x=2A=U zeS4Y2mb3ljxP$%=;cNAwJ!9j6s>9VU1=?G%%Ir;;+JfA|7{gR##t8-O$YZ@A$8hId z?oi0RpiJGUlIU8cWru%b#(SrGpvAqjNUx#b@<46+&*}1hMfUY6FAbhPevdILEyHAQ zh~~(ux?(N08`)j|5;lQ{Meum4cE-&JDVdRs!+PCq+~jHS9{97}PTG51nnDvG2yGBw|V+zA%Hl|k7==kE{XB$K*? z`_1M+Sh)4UGqm~`Joxcc^ZFl`K4tx#m+Qg!BJM%vmZw^H-J$aF1oy0IlugeO?aLM3 zbniEY&ylZ~ie=UDHts;)x~(`R{?lVW`81$3?t|d2GRE2~}iD~fKeu#vn8OW$CnkTf?#jU}9vZkv47U$BP`%mY*pArffyl950 zHE!MPc|pFXNU;slQ2t`edN}-3aqQadTJ#&G?;d{MgS4g!DwtuAkkh7`mph#y*b3Mn z35^+i7(H%SO{ZQjl00T>b-n`1@^h>3H<#9@7QT1N*aio6$b4rR)kJpnuKM4c0t)R3 zTmJL?h1#Kh+NaZe!YQrQaVHiX?*Nc60=Y?tUxqW*JuF+~>aB5{8si3!zH%(#h#BU) zOw&jrU{L{8c#Fi+9+Jm`fZvua)hPgOQV;3dS04=!NN+>fs=GPs&Kx(8-TC*%7b8*u zrLYAU9^kh#9`L!~pqgQJ(*Z4AFYx}GCR zIV4-yR38DA43ZBQzZ~EiLnn@)EWAN7{d)v|!Z}NMR|LUuXBfoxqyn!%SA3aDN+1%R zziLEH^!m4zbhuHwK-f*x?+8$e8(6JlIF$A=CrEDQO>rJx!?nbf+t-7Is~T*PsxL4;rw@aE9Fdtz%W6 zkPzI+(5!O*_BmWM6PKtR%)!79e{GPM_%lJQUatZlo3-dSzYFa(fXD?udvE%9@jI7{ zwen_+B=hK6EI6W2CtFIO2=^HMei8FYma0jlzqTfG7dorY-GO!#vs|Aq&TtAZ8~#dO zF-iLkz2cA`WSN+G|B4p}<8VrQOV@R2!;3_pFQTUlVuv;`l<>=Ca-si8c=pYgK$)^a zUNGF2!r4{g)^?0hgQmog=lW?k+ckZfQ;)v;#%zt>Erf6uK1MWqJr+K2dssK)<~HS6 zckwcl^M!Fk`x2+;zF)8+@utsDS0zI|p5tjHIp02OXoiu_q27`!3J3H)M;6Rmkg^h0 zhfEK6tl%=C7UH9&(E>8&Mlz)@Y)m&)&3NtgFeBziwzf zsHiH#KL>l$Azp$%U|Sc^wFARLX;*SiFwq7^4D?(!mlc8&!-r#S%-9oeLZ$u*Lbi^YH z^OiEwAK^)OX9$~heo5a=QVbJ{5%G56__foPF2xp!fKlq*~d-$SItDez)n`}2B;g9pDw^P2?ORCUuh6Vv0_ zgTX}N-;2g#2di04mfH;GJACF)WnmkN^dC3r)6mmGN>w{?KV<^E|2E--V^4SegkXc4 z+ikoUcp-p#^=#sshd4mGc=OKCyD~2uGHwHkS4Okw^FN0zp)qucGJDe&?Y%Vm0}%vQ zh40=o0zIvacs_Ho{*=II$H+T2E7nUsh*~6r`v9^ovI49x`bIAU^@)=0| zzYBCgoL9J*TEBVW<3<1p6~;fRt|qc$KmQ(#$|YCsZguGEe8WyFJS$g)?%fcS!Q z6U?2NgUx&M9eBsJo6~J4rap;A7eT+FU9A9OG4sT^+upZj4e7be`w6des{QUSy0W-_ za3OLQ3_LAqRnxJ9I8{{7CS)x`8+IknkJYDa-$db+Fa;_!tz-|$QnZT@=i)+}p4WoN zhXNu)M+svFxwGvK0w_d2e7Mn3wbGd=!W@((*fJkN9gw-Q;i)Bw(nU!KN$6>iuS_#t zTcEKU5I@9Ae+I99jNz2l^^0a!>h7+*tmk$cX6WH6#E#2*jznHvxY8v4Q9_K{Oy*1s z%sTf%W{M4NhX%`95ib&|9vk=HX(03By}0`R9ygD- zT*{!_u3FxRjT4}F4#eL&Py@wC_Kf^mN|Cy|%9|1&wm5zWHE0N{_RUM94yfp%CPE6i z1(7RJU6b}@w3q5SX`Cakn>CBs`p`Ywtay=L&E?$Y2f@dWR}sgCU-+$bx$GO+$bxma zxQGO{raNaA%>Ywn$C{O1F!htn2XSDtt6I5ZsMZp^ib{?D+DV@ECEYD zk>^>4`%HG{<{}|Bdek;jJz^Q!p4KZQISjRmN}F;`1q8B47;7k2XCzKBB*rK3sAUj- z1^1Ov^V+`O{$tP)Ih0k}#Af^D*c{`+u3SBf8#TU<;M~&Rq%A}A@JeVWZ($so7xt#= z@ZhS~qavRua0PCbM#xLn-&l|qOP__WV zh|KYa!FhAGna#_~J;u`Ado1_r399VPnlCzQON5*EhcoBi7!bRm`Dk}vh>%%sv0{7x zDP46s-k6LHE6nYI^f+oBvAK)o8=ux`eR?W<-L&iuquSGM%-ebOk1Do)ZN}@1}WoNts z$pHGcV3QwvZdorCmAh!<@9r>$h@E;q*|e4)cqT5xc=ESg__q^Sq%!qfhB_Ro6VS=1 z19|4uNfX*#U-M9ixjPN^W{N*4vB=u%`sA#A&EurXIDR!jD9Zz6n1+dxPkXM{Q5be0 z8LcgDLqG8~)XFLmq25WE)9WB-s+D;Qw@49|01EUzA?LlLg+NOd6i@n%Y7eX^Tykza zYj^R6)nIZ{EJK{J*$+kBlS*Zh5XWw5kFIiBZ!CaUR( ziJ}=^>pVrCpzS^(K6~h)|8YXE=2D`R%4}Se6E>#6&%~7{5R-b9-b{M+|Nivu6DH*f zt~P;W2FJkxn;9o6Z))dy1`uHt5(d{V1BH}hsi>MP6WUrq5m|=xATNh!=Ve1 z|0hc8@2;4w0=isLn(!<8yRld_3H_y!9I7VCSBtwRm}*{;ZA(O|W6-JlsF(d?F|oE~ z5PguW%I%#8;R-*eod+&=3JbSy5~E+9e-tBra2v}dF6Kp01AA^9mceE$k4NME7?*u= zL%|hVEiz{+q?mp|{%g^*`j0IxHUwO~&m^I|re-Vq^D3W$J9LhB(SuHh!kIGXTQAMO zerDT2ZstX#BHy5JHkeOB5@sQ7uEw;IUyZq{T1?c{WcKv7EY{O*pX;7! zXF4f!1A-Tc^6}AU>1V}P<}2q*PN3q&|LN_zt7-W6`2kyHK?mMO#i2{@RI;ZZjh-L^ z>WN3(Q@NE-RM1pO@5i`TaOLK$$+vibMwa^AQRe>*MS-tMcAqKHFU}^FQ4u8DB^)+ZOtj zhY)UTyjZx3Upo=S7> z=yCI=AGj|DJTW&@I@BI+Da(oM#TCW$$=gT++?ip0g2 z{v#vg7-%te^^)-gXJTJTvEznv6Xu{uMq;_d{mm2Ce0ubn*Y3%6gdcOS`z#}&s9pej|Hq7IFI(=fM5i0%H>sxAboNt!T1rq|wtbH+aY%k+52(to&I97) zLGtPw<7am({>ua2Drc(cL?u3CU7pb&Eqk*(V-~i0+!=+bB&33REZn|w-#z#%>!^qz zIypWc`Ch9?bgZR`*&T+Y2>Q;K1g_eI{_Dh28kX1zyE6NS0Rls=x7k#abyjM1oFDL! zOKucb5hu&^YV3#=|GOL+FxH5roWC<}%qqRl)Orpb)wblO?iP1T(Fmf1*^mhuiJa}8 z_BbD9(fWv~@wKz*xVXAo(9PlCSKKMcOULP1`1az`s+PWqt8V-cU8etz<8Ap?Riy?!PF;+& z9z1ljQa}G&xe8cO?gwu#NWfgO-YeP0y9#J+jE#)8@-eZ8RUWjRr0%0jB2$1V;8iKT z_Gqoj8-LTdY}x*a0s4P)7-I@&V?xzmk7XKzH(R%zFPHq?mYM_B;=;AN0 zrMhrudNz+_Q0AUn^bNe!uR4MZ9~kcvS2eJf79Mu8&>EA(*WO^{&6VB#dz(w43Y8dc^_A=CMBN?5i{tjj zFmkNXy;~P9ICu2mbSYMy%K5gM@O)7Dp!2s3#~TKR9%3KA>_jTXZpEVKM!x+dZ+=02dTxhF>oUmU@q*PX zW!$U+%PIoRKA06ZBtAZv_iL$%s3lb(s-xR>t?};}kg`T))8Dk#Go&oxGL=N?bMus% zlDluI(H}=#uPsLaphO(&3R`OvSqv=BQp%pEkuO5rCdc>oPI%qroIihym9Hae4k4g_ z$gB{_D?m{Hsgv@O8gIQDjH)w4Kn_3%@dKE-53!5OKHUm@Yx$7GIo)Y=z?f~f`A6eM zfC#6|5|CB_44vLzI)ZNnY40LUya679xc+YhJ852D+%mvRy|t_6ezr%e{q*90dl-W3 zH5QB-4Q7s|1hha;0S>S=z`EeL!pxsx1@q#hs}koX3--gY>e1o`DEx5=o^zMyh7&an zcJHX{pktK$f*Oe)#Wxvx*a0lW1n`SA$-jXDrNE%E7*V}>Il`eTro4;F9;_$u_b7?3 z0?D=8U`z2s!g*@1hdPnTXQQsLwAqAyYK#P$AcgrnBcD)}>>YX_(P z8wqIu$FL_$-r;XCwZmJiU8og0g&w4LKX8Sz4flHHGwvL*pgTbV!z77yOUuri$K^kN zS!T5IS@S;{OY3urW)UEy6Ce2QotQ;@w<*OY`Er$>M2Nd5&)iStXlL)44N{I$Nv0R_ z7-AbW57y=$lM!*+|GQ*4hzDZ0NYtb|G1ydFUxWZUV_sGGA`tRB7e&!FPoGpKpR zoq}%S5`m*MwAY+bvH@z5I$F1{Z4aAv6fFO&11}m7m~3C#@Cn32ErW_c1l2$}NJS-9 z9RRQ@&i%JvqWl<^el30-s2XUJl02y0Rv~uM*aN&VKTM9o}R|589)jfKWe= z^S8JR0vVc~dbBH!fh2NM<0VEnMF9bE_;Y&fb8A`~B%~meSquoI>TcVl3>6qcFI|n| ztv{Hwr)J%(n|`8v?Y#HK0G{hdyXa6`+J(xWuG#<*T?L3J#U;M(|02W)p1u(uR;+S! zci6|%TWaET@RQ5uH#^4F;v)7oU8e!GdT6_e_)ff+8U9SUD$u)Vsg^ZBzwbcd*PnEw z^R9NRr~a>F^B+w3kFn=byDOU2@{#Cd}qpPivLt#GhB>o$_m8sNqIC)ZZ3k%^NLy*_yg?A z)@ST$WQ|(Lo(Uhjz!-h(*Q*(oZj(VyzpB7J$ZE#(vmUfUMN7+eL)_VC%DU-!&p*9o z_Llch7+H00sr)nUO7!^(NMUZsYaRas3*O2y6pDOF?Qp^RbVT=>vvmzKU6lxRw-Rkn zdnF?aTk|swI*L34Gw#&Powr4O@+O1wkvevabJM=<>FVn z!b)U4uCT+Gi>S7I)7MUk-E)bT+%dt#tpafQexa}Cf#nRxApOg}Q)4EN!M((n@9aqV zyFkdqtUy=gBWwTZcgw#3>FkA6G4_8C7bYcv_1VZyBEK_JW$c~kN}^@tub*Cb{QWOr zLR-bzCLD}9pbgkB)o`V9T(!rf{3buJR=@3WrGF$pnT#LZhto>W@UwV+<6jcRg5=tb z)$;O`?8!XkOmM6cy7wWhoZ`eTFYP`uyIRn3@=G+hl$1+(G#@oI)z%)J}|3We;)ipxu93~bV@A==e~FS2m`E{_4P zuC2R{cK+G<>!DP#3o#sG7)neSf~=s~`h*ql|8E0d#*z~ZEh_nJ^N~W$+5SOm)wM^o zH||G@AAJk7*!lp2I=+JV@Btj->)o}vBl$ht?^FVuZ+*krq#LVTvV0zZWfhA?znbp8 z@Vp#PQ>aIC_CK&ggAyG(G@fYq7XyO?02HA6xGTE!LbkmY;$wu$b0TxKEB&r_c{CAL z&lv@hP>W|PNj97C!@7-gyYKwBEruBw9_83can=|SV!?z$J8-C~12k)Dd%mLt+;-YF z)=rQ2lrJlG)g}l!Sy^jT&Xk4Ny!0Cr>18suzW$HWWB1PoM$$^#ke!F+wzN4+`ofM{ zc)=(eAoLifxUg%j$NhT~@+icxX7T1PfZ0l}C6ywy62dI$VVRSz;P@FDuuCVAiTk5p zxbziN0+ujLpb`m}rH~E5D6f*VXFnPapfV!!{yi#%1YtNSB5%8{-1$coGHGZ-tzf7f zz~bW@cXQZZU zs~$2@%e*N|`}#&P#$DgI21$SYxd`i*UmH1qa#P{|%7zA3!W#To1H00-BC#TqceSu4 z76&jEcmi%MM{t;`%#sZ?{5ku63v@NrFmfjR@3Y6@L8MU5EgIE89wAH|PE?dPIqB1j zAUHC2zkLFk8D9JWm>rdX7*IMT>;`r45n*Nq0{$H7+!iGAXJYho`mA{4e@WM8O!$O8 zOxJjJjEicSpqV~^uzyMQk^fEkQF8ygMD>u*wZC)Ssu}-OXj&Ng@Aa=CuYc`VgCQ4& z9r9}P;{6{IqZ8e}BXvKAz2f`ZfBzR95`*Y6i*6HLlf%eMeG=XjPj;`fpuYl1R#?e9C1J=KS6@BB!!PjAOFEQi7j?e~-a8lOqjakQ=fdDh$QM~x~RH$Ns zq=dw?QU4#77mHdd5r`Ft*H^-T0YtuoS0HDqt94n^f)DF?vXB&$!+*aw@nxmjEdy_i zL>k}~TTX7SzQqPe`{*|2h=@N7NN9wH3yoCw4|lTBpWbob?Cs0-?vHk$OQ=e(flN^EROJ1 z2-X^WKyxw$6az)+jLV2>?OJZiBw)AHKWuy};oxM_5fK;=Gv3ebC)qR5 za9!Ta|5d?pq0e$&w|;4m`#<5qI_l4ELd#X+o?-!i8PN%bZ6H}g9bhJ!@xy}*uifWq z5EN^Hshxt{W>~GL=|P8&G(M0I70j z%azl%zVZ4GJyvggWYqnW_zL8qOl@zQ(m=mF<*}M(1IT-&WwZ$ERoTobMDPfvPWXmx z$Tfi|+e#7sRa*E%S05xTKs3QYoi{}EfQwN#Ye5NTRHRqZ?t`?H*MM?Y!1l^-P{#q| z=Q>FDu(FF)8tZzDY@wUAPL??u8t29xIcc4j1_f!Md}6V_^h!~Ilhmd;ub%y>n)rzo z3aTfC?(@J2uDUC}ogcJ+K2rUp?CGN?A-Not;LqwGY;$bBQy?g!R#6jC zK$9n-xZFFxBNFbDehdy#{lXnE;BhHW)~g+xE$H|~ufNXUyTBNUBc->CX#R!cZYZF@T zFfT0VIHXrp1wS#r84NEhCh#tq1%YDcqUw^azKJzgg@1xnZv!%2vSWWA(GGx5N{X3! ze^p!zA9-7l{y&Y*Rhs*+^Pc zi`?H^(HT9d2fnz&OI{$_SaBrO@3vG=Xs1?F20o4OAVM}TO?9NoiKX*n4$&@{H!kBl z+;y&iTdK{Oy`5uEKB+FuMz^~ymc8FT-D|f?cbEGk$KOWd16IFPsvw z!@K{Z4@po0tGpyv;uYhu82k`|E<{6*wr-0{v%+b<{TrS&P*c~m$$GS^9VYlo1ltF# z?ztJxo?*Qv5ZjjeN=OEz)r<0Zyu2&2VCNa|8iW_MIbXeM3{aH{Xg8h~Ga@qD;`ppc z5Dp;`Hj@MOR-_n*C8rAuy}FRvptUH#NTuN@2<|ne<62` z1m2H?=!`)&3dCeqNDq*cZQvf&n9T>y4J2s68RpdFcu)%$O(W_mO|G)q;lK3A3c0~m zNOXNzWHVt-HKM%$Z@!?p))~Ul7dc@`dJZOa4wt8h+^|*{xv&MHxDFnJ*PQ{{H4vx;D%lFS%1JepyY=Fi z<-%2|4QqpM5&NGaTZ8@whwk6CGS|f*#@DybzQOc@2t)Xhg~S5rAvd9ij==sJbmy=Z z7kxI22qq)+1SP0pSwVBK)Hmo9QlHv*!dR^iPJ_s5T%*A-TrGDxQ4+k4fq@9E?`_GY zjvq}v2T?C)z*qq`yz@1})<$#K*ENV{Ic>@{=I>j8b%}&>hEld55@{5GPS83e)^Y{& zJz4tfbOGPtx2mi z!kb^T)h}o5{sbAf+^`yNx*_wP5SgWpBu@r=I0 zVC99bIz*AY1skyvw5OBcQ)36{Tm=X#KHTq5rHptjzVm_P6NBFW_sp-U5q%c+cO5qw zR}PY|18S2BCr)D0Us^sZE zNc?eNPZXm?bN}0^OL2Zp@heyAa7g@E)a+j*20K6mZmf&0pxXFVfBYU8w|mt zB!1n%-bE|xw5{wW@H zK~M`79)-3+GX`($YjVy3k}AO`Ge9l$uSk_L2OX6gJUXgs1bVE z;~wAkJ=-wrF#a5AQ;a8{iNaP%7ZVwJkQC3VVXOEr)f#}KveIxtrwQvzoD?Xw5tyxR z=#AnO9Cu3u)FPyTJ<|E@%l(N;Yr*SG(*uo>c=r(2^??Ffs5Z3u)h? zx1WG=(U$g>OF1ZpOYdL&{_t-|fiyJMZ-pioTdnT7)HJpjG>%z3WV+BVi!XB30c?rY z-%Wx-q-Mr_F>0(xzpQ$1FbZB5CwAtR?_S@{5(v(<{kJym!k!N#ZKbj=wg5mSf{y^H z_x^^(C#vvPjY;%9m|ierxRYBu%A|gE+59@BCjRS7dB{Q-5p%m|ctIhb?;kJw*`@|@ zC>x55XCam>mAH^{++xI7BdP~LJ5cHf^8CNKk7xrlxEetN)m4hzP@Sj$#V0@HK>6i! z$z^I_H9_r({U{GGAlX4yI;U22>%yPWe86J~%D&4&E^k_lQt|!iaid4#??9pvNg(?7 zPDlQR05;;Dw25`NR66!I+M$FGt&0UIX$RkjnB66zxod1h$^*(! zKaX}Ky8x?A0fBP3B!mE`TyQ8hWcgQ@5~2VdAe?y%HdyzuGii}_%S|vi<*$AYmV~tc zQ%jG*kjZa@A;ZYD+>U}iP!`L~yK~ClMQ;7ic<3|(EUpPy|#^2IeJ0RYJ6H{dBGjz-nCnu88tk~PSCBff4JMFst33RuQo zJ0C+xk_E?&N8lQWdnp$G{J*$_a6yeX*CildUtn^ALpB9MQ@g}eg}=0DHFaXs*HgXLk@w6F7w3K5_nOA-}@ z5Y(~oh19MelvXu3g7%H3G+u-PS3qU@lc%Qv5q|}1uQ8Rb0gS#0^1YkbA@b8*50c?q zN+XeGa3-&N^_7(M-WCTjO+j9%zQZfSYhD~hHryF^;&nyPn|5FX;Poco{WRaVnoYZw zs-+?-An2bD%G;gQ#`*iY{f$Xp$R_{Y$*@rvFhrRBoa&*tV3p{vcPevV?o2jzPK1et zuRFUYM#=*IW&|flMj&xxM)Mx(s4{x&h4dAp(w6F?rc+zt?nTpkF}~6_$zzA%x(Ytd zinsfN+n9Ax%cF_T7Ydm%5DsD#R22RVkU5I*I)*o#VInr_$(gmu^0ML^P2C@(US4rx zF(_mE@4DL(V>?P14J-b4*2s(;=8s4bC<=PU5ZH`w^NKFJ4~jyLh2ZUizVhg8fMS%; z=AGcD@Kj0I>^>`AtaSFT>aqm#troyog?G1q{|kwH%#eNb-jW+7`g`N$ivWqe1tEB$ zBN|sC+_@~R^Q{Yjto-aH$8Sn#kVj@Hf=BMNrBwyA*MRi@BvR0S~1^=aB5u4kw=>^2O%!@{M8Z_ z7wu125Fia$b12`yor(OMXAm9l$CNzbX2QUN2^!(~vZHa?tbTXarW>3~0LJosWH~s( z&mJYsmjCx~YskaNF2Yjkl0(D53R}MZICF2u@ClRAS=LR)Kd9-qI`l3JL;AwmFB68{ z_k$lWB1;^PbYfC6nvnaGqA09RQf29#IQoCm8*Fr6RGRwvQB5)Q!OOhjEvaU>&`nNjVCzb*2o)L#;*- zUxd1J(G#7nfH0ia&`@C9SRzh z`M%@oN~1WK{l>}xK*v_~wO(@-e}}TtN;YZk{(C6-kr0gJZt`~+jyD>jl}Fxw`ThPH z{VtQ;7fgCD^J2$*LIMo!T$KvQ@;l$~7Y6^022wf5?!EGoIIL9(BiZ@xBnGfPbQ|x( z=+VuNpdI00nC4UFGZ+MWHFlU{Kd|ucsTgGh)bUnzoZi1XtQN(~z6B?nvaH?I;AwI8 zVm#_e+XY2|VX707yuXAZP9Vgv_U~+?`~$E_via`qNyje?-)&@S$k;o*R(OPiWv(>| z7te=xRM3*TGrFUsjAj#enGQ0{V920NO@r?JJDwPz=`2UPbw@W66P;d+o!fD5*j+5D zP@dmE|4u>6n=U(-R8fpM-}Cn>O3tB1EXcJX^$B)fFNKn|PBDtY_C&APQRkgp<{-AY zR$~`yO3>4q!6_}gUkS{~_rA;5z=f811zAS+gT_f?M~u?v8pe}`vOmusNJ=#_ZXgc| z3F(4twC|wiX$$CUt^Ds4tWe)StI$VBPScCW7{fI8X$ttc^e~3}uqh}!W@&BZr*bi# z6w4k6IM`DLD!U+fw&%1Z0kLa$toTTF?0rq=KWftu{#9dkm6^h>GDcStmalPSgw zWCMo^bvcnsREB=6`dn#if+y(Uo>n;an=*MrELZN8TWTpneU}L6jC+yE5=iE4iw)K0 zkXs4W>pK{ljzPH3Kfn<58fc)qT3hQlH3_Mo70>&9Et$aiwf7P}Dk|HSEj~zc%93FG z>fkJ$eg`V?^nB`HZ+(#L67u0;gs6^NR{W{1+u~AY2FRh?$y#2h8{|djl}R^j?w5E8 zkOS)zihvDBDq{$q`&-aKT!bJ#S*J*b;^ZHCI0vG(O{kre_A$3qM*lcz`QDR-S8{XO zspEs_V*H-Ol6b3us*7x*80d2yVGMP8)AVm~|JBt_kndMQs;oc1jbMh*1<=D2EG*)^ z?+z0ktk-2{@&^DoO8Ly3oE{tO)C4$dQXH$NhA6U{8t%wym}V4w@?dDqvn z=@g&!mVMN$acLR2MRYpVHOXqR^Xy>5x{0eXH9w?~2 zuaQ_FrVMJll;WP%j^%BaV2~Eb5%_;8J){>SU64hK9R@B?c;|F4^+aLrSVA!O9O=CH z%O!-2!~tf-^O7}rHsfiFleeAPK3+; z+oZVrJXc-Uho-FOt$<6>A0+XWY9od?S#FXS-ub*0G^36Vi9CRA_h&u8+r~nmE9f<9 zt(|;O#JimW&g#N3WE;>tfxC(cJ@bf|yyN(L2Efw25H#s!-5Vg?k7~=u96mq$=}Ta? z)^!dYo(KZZ=zvTDW5az6AL)QdqNy*efg<@Uq)Wth$mj>Cvt}INQoAYO_2n{SQMMOl zuhvhHDGT6*ul6R9?8-WC*R`BoAL1S+n)O(vMcgm8fMgb$bcO#4_2&z;njD2p%xM7W z&*^!69oJC((li1DU0a39unzcg$)%6<57w{mBB4?{S2n!`DFo^hkDt zT>&^FwgdP77v=%x?uaJJzS=0u2?=5lGBqrMSY$l-1}^-RM$oc1Q9_jfr-|B@i=P4L zN14u^Zl8W<_5#MwZNNLp(wQX+w|g0FD~&HC`=mPx_}s-K6!t|V!uz&8Zwx(4pLuL* zlh-1K5zyvOHY*yWw=P48RYq>?B<)Ur^m(Mk*LMm~71;)_&kS+;vp}JAJi=d#xwU=` zIwrJks~~i7;h1@s=Y5{%``Ny4 z1^rKby$UtRz3yvm*U=Z2jSokXy}%C1*&@I&hN!Brk<;N{EkagdNTzUezr91@>QbDJ zMU!)UUqFEnb4+Bq5qgRQfY^bV8zNKuu&fAP8*^j@cASrapN;k4YC~_Jmb2OCob&pe zo(0U2F8t)o7kR8bvks9XaZ&1LrBCDt$NGF70_sDF8Z_26RfQ5`z-KJiFjJf&4)rFL z{(#aE^)s7?4q>!lZ*`E(;rkr89Gv0jO2mOYk!kp$&;Qt&oH^hxNIMRVqI66bo1-tL zC36%lz*Z|zmpUHZ6wrn?5y-Kw7vp3YqfP}={kyKtQ(>Pw2Rac8wo0(Pa=wyjgW2lE z68#J!E?eLe^Tobygi^2PK=QS;PyAzH;jUO2lMsU90{?={g6e_})rD#~78J3vKWWLT zXZ z-hZcxFjrF-BZfm+C03VV_4&%Jss-GfKwu66boQ7X{FJ=)+{Hpflp*BDSALe4_|$?5 zN$gQGZZ2+i97)z=ZE+%BxQ`_87GVBg6PgD5rLzV?1&{jA6C!^3AN2>gnrTk-$D=te z)P#OLRH9c67!6&>@lc29;=Hkii0rV%N$53~mj+a>F&(jZJ*ux$b&|DUUCCl7%)T)X z+dnMab{E)C^BL1>mZv@!+lzl}90MjrLQpV4H`sigDm5-$JZ3UI)KQfSkpO>6PMn_6 zBw)l`iEHRziwU|+`8SdUnV3%M3@*~gtO`x|yjFe3wQvQB7Cr(3`L|M%J9x~m0#6vd z@?jRG#D+^Cf#qf3FMhE02ig>XDM?QBRCam8*I|j4A zP+wW*?s^cX#I2&DcE7pCWII1`=rYiR#~PAw&ejiBpG)sM)6XqEx@>iZTlm2)gdk(U zz*aEoN=8}cc?PcA8@;;gjX@QteQQ7a??o`l)Y*ZN-d#XTNv0HtL48Ztyd&e`bvXQ! zAV;pB!7JYcZ==-)?DNbQXWPOR)^S{$c!9YakM!w~1Zv(++$1sM*)R&ztBPkUz%4vu zo#2054+#QlehM*8RWUP3bk}{q%nUq1mng>!!@y@rS7kcSQp%O~(t7|Orry*^b#Y8k z>w2S@kRHa;#r0Ov+PF3GZxNDSWes%`w}oAzP(%yoj3{3^@gvQ#J09!5SEgH??XH<2 z1jPlS#u<-(r<%<_{-UVrUb$avciR5z8ny z#T%CLn{pWyVm3xhT2DL(WK}4CcAL7@S>94OwUCRp53LFu(7GpABy_rO}+Qi5s8T%Y}W^nJ+l71|;C;Wh| zS=eSF`#U*C%HPX9hVS<-?wRtM{LC+A)5RLaZ=%ljqK~lJK7z)L%2!YOwrfCFd?#Y zGjzbjIx;08FP>eRfs=l2w*hwxf4?kN!}xFlIr%$VD(*#t<7mujVGG}!AaZ7ksK06t z#%Q)z8-FfMX67jvrNlikB}$9MT73tOnH;^N(6UEJ;H#l#k6SkznXVwL)h1cVUNkyV zP_%irH&l-$J%ixH&wUWD2#H*U_XuH*5r>*xD|ZI1Q*k#hiMMF@Yj>w>oWtE9awrlM zZ5u~Vgs~STlTChp@jF_i8zd8Xtkh$ya5H`Me7IR$`Wsb`VfiJ|#Txo3;wQ$w=7D_$ znXTW1Fv&AKl*?m$%eQnorwC9j2PF@5EXgY>q@8VH6xIwmYLihs}fS!qipJZLqBVs=RdNjY^d+iLcH>{f53n%h|mw zxv>z$As=|TT=slrzIpS`yUW-;#>yt3bn-7Git^%LTpwn19{v!wm@pzLWG?&#o3c`W zFN0@~g05TbBu^(-2h6jhiN5BGS46FMlU*jH1}Tyv?os%6L7M&O4T0C_sA6Vc@u`{g z&nZVEmM!Sa9qV+{*X5{qjz_^f#KpaIN;7wo4}aH=YM>IMj_~XdA;@L9YvmMp?Tv_4 zZkS#E=G#Yk&B~jjJt*#!#^YD^(~^58eYQQw;JX&#@a?J+)~L`Cg(|JF1K|m42b!|) zMi%a?n>yS2L2&`I-~-*`4IFWYtsl&cP5qP+bJdQ#cwt)&5Y-}GO-dA7+ZyTE8Q@?O zg6`soLbRf6*8-)@WEKdmyLU)S$NH5yeN_G%+2W(^|_<`zQQqo?^(sPDZS!l z;p%wlRXYCyBFUly5~6&Jqtx=}^gWJ!GM=>?2)lEx)mkcAGwWR32CAuOr*ke3I+IIF z8Lv(eb*dB$p2G|8tlRNvl>FW}qRpi%zhjPokMXw`*ep7;3ro-9%cY`?O}WWZ z7iqhLOV9fC%~=?Yar3}dev6Vc!=Rb;H^#}CGVh| zq9b3CAGg3cHGNW*MwW3P01kts6q=sp{-DiKy|?#M&I8)i!z*==Zqg_|ti66lJbiGZ zk!K$bC#uy4oXFGCHpkdFGU^f71oeIa6!O*4YxAW|_N!zy8aTG`YIhk;Qs$NhXOme} z+K`wQ=^-f)TkH2tzq7crb=}c#p1lEzIOpboy0u@9`}$vY5F=LY?_U8?a(x8KreGUg z8itXBSFP)LX3zdC<;&a~ztb1B+xuJB&-r=7ry>p->EX{)8hd5?d063UtWdYD+bR3u zYLFA_dgO2nTzX;s`G?wAi!~jO<=F&EK7F%(-Y)?Dn*><1?O5x#yJe%jdn1os0vf9K zML7kznh?EV3a801r*7+jU{t2iGpiU$Iy4WU|L8h1-q~GKStXg$*Pi@lLK&nYL2_4p z6KPlTIx^Ia9+W1%pVlQ{k@mnS2m zybK33NXomPu7m?Pp(J-;!Fec%wtV`+x$g>OE&Ex&cbu_q^iD|{oPju;AQ05Nq>z5< zdWrg~mL-OK5OGK~=oN+hzU+{V3lB&a9DV;@7~Vpy(D7%zi! z*%i%ud!TfIE@kf_k|patgKgsWjj~_|kW9{-|5OPRiuwHVZGvz-2Ll-48(6;Aae`J@?nQPH!s`REvxA)b)qz zx{m1=tg<#QI!e*-heqTGukn;@M2n;N*$UQe#*)wBzPtM1#b$q+=97YU?h~teI{A&y zH{Hr~dFF`UufAumsN_AF8SYy(b_h)?kz$ANK8fe!c5~3P6zR-VH)yWs*}k*plMT=* zb*Ol(ib33})v?SAIftW{H#YuvYt7dHE!_IG9Dai-m1E4K^dPueGd# zGSwSry4=YOd0#I-cF$}gk$AqM-F`s;@oqv5H&y(WUXHf2mq5}8Qv=Gg?wn@e$_u3^ zEG8cJKbF*|bGoNO3+~SoP-$4*3JY4D4RvSlDpGl@b>}_}>AwSyqu-q1(Eb3Xv{%)$ z>Jk3Af$Gccx&b`ysi)Y|2LY&!9iINP%65KhY>7&l`2b9cZPAJQS=%(#I1I@vyottO zJmTUF%d^*4I=7FWA(*@NQ#at#?YkT#RTBEoEp0KW1xxJK0^^Fze=Z-26E2_Yz>cz5 z+K`FlYbvS(g&=!#0>!&y?ot{UhAxpGz%%91auK>RE?a#Srx6r-sR%hUB~nhl5KlM8 zyPt9@D>E(Qct#VH8}3M&7Wj@w!+}tat+xF0)By(o>%2g;Q4K9$jeQ2f(I`Ch_<@L5 zZBJujcC?QHL_Z>lQSxg6B{a+xAbjKlH1-zD`Z0Jvv7HpCC2c^dKd?*Q-U$(QLjTx3 z?w;9(;^pUG(ZoD#QEYMFSz9DN0NLgV#-3qFF7N;;=}}NSxO$N4N?}XaclllAgQ4L4 zly4{j2qmleM@0;lYD8`^x_s&qvngGak%0?YljrFgE`i+tz(f7H-gU@Gy@lwFnzK{o>Em2q)*x#vAN9x_C9$qw&T*9ha=GH z)Dz4cPk=&BC`OZ>czJ+?Kb*9Qm(`;|n*V>Uh_w6yse~~l&n`Rip@~S+L*B>Pk;fNN zY=s7A1A_aa)&79!Sw%Glk}*>|kdB>*icjSZ7z_@PtQ(?DMV)az+c;a&bf_bq*zVCAei0>G>8)`NCn&Z04 zifn~wVE?xMLJ}z^N}t-iYe4IXFYfTS;>muqv=!*2`4jv=%+0>{y`Iz{(JpuWH}MJT zfs5nSw*-|tKnlQ5mV`NHFDHe5iZc+|Iuyut z!BLFV0xQzkZS9r!d8xgo7v0p~fEZ3-kMtuXRPQxj5t8e1dtJRHV2M*c+fe)4#f8-B z??HJYaKF;hc+#S2MmSje98UJfXOP9ceQ(b?2k7lVfqdfn>(5}l8GUabxe59UcIg^K zl8c0xNp^3}tY z=_O2pFfY@iz*WcKzgo4PK%KdQgvTNB0igLgqkV(xfpY%+P9Un)g|WG`uy+W{!c-s~ zHxO1v?mQ^@v&Qa2a?%8dIo=>nEe;9?kNP;BkIO&@juUZt#kK+Uge@QF`g!9Y@*M`C z<2S2k*xhNvIr0Z=;gY!0xX)c--CCZ zs?pi<9j?2obFt3RpvEB}35rG^{`!xVTf*`za%6T9WvZto7eQ<@0uPTj_}I`JK7O4LxUmlj6uyApVb_Bh(q~)GuNnLB50JygJx75fEt>gqD;~o-+kK@4wT4WfyV;e=L;F2kk`&i}!FDMDF?=Ra1TKP^@H$ z?X@<$uXcc~kuAQxR?C=$D9ic3dn6l-4D80Fp(aoC8Yq@Mp^GFUo@P$(HCG#HI|T_c zSk@Y1pBaely_DVikQtWQ3zPH;iO~A@1wthDgyNx3m%VneA({ec2{~Pj3x-Y4tqKsi zoR*cNoDK&>Hoi3zLJdBE%-$QCo%V%ddbUlia1jtDNgUyOuX|fbaYi*Ed_AyuoE9FM z-~>g&m>@2(z49vrR^C^9gz|C*4ZSBwX@)@_{(i`sA7s=24!o`da5(kT6@#XcbRG=1 zMYTx?tnb?V9UdHq+2M*9HN{-=L&^-{kQ#Ys0cI4S@EsUL#D)*8J3~d>*_+dkMBl*^AJ{OAUs2 zo}k;g4&>nnA@|2Dx2h0iv-!^}F&h>Xi3KNBg&ZdQ!lA2QZCpPZsi}naojePxM~hYy zGJ^3%!==_T>PddhXfJVp77zm3{b~E>-5SeHLWfn8Vpi`eGFF#$9iJ6XEmZ^NT)>vBR(fPxESiRANPaQZ9A{~4kGqy0QG;oUk EKkGi}SpWb4 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/progress-update.png b/design/unified-repo-and-kopia-integration/progress-update.png new file mode 100644 index 0000000000000000000000000000000000000000..33305332b6ea9d1d53f2b6895eec1a3bcb3952ca GIT binary patch literal 57729 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/scope.png b/design/unified-repo-and-kopia-integration/scope.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff97da03fde12bc203bdc160759976078e27d9a GIT binary patch literal 32284 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md new file mode 100644 index 0000000000..4df9d4265e --- /dev/null +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -0,0 +1,320 @@ +# Unified Repository & Kopia Integration Design + +## Glossary & Abbreviation + +**BR**: Backup & Restore +**Backup Storage**: The storage that meets BR requirements, for example, scalable, durable, cost-effective, etc., therefore, Backup Storage is usually implemented as Object storage or File System storage, it may be on-premise or in cloud. Backup Storage is not BR specific necessarily, so it usually doesn’t provide most of the BR related features. On the other hand, storage vendors may provide BR specific storages that include some BR features like deduplication, compression, encryption, etc. For a standalone BR solution (i.e. Velero), the Backup Storage is not part of the solution, it is provided by users, so the BR solution should not assume the BR related features are always available from the Backup Storage. +**Backup Repository**: Backup repository is layered between BR data movers and Backup Storage to provide BR related features. Backup Repository is a part of BR solution, so generally, BR solution by default leverages the Backup Repository to provide the features because Backup Repository is always available; when Backup Storage provides duplicated features, and the latter is more beneficial (i.e., performance is better), BR solution should have the ability to opt to use the Backup Storage’s implementation. +**Data Mover**: The BR module to read/write data from/to workloads, the aim is to eliminate the differences of workloads. +**TCO**: Total Cost of Ownership. This is a general criteria for products/solutions, but also means a lot for BR solutions. For example, this means what kind of backup storage (and its cost) it requires, the retention policy of backup copies, the ways to remove backup data redundancy, etc. +**RTO**: Recovery Time Objective. This is the duration of time that users’ business can recover after a disaster. + +## Background + +As a Kubernetes BR solution, Velero is pursuing the capability to back up data from the volatile and limited production environment into the durable, heterogeneous and scalable backup storage. This relies on two parts: + +- Move data from various production workloads. The data mover has this role. Depending on the type of workload, Velero needs different data movers. For example, file system data mover, block data mover, and data movers for specific applications. At present, Velero supports moving file system data from PVs through Restic, which plays the role of the File System Data Mover. +- Persist data in backup storage. For a BR solution, this is the responsibility of the backup repository. Specifically, the backup repository is required to: + - Efficiently save data so as to reduce TCO. For example, deduplicate and compress the data before saving it + - Securely save data so as to meet security criteria. For example, encrypt the data on rest, make the data immutable after backup, and detect/protect from ransomware + - Efficiently retrieve data during restore so as to meet RTO. For example, restore a small unit of data or data associated with a small span of time + - Effectively manage data from all kinds of data movers in all kinds of backup storage. This means 2 things: first, apparently, backup storages are different from each other; second, some data movers may save quite different data from others, for example, some data movers save a portion of the logical object for each backup and need to visit and manage the portions as an entire logic object, aka. incremental backup. The backup repository needs to provide unified functionalities to eliminate the differences from the both ends + - Provide scalabilities so that users could assign resources (CPU, memory, network, etc.) in a flexible way to the backup repository since backup repository contains resource consuming modules + +At present, Velero provides some of these capabilities by leveraging Restic (e.g., deduplication and encryption on rest). This means that in addition to being a data mover for file system level data, Restic also plays the role of a backup repository, albeit one that is incomplete and limited: + +- Restic is an inseparable unit made up of a file system data mover and a repository. This means that the repository capabilities are only available for Restic file system backup. We cannot provide the same capabilities to other data movers using Restic. +- The backup storage Velero supports through our Restic backup path depends on the storage Restic supports. As a result, if there is a requirement to introduce backup storage that Restic doesn’t support, we have no way to make it. +- There is no way to enhance or extend the repository capabilities, because of the same reason – Restic is an inseparable unit, we cannot insert one or more customized layers to make the enhancements and extensions. + +Moreover, as reflected by user-reported issues, Restic seems to have many performance issues on both the file system data mover side and the repository side. + +On the other hand, based on a previous analysis and testing, we found that Kopia has better performance, with more features and more suitable to fulfill Velero’s repository targets (Kopia’s architecture divides modules more clearly according to their responsibilities, every module plays a complete role with clear interfaces. This makes it easier to take individual modules to Velero without losing critical functionalities). + +## Goals + +- Define a Unified Repository Interface that various data movers could interact with. This is for below purposes: + - All kinds of data movers acquire the same set of backup repository capabilities very easily + - Provide the possibility to plugin in different backup repositories/backup storages without affecting the upper layers + - Provide the possibility to plugin in modules between data mover and backup repository, so as to extend the repository capabilities + - Provide the possibility to scale the backup repository without affecting the upper layers +- Use Kopia repository to implement the Unified Repository +- Use Kopia uploader as the file system data mover for Pod Volume Backup +- Have Kopia uploader calling the Unified Repository Interface and save/retrieve data to/from the Unified Repository +- Use the existing logic or add new logic to manage the unified repository and Kopia uploader +- Preserve the legacy Restic path, this is for the consideration of backward compatibility + +## Non-Goals + +- The Unified Repository supports all kinds of data movers to save logic objects into it. How these logic objects are organized for a specific data mover (for example, how a volume’s block data is organized and represented by a unified repository object) should be included in the related data mover design. +- At present, Velero saves Kubernetes resources, backup metedata, debug logs separately. Eventually, we want to save them in the Unified Repository. How to organize these data into the Unified Repository should be included in a separate design. +- Kopia uploader could be used as a generic file system data mover. How it is integrated in other cases, for example, CSI file system mode BR, should be included in the related data mover design. +- The adanced modes of the Unified Repository, for example, backup repository/storage plugin, backup repository extension, etc. are not included in this design. We will have separate designs to cover them whenever necessary. + +## Architecture of Unified Repository + +Below shows the primary modules and their responsibilities: + +- Kopia uploader is used as a generic file system data mover, so it could move all file system data either from the production PV (as Velero’s Pod Volume Backup does), or from any kind of snapshot (i.e., CSI snapshot). +- Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. +- Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. +- A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. +- At present, there is only one kind of backup repository -- Kopia Repository. If a new backup repository/storage is required, we need to create a new Library as an adapter to the Unified Repository Interface +- At present, the Kopia Repository works as a single piece in the same process of the caller, in future, we may run its CABS into a dedicated process or node. +- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. + +The Unified Repository takes two kinds of data: +- Unified Repository Object: This is the user's logical data, for example, files/directories, blocks of a volume, data of a database, etc. +- Unified Repository Manifest: This could include all other data to maintain the object data, for example, snapshot information, etc. + +For Unified Repository Object/Manifest, a brief guidance to data movers are as below: +- Data movers treat the simple unit of data they recognize as an Object. For example, a file system data mover treats a file or a directory as an Object; a block data mover treats a volume as an Object +- Data movers don't need to care about the differences between full and incremental backups regarding the data organization. Data movers always have full views of their objects, if an object is partially written, they use the object writer's Seek function to skip the unchanged parts +- Unified Repository may divide the data movers' logical Object into sub-objects or slices, or append internal metadata, but they are transparent to data movers +- Every Object has an unified identifier, in order to retrieve the Object later, data movers need to save the identifiers into the snapshot information. The snapshot information is saved as a Manifest. +- Manifests could hold any kind of small piece data in a K-V manner. Inside the backup repository, these kinds of data may be processed differently from Object data, but it is transparent to data movers. +- A Manifest also has an unified identifier, the Unified Repository provides the capabilities to list all the Manifests or a specified Manifest by its identifier, or a specified Manifest by its name, or a set of Manifests by their labels. + +![A Unified Repository Architecture](unified-repo.png) + +Velero by default uses the Unified Repository for all kinds of data movement, it is also able to integrate with other data movement paths from any party, for any purpose. Details are concluded as below: + +- Built-in Data Path: this is the default data movement path, which uses Velero built-in data movers to backup/restore workloads, the data is written to/read from the Unified Repository. +- Data Mover Replacement: Any party could write its own data movers and plug them into Velero. Meanwhile, these plugin data movers could also write/read data to/from Velero’s Unified Repository so that these data movers could expose the same capabilities that provided by the Unified Repository. In order to do this, the data mover providers need to call the Unified Repository Interface from inside their plugin data movers. +- Data Path Replacement: Some vendors may already have their own data movers and backup repository and they want to replace Velero’s entire data path (including data movers and backup repository). In this case, the providers only need to implement their plugin data movers, all the things downwards are a black box to Velero and managed by providers themselves (including API call, data transport, installation, life cycle management, etc.). Therefore, this case is out of the scope of Unified Repository. +![A Scope](scope.png) + +# Detailed Design + +## The Unified Repository Interface +Below are the definitions of the Unified Repository Interface +``` +///BackupRepoService is used to initialize, open or maintain a backup repository +type BackupRepoService interface { + ///Create a backup repository or connect to an existing backup repository + ///repoOption: option to the backup repository and the underlying backup storage + ///createNew: indicates whether to create a new or connect to an existing backup repository + ///result: the backup repository specific output that could be used to open the backup repository later + Init(repoOption RepoOptions, createNew bool) (result map[string]string, err error) + + ///Open an backup repository that has been created/connected + ///config: options to open the backup repository and the underlying storage + Open(config map[string]string) (BackupRepo, error) + + ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance + ///config: options to open the backup repository and the underlying storage + Maintain(config map[string]string) error +} + +///BackupRepo provides the access to the backup repository +type BackupRepo interface { + ///Open an existing object for read + ///id: the object's unified identifier + OpenObject(id ID) (ObjectReader, error) + + ///Get a manifest data + GetManifest(id ID, mani *RepoManifest) error + + ///Get one or more manifest data that match the given labels + FindManifests(filter ManifestFilter) ([]*ManifestEntryMetadata, error) + + ///Create a new object and return the object's writer interface + ///return: A unified identifier of the object on success + NewObjectWriter(opt ObjectWriteOptions) ObjectWriter + + ///Save a manifest object + PutManifest(mani RepoManifest) (ID, error) + + ///Delete a manifest object + DeleteManifest(id ID) error + + ///Flush all the backup repository data + Flush() error + + ///Get the local time of the backup repository. It may be different from the time of the caller + Time() time.Time + + ///Close the backup repository + Close() error +} + +type ObjectReader interface { + io.ReadCloser + io.Seeker + + ///Length returns the logical size of the object + Length() int64 +} + +type ObjectWriter interface { + io.WriteCloser + + ///For some cases, i.e. block incremental, the object is not written sequentially + io.Seeker + + + ///Wait for the completion of the object write + ///Result returns the object's unified identifier after the write completes + Result() (ID, error) +} +``` + +Some data structure & constants used by the interfaces: +``` +type RepoOptions struct { + ///A repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + ///Backup repository password, if any + RepoPassword string + ///A custom path to save the repository's configuration, if any + ConfigFilePath string + ///Other repository specific options + GeneralOptions map[string]string + ///Storage specific options + StorageOptions map[string]string +} + +///ObjectWriteOptions defines the options when creating an object for write +type ObjectWriteOptions struct { + FullPath string ///Full logical path of the object + Description string ///A description of the object, could be empty + Prefix ID ///A prefix of the name used to save the object + AccessMode int ///OBJECT_DATA_ACCESS_* + BackupMode int ///OBJECT_DATA_BACKUP_* +} + +const ( + ///Below consts defines the access mode when creating an object for write + OBJECT_DATA_ACCESS_MODE_UNKNOWN int = 0 + OBJECT_DATA_ACCESS_MODE_FILE int = 1 + OBJECT_DATA_ACCESS_MODE_BLOCK int = 2 + + OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 + OBJECT_DATA_BACKUP_MODE_FULL int = 1 + OBJECT_DATA_BACKUP_MODE_INC int = 2 +) + +///ManifestEntryMetadata is the metadata describing one manifest data +type ManifestEntryMetadata struct { + ID ID ///The ID of the manifest data + Length int ///The data size of the manifest data + Labels map[string]string ///Labels saved together with the manifest data + ModTime time.Time ///Modified time of the manifest data +} + +type RepoManifest struct { + Payload interface{} ///The user data of manifest + Metadata *ManifestEntryMetadata ///The metadata data of manifest +} + +type ManifestFilter struct { + Labels map[string]string +} +``` + +## Workflow + +### Backup & Restore Workflow + +We preserve the bone of the existing BR workflow, that is: + +- Still use the Velero Server pod and PodVolumeBackup daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules +- Still use the Backup/Restore CR and BackupRepository CR (originally called ResticRepository CR) to drive the BR workflow + +The modules in gray color in below diagram are the existing modules and with no significant changes. +In the new design, we will have separate and independent modules/logics for backup repository and uploader (data mover), specifically: + +- Repository Provider provides functionalities to manage the backup repository. For example, initialize a repository, connect to a repository, manage the snapshots in the repository, maintain a repository, etc. +- Uploader Provider provides functionalities to run a backup or restore. + +The Repository Provider and Uploader Provider use an option called “Legacy” to choose the path --- Restic Repository vs. Unified Repository or Restic Uploader vs. Kopia Uploader. Specifically, if Legacy = true, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; if Legacy = true, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia to do the BR. +In order to manage Restic Repository, the Repository Provider calls Restic Repository Provider, the latter invokes the existing Restic CLIs. +In order to manage Unified Repository, the Repository Provider calls Unified Repository Provider, the latter calls the Unified Repository module through the udmrepo.BackupRepoService interface. It doesn’t know how the Unified Repository is implemented necessarily. +In order to use Restic to do BR, the Uploader Provider calls Restic Uploader Provider, the latter invokes the existing Restic CLIs. +In order to use Kopia to do BR, the Uploader Provider calls Kopia Uploader Provider, the latter do the following things: + +- Call Unified Repository through the udmrepo.BackupRepoService interface to open the unified repository for read/write. Again, it doesn’t know how the Unified Repository is implemented necessarily. It gets a BackupRepo’s read/write handle after the call succeeds +- Wrap the BackupRepo handle into a Kopia Shim which implements Kopia Repository interface +- Call the Kopia Uploader. Kopia Uploader is a Kopia module without any change, so it only understands Kopia Repository interface +- Kopia Uploader starts to backup/restore the corresponding PV’s file system data and write/read data to/from the provided Kopia Repository implementation, that is, Kopia Shim here +- When read/write calls go into Kopia Shim, it in turn calls the BackupRepo handle for read/write +- Finally, the read/write calls flow to Unified Repository module + +The Unified Repository provides all-in-one functionalities of a Backup Repository and exposes the Unified Repository Interface. Inside, Kopia Library is an adapter for Kopia Repository to translate the Unified Repository Interface calls to Kopia Repository interface calls. +![A BR Workflow](br-workflow.png) +The modules in blue color in below diagram represent the newly added modules/logics or reorganized logics. +The modules in yellow color in below diagram represent the called Kopia modules without changes. + +### Delete Snapshot Workflow +The Delete Snapshot workflow follows the similar manner with BR workflow, that is, we preserve the upper-level workflows until the calls reach to BackupDeletionController, then: +- Leverage Repository Provider to switch between Restic implementation and Unified Repository implementation in the same mechanism as BR +- For Restic implementation, the Restic Repository Provider invokes the existing “Forget” Restic CLI +- For Unified Repository implementation, the Unified Repository Provider calls udmrepo.BackupRepo’s DeleteManifest to delete a snapshot +![A Snapshot Deletion Workflow](snapshot-deletion-workflow.png) + +### Maintenance Workflow +Backup Repository/Backup Storage may need to periodically reorganize its data so that it could guarantee its QOS during the long-time service. Some Backup Repository/Backup Storage does this in background automatically, so the user doesn’t need to interfere; some others need the caller to explicitly call their maintenance interface periodically. Restic and Kopia both go with the second way, that is, Velero needs to periodically call their maintenance interface. +Velero already has an existing workflow to call Restic maintenance (it is called “Prune” in Restic, so Velero uses the same word). The existing workflow is as follows: +- The Prune is triggered at the time of the backup +- When a BackupRepsoitory CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo +- In the new design, the Repository Provider implements PruneRepo call, it uses the same way to switch between Restic Repository Provider and Unified Repository Provider, then: + - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic + - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function + +Kopia supports two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), in this way, the maintenance will finish very fast and make less impact. We will also take this quick maintenance into Velero. +We will add a new Due Time to Velero, finally, we have two Prune Due Time: +- Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance +- Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance + +We assign different values to Normal Due Time and Quick Due Time, as a result of which, the quick maintenance happens more frequently than full maintenance. +![A Maintenance Workflow](maintenance-workflow.png) + +### Progress Update +Because Kopia Uploader is an unchanged Kopia module, we need to find a way to get its progress during the BR. +Kopia Uploader accepts a Progress interface to update rich information during the BR, so the Kopia Uploader Provider will implement a Kopia’s Progress interface and then pass it to Kopia Uploader during its initialization. +In this way, Velero will be able to get the progress as shown in the diagram below. +![A Progress Update](progress-update.png) + +### Logs +In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. +Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. +Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other uploaders/data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. +Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. +![A Debug Log for Uploader](debug-log-uploader.png) +![A Debug Log for Repository](debug-log-repository.png) + +## Path Switch & Coexist +As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. +Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. +Specifically, we will have the “Legacy” option/mode in two places: +- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. For details of installation, see below Installation section. +- Add a mode value in the BackupRepository CRs and PodVolumeBackup CRs. + +The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. + +### Upgrade +Changing path during upgrade is not prohibited, however some mismatches cases will happen, below shows how the mismatches are handled: +- If a BackupRepository CR already exists, but users install Velero again with the “Legacy” value changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized +- If PodVolumeBackup CRs already exist, but users install Velero again with the “Legacy” value changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved +- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. + +## Velero CR Changes +We will change below CRs' name to make them more generic: +- "ResticRepository" CR to "BackupRepository" CR + +This means, we add a new CR type and desperate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. +As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. +Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. + +## Installation +The “legacy” flag will be set into Velero server deployment and PodVolumeBackup daemonset deployment so that both the RepositoryProvider and UploaderProvider see the flag. +The same “legacy” option will be added for Velero’s installation, including CLI installation and Helm Chart Installation. Then we need to transfer users’ selection into the two deployments mentioned above: +- Helm Chart Installation: add a “Legacy” value into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. +- CLI Installation: add the “Legacy” option into the installation command line, and then set the flag when creating the two deployments accordingly. Users could change the option at the time of installation. + +## User Experience Changes +Below user experiences are changed for this design: +- Installation CLI change: a new option is added to the installation CLI, see the Installation section for details +- CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "Restic" daemonset, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes + diff --git a/design/unified-repo-and-kopia-integration/unified-repo.png b/design/unified-repo-and-kopia-integration/unified-repo.png new file mode 100644 index 0000000000000000000000000000000000000000..609f6db58fe332aa9d77c1e0df5d9fec690bc00e GIT binary patch literal 38955 zcmeFY1y`I+(=Lhz2n0=V*AQHS4o+|j?(XjH?(R--cL?t8?ry=|Vc5eX@AvMr&-np+ zt;4LD>FMt3YQ6iax{44PDdA5au|I-=fqfDc5tIc31K$F@?_ocHa>Dx#g2BK(Zkh-P z$cPFE5X#tE8Jd_IfPsmGBqYJe#|@!-AH2;7@I%1mh`_bMO~VnA`J?mlaRiHrK){jp z`e$nkqbpe}Aft5E_?8lXtf=rVgGT8K)$M5~4D-EGr(TqC;kkfqc^-E=Z+*@hazEG~ zr?E2}Z-NA4kEcj5?34#%BwMNp=G8J(cZWc`N6-etdQ9&08AuXe+S6*LFA+(fyD<~*YJ){2>$+pRrw&R!U9%A61?s#+F+k@ zA1{u3F-VW2z?96W3JKjLG=~7o?A;lX1t(l0ay3Nc`En`nQFu{`7+D-yGolJZs~3$h zyce2@iK({V1y%>XSMwuSN+dniqcpeN+cuBNVb*}S!j--Tn(sZ@NIq8U>YJSzuu5w7~=X)Q2lh^L&ks9LoYFT~UyU*A9Gd5oir^MPs1{Izr(0=qu*+jA#o_!vf zArUCBU=wSTK*<@^`U-cG5s8ys#w7hGA6?_PZV%*#b6YM1NTsCl{yE^W zKr63HGJO7~C(6{FaMvBqo3ELPR;->DfjfR!zctJyu>&0>&2Rw;B>eoCOo+_}Mxnn* zf*nnxp30?(Kp~z=g;p3^lVZNx50|12x8X=W(>2}&?tS^p=|Z|LB(W+UY~!k$RFA+Fo$5<{-3z=Pv3|0;^!x5(eCi4-@*nEddpFNJ_<^kygGC1_456dJ>e<#EuVN(+r z(x%@o20}OQ=U^CkaDrf7@@OA2ZFXhIk9-V^2o1hsRXDTWkel>uA5%L;tIlv58hxZQRZpXuZ_(Kt+Y!qV(vhM8d?oa>`%XvL znk}7W9Qz;yzUR_<={NtX<0{cA)~cjB&l!m(w3WA0C(I_=1*$uDJFXY97q%DL8!nGn zHmU^NnLoX+ceMDT;G(!OkvhcM0+fxCq9V-V-^HVH zD5YhJ#WL)_Y=7n}?V*Wg=>OP`Miu=yEi~;ZOLfS7NRuMLLh>AqHn_AKv8#H;euZ`= ztdLq#{!K|CyIHJWtp4Y1!4cI*A6+pWIds{puY2)&@yGE*-+WAk_GDaU6K201!I`o; z2u2hbR-zXq7B}#nW-pV~gFB=*r!;po^S1G}3B9Jf5`6l>RA6K5zO1^ex$L6KgD1{f zXm@CGqx+=eO5vd_pwS~rBBWO=l+aHZf&U>?r+<@?5i<=_7|RJO>-#P9QY!!0ZR%|* z>vtaGvB|3`RMRge-;LL%p325%?-ryMXs0ZvJ6YXyyx#$7UUux_yMnI4!z z4RJFrc`@eerz0jc3eVVncW1S1dN!zup<*{G2-6}ZB7;E&F?UtphQq3N= zPBs&cJV(Hwpa|jS0lp}orTyA~+WYadYv!w6m$S>0OI=qom)AdYI~IS&t}E_5kMvGR zE|w0i(=CRi^ayl2uIjH79`WyAJ8cfiHtU{pULamWUpQXpUUOfmUbCPuq4Pe>LL3Zb{?Bw)oxohqW6lB`ISb=iUtD$5_M3C=!VpQ`rw-2#y`rakM2+gzI-`_Rq_vj zKBjzT1Hw5b2l1oqK{a_=Y*f46gzW_rK9iOi?!2JfD3;X)_h67xn9d95?+a{j+}ebeHw|}soVJx zA){xxV;xsiT*%(ubS!ug(tsY7xYMx=Y<|nz8Y+>)E@a6d&!lDJawI<=6)CNA_+x&w z)ZXyJ0%@Wzqh*b|vhCV^@G%Snf;RnF+bO;2kIUsQ*}XUgZ7nUUhE3D7^W5XnrP=sG zq*Jp!i>6cCx_i&5kodw$#SCkkRc70s({Z=O*(sIN{`z4vj>o;GldjVQS67?QGuFKp z&!7wall3jhXzs6p*?~Q2`z-l4!$&oNDDjD6&5rd_X_74Nlb!pv7Y*QZ642LZ*Jy9z zQ|1C98$voR0%q%_E-(z3G@XiEW4 z=wv~%MIya!)K2g!bMy0g$49zM8ZS$ahlfDp3i>{OyL4|M(TZpTKf)j~LezzEU69I~ z|3i8#L}%Koh!-vr*y#tb-p9DtTYug-D_=N>w-5@q3pg9_>;fF`A_FjgO-OAwvgv7J zAmv~SVY=@hF0hk7MYwOME}^+Sd33R?)2RS_95*v?unH*eN-{mY-0ymNT4vGu1Q3&{ zoJ$K)@VCfUu~^$s0YTi{UN|or(*g!BC&^IdREt3uLZz=dzHspWO z_wN4~{N z5)u*uZ1oK}WCex(5eNO^A~v?Sx8|Uub8>Q`bz-8mvNfV(U}tBiqi3XJWTXMrps{nY zwAXQ_v9u%kyODp}5j3#Vvo*1{H?guLd~a7r*UG`3ih_jc+CsA_#iJB7+n2LIv}d zkVG@gndt}oC&!yOnsV9Z&svL9U6{zDY;dPpWm{r5YH|iF=@)Qe{I^zVStbS#38H8| ze9LFkS#jK3QUs_hFQALh96%rt!IRe|o#o>9{kq%hLOiD+VLi(43QI4o1k8BOXJ?cx zo6#Kxhr{2qXHr8YbmSrrH-|GrrnpQf@0~#v*t|)Z+232Yt{_@N*e_fi*KjcK0)CjCW_NnvpCNYY4;cJI&jv&#)AK<{hD>=%KSmt5i>oyus#tHCwAKNAa zgb{8oT$)$pGh5I!IU0!lELd1lAaECAm9p6bN)G_X!$KgnA9Ec)FYID06 z3qd8*<=1ph6ULtS(@OJI!IiJG5DvzSCYZFk^PzV#1m~v|sp#)Z@w}t2p`jr|9NBP% zH>_6c07}`s;LkLw%*rm-l*#L2GLPHF&J6iFKHFEu;M@Di-slC@CR)am36msuBM}s z(i?+3+r42|e6RM`JGG|$RI?jQ1IY{yh5MgEvdexFZwZ?4`vk<%!?2`za9JM1q|^vI zX#IVe5svO<=~FckaFUoCa4q$vyZDedviV}7L$PEIVXxf*@TK$YmaA;#kor2u%T0EL z+Wi$_w9O)xt(QU_4VcPrNm`zi2U%Y2pQ)8)K>EoRG~LR>aoJ^U_eUu!TdzV9uo!!_ z_N_Wz$;(yiQH@iy+%E&{PZob(uRAPgxLVE@{k(cU?->BERg!$jQn(^F{#P!phO7KB`1Y|-B$3rW$(u`^2g6#vpc9N zTV=nmEZ3XC8&>dpz_I~PYA0SEb5aB<;AwyJPN;-eIV6ErqCDIynAabfO|KoT|F?Cw z8o&vYp!7koNSv%`8MGTqf8^W4+ZRZtk!N{66~6^Bta`n@Dkait94$n~AnH%&3L}vT zCF}5+Cjub%jHiAD0Pa`qN?tu*E(dB%X2_fN64mmgGB{-GO%+_8PaF3(yZltSVL8PU zscpA=;ST3YlM38kZx<3ac^mLJ>}6^l7b-Lq;sRMhRq~td4~eC@AM@NVx?rxh!|1+U z@?Cr0uQ~0VwVWqPq%ev$TCT-E@a@hNN+(seKMhy$d6>LhVbRRipRw2omzc@}eOE{VJJv{Pd95{v9s8uphDy;Pj;u8{6x-*IOKD3J#IT4NlasP8`>o9XR@5wt@(r`n*$ChE2Gtb2R*z z?RE!qY%tUM+3F>Ym{#;!>JO$gji1@9yy$Iu^dqmGiMql9$hJCP8gMN`c&UlNXeJ1# zFBj1O4I&Q>)q%q9hq-rO$wd}Ti{NNB!5FQ*O!ZF$IIF^oLOA z^lcFRgD>^d5O4Wc>ZBwuB0oIVTdXMZp5U;U%f$9Cp5i&J5?QrCBzM zX5?-re1f4fN0EW?oRoRKIm|7voORml1fR>~1F|gEu@E#wWANnJ?fBWjo?T<=1%|A< z29V$z@N=_;@KN%%vl&mUo9E_fSxqW#vSjnk9? z!ynPlD`1`<^x?qu1}_Q%i!oA(MhDJZ#gk~u5mg9f!n1z48@GsfZ3SSME;lzkfXdYKEBYSBhuhw-+-Pyg^wsvfXOl){DClVO;YRe{-zQ8^Oj?$tj1@bOI5-* zWFJ@}WcSt?fDtMl(j5!F03XSji%><$Dxamvy702b>W^lYb`3?E*EzZY-=#49;{prU zm%GLL-KMv;-KcB+d3>5Dv6-ud2QOCEV;ZZ76lx0Tj9FKfinTHuCx`a8bl3B9Zi$`w za!oI(bc`CZ!f;rJVp1g>yHtAoeZ`)d^7;WEHK*1!7`R3IJKSNxDyV>mR+sB4$}tqj?MZwY)^|AnH@?Vjb%n5 zwWaTcxVCTSz()|Ve`9dHQ%0- z_6>h#n10f%Xp4WlDW0)i$+n!{4=ml?1o~aA9favW<V*+k%foEn3G&LJpRWi->=`W+Ck79O`9%{qkj;m#{+wI{cE~ ztPhh`gh3fhhX}7VlW*2%XPo|+Kh5LmuyE%~wZ#exyUUe+tCepISQfp{=Tp`(k)WE9 z1ggS3^vY;VZf_-a?g(8%h~hABk|?irVQhJ;l;$~dnUx86r6EXG9&)R3Prl(yt>G+) zsC}OW9?~CTpJP!C!iIK}3PiSap$#UPUiXr+Jd-*+Rp^#%^%D|!UdR*ZO1>fioEmqB zvefR*fJ&P9+?o?gt=Z`}uA7F%@@=hUuNHo-td^njZNF>WuMYt(r(p-L4mt<(`%a=k z_!fC3fX{&vYY^oE#LQ59Ac>xrAs#jhQnY7~7V zpA)7NLI={?;K>(VB~6E(cv!@0?~+VX=UG{jK4QdA8Fj}tjUcdNBmkps<8Mn@hr)+y zcW+YUt}afcF(vP!5wJWO<(sQoS6#*k*{j~XaT$74;M-sL3_Tv|>ss;^TASUm>x)q) zFc!jf=8hGoq-spo40GL{nYTvNG``vKJg;Y2u9yArwS#^8o|fElVtro9%Wy$KK05WY zqKv{dg>O8C$taOhZWO2J(U2{1CDvE~3xrdV!I3rcdVp;(q)zSXfxF`gWIZleyB9em zvNik+bFSh_)I?3!Jtng;M-C+-_s^Cxmv1iy<>s0#KY0ImRj5)}O}PCGP5egv9Y-;4 ziC`jOFHEtx76TQD;LY1~-skpF;upPrRsxCL{B_+6TED#I#DbZkxTcqF?8(w&l>S)a z56|@H+8fV5=v;+{wo0C3qdXzR$Y`Vp5p;@>xS8~BU-+pTNfa*&R|N?fpxm*wbt$_E zl&X}u$;Jg>kVGTZmv$6>+g?_doT{=%zyzCS6Siy{g#F=W)SpNgOAd<~X&B>V_PXzP z>Z3Z?@VXPvglHmj8#gzUXsY7TY8BiE6Er*KUj=7c50lw{LEVoN&LU55l(8&c%5J94 zypGc!NtsJ-q!R7=Xb{ZAtLLoPghd0&YozgMyKVB zWX_=cc<3c1EMbBADDHV332ebs5&n37~Y1pWK@aLm@Itij@S7U zz1~U@I`ryVne9mJ)g$~GOtpsB)CA9#s+EA69Y3Tp*~mJ8zlAh$xJ923&uOJT$Thoi z;e=%_&08qW408S)Ilh%)O@M?^L~~5Sp;TA+jro9v7OC<(`;1_;`D~~2Rr~T`6oyG@ z$$1nt!0z?wcIF3)IXlZbK8)qsWimf%7FyHH@13=SQ*J<3^)69Cf89Y zp4+giyHF+>w%235?M>?zrhn%;_q*Br!w7u1Q&NNn-HzTSeon9nCit~J!<#$QhuDzW zOTWrhnglb&=w~Zjq6LL>%C^*1ik6167zU<5@d|fCbij4xyusYwo{rVXeQjcLZmnve zv}Z04j=0X*OOyUZGN-|I5mLTDeWqAv-@dr>^C&5FT#NTc*Pq-Qd-^JEBL+ ze&WMNZ48sSd{JCln`OH|rq~V<&8F&6@X{^a37 z?Mhv-gZpD?`9>3&V#W_b=c-JS@D6n;L*q7{A`f<-%CnUm%cyV}XPNG|=&|~2$ zhOVk|9nyARnhx6$r`wI-hf+9OX024z_G+%2PS#ZS25fR!9P%9FY--#_@Q1XRJ18lF z5`Wf9LA#tAF%5W|cH5lV)}Qy$1wJpI_jf6H4Tb(NSWabqy#+i1Aw3@%v0y5s-`wS{ zVW@cOFTej95DbPx{b1wzMP?p^s~w0VTyKxy(6zw8cK~(b5~G&`1YROdwtF|v`o7#{_&UrCA}f#p@AMk(bC6T=WqqIUGYjaUt|%^kh%Q*`c&e(wkG%2f z>&++j6{8fni#p=mmye&2uvy82DF(tQ&T&+taXz_$n|SA+nB4BaskPeG4R~=O8VKdeO6(8wlfI$&nO9pw z9laYAdu{jYauep%pTQY{AVZCr;7S_-Q**_+iCS`qaIYDA=L<3_N<#Je3wOpZ38qLV02FF;;#?hYFVQ?%U^S7{+LS?%bJ&akq z!YB_WMlutiV$=lDv=!u?(kR32J5~x~GjIoJa7OtvTa4zIK+h0W&n2W;X6^r2oU=u& zt+9n;`J3+`w|@lM@PHq%95BR9U+56<@r(ZPd^w}%i}O$xPY11tPkq!+{QaUp^iQ!u znDpA->Rzw6-#E8p@aZt``uetig@E{R53y~?&>bQaSa^sMIGnX<*2LmKBRQm~83=rx zLwcRB%%IByJISM{r4bzoZ~}Tn*=PeItFe*iHv`ZCw!0#8=+&|)66N$v0%#4lI=6(; z6#I_-+;zgnDQrSj;}VMlE~Ali)q289jaFIOA$)G0N3+EoR?Lyr#Hfl={N7lYk7ccs zb?~3RZdMXpEjmZs zON0hc@t5}-UQ{}MFKghZ;O+EIdS=J9CS@-^D>6G49H7h1lmzS(QruA~D)Y~295(B0S3x~7(L6e}hO)>Q-(>4>0*M7>V{iv; zI6k!s4KvKtoCI+HU>n8cx`XkI(tY*Wpa;!1*CCLJn%hyK(PH{a=#U(~cBQ8xnBI7c z%V9rA5XCkN^Q(5xmzM$Ee5S`g0_+O7>4y|A&-1v8rm=z98`_+WLXrV$dZ!yU*e?YC zw7!O)De#^((MqsY(&)H{{pG}FlO^7nCjKvH$c2eJxaQQnTIvcLJfHbCS`KZ(uBl$2 z|E0I|A<`e^X>W2i{Xt|9WQvR>mzagcd=6-2N+cGnl9TMtYcZZ8c1VP%_Cchq<;$q*l3sLH)tGc>z7c8jK$%=>2=^1{zfm05LepAuyKCE;kTl zf!jc+m^`ut8>qKyHCq&8dP1l=i1-N`QIF^4!q2pQGCc=NfHP>PzZod?3N{ z?UXRVBVLs`XUzD>zvpQ2QrmJT>b?LPKs=itt8O8YoXZEM2`x_k6-kw$JthG13oHs1 z3tyVY<6cri_NEB}#1DLAjOi|B_$!ay7-43_J#Xbw`C{UC&mbZm$7a0!*{Vv2E67R3 zXue3#bpZ5h=B>KOZpcwm;t%HWUrZ*!^qIL+ye=M7g9@gEc=sf-+-_6Qmw{N4Ko@Ya!p{K(3^cFTUwIzyLk;LG~Q52rN~WE8RoB7w!E| z=-Cm(ND}SJf4Txet~kwWpS&D{l}k;W^L5=->}BTuoHZsbzFcA4-M%o0VIT8oyvzyA zI3dpRkd?#`d*>x@iOJ0#tj(^me1x75`VlEi@D16(y^%D93AWGa4S~Kp6$gQzj&*e@ zd+ZD0DBJ}yxx#*&T?$mGX07>_@v5G&aaq6mB%1H=l{_rHROkaW+}*-2jnf7QP#Pgo z{YBZU8Ow5&wXq&DF`&#RcCIN~kQn+l9=H;cCNeCKKwyo zdU#PHH${01SRlMigUn<6D`s&!E^|6x+-$8_;f*Xw{nsfpoN&on@lUJi7HT_W@N?t~ zlkSFAwz&yYZ^bXWHn_aifl+N0Q*9A&sQpF)f4lW7Q7$|vP8^DU ztKWM8c9L`1Oi)Er76o)U@BcQIrK6fb11CoQ2*EAMHqJ1@ifLYRxp}wTT(L8j@Jo!KI>bI;gZM9%29_ zy$~=ILWsY32qa`eI5#ljuz!9MN8>_?bPw@GxCH-G#n~H7bP*C+1?nFOpIt&;R4GK$ zQ30sGt8_wwB=EfliT{-#g#nMXww2Mt2Pyw64<96P2PTNl_m2c2J8ZOnoFDe}-^2(Q z$^{sBY!wV)4eY-XLww$(>A17WLH}?k-Y_5uF0hy%|4N7;LXd9lX7>aBB~t*r-lQks z!PLJ0vMsitflPx1mEcA`i} zemfj14#iPO$6iAlF1EQj0bB^xK*sxC{emXE#IPP{{y}E$H{vOmscF!8ptvC}RV

    HH{gK`C<@dYGUAEcYq*$&n(jDZVQAKay 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 Date: Thu, 19 May 2022 10:12:27 -0400 Subject: [PATCH 168/366] Include moer details Signed-off-by: Abigail McCarthy --- site/content/docs/main/self-signed-certificates.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/self-signed-certificates.md b/site/content/docs/main/self-signed-certificates.md index ae4f3f854d..acf2019cd4 100644 --- a/site/content/docs/main/self-signed-certificates.md +++ b/site/content/docs/main/self-signed-certificates.md @@ -52,6 +52,14 @@ You will need to change this setting on the server to make it work. **Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premise environment. Using this flag in production is not recommended. -Velero provides a way for you to skip TLS verification on the object store by passing the `--insecure-skip-tls-verify` flag with Velero commands. If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. +Velero provides a way for you to skip TLS verification on the object store when using the [AWS provider plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws) or [Restic](restic.md) by passing the `--insecure-skip-tls-verify` flag with the following Velero commands, -This flag is currently only implemented for use with AWS provider plugin and Restic. +* velero backup describe +* velero backup download +* velero backup logs +* velero restore describe +* velero restore log + +If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. + +Note that Velero's Restic integration uses Restic commands to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve interacting with Restic, Velero will add the Restic global command parameter `--insecure-skip` to Restic commands. From 0aafba2cd855a0a78996b2635f92cb636f1b2f7b Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 19 May 2022 11:11:14 -0400 Subject: [PATCH 169/366] Propose Shubham Pampattiwar as a maintainer Shubham Pampattiwar has made several contributions to Velero, most recently in designing and implementing two v1.9 features, including the following: - [Add design for enabling multiple label support](https://github.com/vmware-tanzu/velero/pull/4619) - [Add multiple label selector support to Velero Backup and Restore APIs](https://github.com/vmware-tanzu/velero/pull/4650) - [Add design for enabling support for ExistingResourcePolicy to restore API](https://github.com/vmware-tanzu/velero/pull/4613) - [Add existingResourcePolicy to Restore API](https://github.com/vmware-tanzu/velero/pull/4628) Shubham has also been driving forward the data mover requirements and design discussions for velero 1.10: - [Add datamover design](https://github.com/vmware-tanzu/velero/pull/4768) Signed-off-by: Scott Seago --- .github/auto-assignees.yml | 1 + MAINTAINERS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/auto-assignees.yml b/.github/auto-assignees.yml index 346fcc9aab..282591d72f 100644 --- a/.github/auto-assignees.yml +++ b/.github/auto-assignees.yml @@ -15,6 +15,7 @@ reviewers: - ywk253100 - blackpiglet - qiuming-best + - shubham-pampattiwar tech-writer: - a-mccarthy diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 561c0ee960..2819a77e88 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -12,6 +12,7 @@ | Wenkai Yin | [ywk253100](https://github.com/ywk253100) | [VMware](https://www.github.com/vmware/) | | Xun Jiang | [blackpiglet](https://github.com/blackpiglet) | [VMware](https://www.github.com/vmware/) | | Ming Qiu | [qiuming-best](https://github.com/qiuming-best) | [VMware](https://www.github.com/vmware/) | +| Shubham Pampattiwar | [shubham-pampattiwar](https://github.com/shubham-pampattiwar) | [OpenShift](https://github.com/openshift) ## Emeritus Maintainers * Adnan Abdulhussein ([prydonius](https://github.com/prydonius)) From 131c8a920f0e205dff9c245df8490b8292991e50 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Thu, 19 May 2022 17:36:21 -0300 Subject: [PATCH 170/366] Remove break line Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 57f8c91517..76e8a36585 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1135,7 +1135,6 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } - ctx.log.Infof("restore status includes excludes: %+v", ctx.resourceStatusIncludesExcludes) for _, action := range ctx.getApplicableActions(groupResource, namespace) { From 61b0d990fa981edc01839af1768690548b57cac1 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Thu, 19 May 2022 22:37:02 -0400 Subject: [PATCH 171/366] fix flag typo Signed-off-by: Abigail McCarthy --- site/content/docs/main/self-signed-certificates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/main/self-signed-certificates.md b/site/content/docs/main/self-signed-certificates.md index acf2019cd4..2aab5c1b97 100644 --- a/site/content/docs/main/self-signed-certificates.md +++ b/site/content/docs/main/self-signed-certificates.md @@ -62,4 +62,4 @@ Velero provides a way for you to skip TLS verification on the object store when If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. -Note that Velero's Restic integration uses Restic commands to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve interacting with Restic, Velero will add the Restic global command parameter `--insecure-skip` to Restic commands. +Note that Velero's Restic integration uses Restic commands to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve interacting with Restic, Velero will add the Restic global command parameter `--insecure-tls` to Restic commands. From e0e3016efa2e9e5221cce8a2d2cf35efb3196c5a Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Mon, 23 May 2022 17:53:32 -0300 Subject: [PATCH 172/366] Cleanup resetStatus Signed-off-by: Rafael Leal --- pkg/restore/restore.go | 15 +++++---------- pkg/restore/restore_test.go | 12 +++--------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 76e8a36585..e4834ecded 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1009,7 +1009,6 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } if groupResource == kuberesource.PersistentVolumes { - resetStatus(obj) switch { case hasSnapshot(name, ctx.volumeSnapshots): oldName := obj.GetName() @@ -1694,21 +1693,17 @@ func resetMetadata(obj *unstructured.Unstructured) (*unstructured.Unstructured, return obj, nil } -func resetStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - delete(obj.UnstructuredContent(), "status") - return obj, nil +func resetStatus(obj *unstructured.Unstructured) { + unstructured.RemoveNestedField(obj.UnstructuredContent(), "status") } func resetMetadataAndStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - newObj, err := resetMetadata(obj) + _, err := resetMetadata(obj) if err != nil { return nil, err } - newObj, err = resetStatus(obj) - if err != nil { - return nil, err - } - return newObj, nil + resetStatus(obj) + return obj, nil } // addRestoreLabels labels the provided object with the restore name and the diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 87528a6277..7655253d10 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -2847,30 +2847,24 @@ func TestResetStatus(t *testing.T) { tests := []struct { name string obj *unstructured.Unstructured - expectedErr bool expectedRes *unstructured.Unstructured }{ { - name: "no metadata don't cause error", + name: "no status don't cause error", obj: &unstructured.Unstructured{}, - expectedErr: false, expectedRes: &unstructured.Unstructured{}, }, { name: "remove status", obj: NewTestUnstructured().WithMetadata().WithStatus().Unstructured, - expectedErr: false, expectedRes: NewTestUnstructured().WithMetadata().Unstructured, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - res, err := resetStatus(test.obj) - - if assert.Equal(t, test.expectedErr, err != nil) { - assert.Equal(t, test.expectedRes, res) - } + resetStatus(test.obj) + assert.Equal(t, test.expectedRes, test.obj) }) } } From 8a156d69b97cad9b506bac98b4aa0802e5e8341a Mon Sep 17 00:00:00 2001 From: Abigail McCarthy Date: Mon, 23 May 2022 22:19:10 -0400 Subject: [PATCH 173/366] Add details about using multiple volume snapshot locations and restic Signed-off-by: Abigail McCarthy --- .../docs/main/customize-installation.md | 26 ++++++++++++++++--- site/content/docs/main/locations.md | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/site/content/docs/main/customize-installation.md b/site/content/docs/main/customize-installation.md index e82002eec3..b90300f826 100644 --- a/site/content/docs/main/customize-installation.md +++ b/site/content/docs/main/customize-installation.md @@ -25,11 +25,9 @@ If you've already run `velero install` without the `--use-restic` flag, you can ## Default Pod Volume backup to restic -By default, `velero install` does not enable use of restic to take backups of all pod volumes. An annotation has to be applied on every pod which contains volumes to be backed up by restic. +By default, `velero install` does not enable the use of restic to take backups of all pod volumes. You must apply an [annotation](restic.md/#using-opt-in-pod-volume-backup) to every pod which contains volumes for Velero to use restic for the backup. -To backup all pod volumes using restic without having to apply annotation on the pod, run the `velero install` command with the `--default-volumes-to-restic` flag. - -Using this flag requires restic integration to be enabled with the `--use-restic` flag. Please refer to the [restic integration][3] page for more information. +If you are planning to only use restic for volume backups, you can run the `velero install` command with the `--default-volumes-to-restic` flag. This will default all pod volumes backups to use restic without having to apply annotations to pods. Note that when this flag is set during install, Velero will always try to use restic to perform the backup, even want an individual backup to use volume snapshots, by setting the `--snapshot-volumes` flag in the `backup create` command. Alternatively, you can set the `--default-volumes-to-restic` on an individual backup to to make sure Velero uses Restic for each volume being backed up. ## Enable features @@ -173,6 +171,26 @@ However, `velero install` only supports configuring at most one backup storage l To configure additional locations after running `velero install`, use the `velero backup-location create` and/or `velero snapshot-location create` commands along with provider-specific configuration. Use the `--help` flag on each of these commands for more details. +### Set default backup storage location or volume snapshot locations + +When performing backups, Velero needs to know where to backup your data. This means that if you configure multiple locations, you must specify the location Velero should use each time you run `velero backup create`, or you can set a default backup storage location or default volume snapshot locations. If you only have one backup storage llocation or volume snapshot location set for a provider, Velero will automatically use that location as the default. + +Set a default backup storage location by passing a `--default` flag with when running `velero backup-location create`. + +``` +velero backup-location create backups-primary \ + --provider aws \ + --bucket velero-backups \ + --config region=us-east-1 \ + --default +``` + +You can set a default volume snapshot location for each of your volume snapshot providers using the `--default-volume-snapshot-locations` flag on the `velero server` command. + +``` +velero server --default-volume-snapshot-locations=":,:" +``` + ## Do not configure a backup storage location during install If you need to install Velero without a default backup storage location (without specifying `--bucket` or `--provider`), the `--no-default-backup-location` flag is required for confirmation. diff --git a/site/content/docs/main/locations.md b/site/content/docs/main/locations.md index 2f54077dd7..d5dbc710a6 100644 --- a/site/content/docs/main/locations.md +++ b/site/content/docs/main/locations.md @@ -41,6 +41,8 @@ This configuration design enables a number of different use cases, including: - Velero's compression for object metadata is limited, using Golang's tar implementation. In most instances, Kubernetes objects are limited to 1.5MB in size, but many don't approach that, meaning that compression may not be necessary. Note that restic has not yet implemented compression, but does have de-deduplication capabilities. +- If you have [multiple](customize-installation.md/#configure-more-than-one-storage-location-for-backups-or-volume-snapshots) `VolumeSnapshotLocations` configured for a provider, you must always specify a valid `VolumeSnapshotLocation` when creating a backup, even if you are using [Restic](restic.md) for volume backups. You can optionally decide to set the [`--default-volume-snapshot-locations`](customize-locations.md#set-default-backup-storage-location-or-volume-snapshot-locations) flag using the `velero server`, which lists the default `VolumeSnapshotLocation` Velero should use if a `VolumeSnapshotLocation` is not specified when creating a backup. If you only have one `VolumeSnapshotLocation` for a provider, Velero will automatically use that location as the default. + ## Examples Let's look at some examples of how you can use this configuration mechanism to address some common use cases: From 7e8f1dcf6fe3fae11371120f06edd73bdcf153f5 Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Tue, 24 May 2022 00:11:53 -0300 Subject: [PATCH 174/366] Add docs Signed-off-by: Rafael Leal --- site/content/docs/main/api-types/restore.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/site/content/docs/main/api-types/restore.md b/site/content/docs/main/api-types/restore.md index a8d064a6ef..ae4354666b 100644 --- a/site/content/docs/main/api-types/restore.md +++ b/site/content/docs/main/api-types/restore.md @@ -46,6 +46,21 @@ spec: # or fully-qualified. Optional. excludedResources: - storageclasses.storage.k8s.io + + # restoreStatus selects resources to restore not only the specification, but + # the status of the manifest. This is specially useful for CRDs that maintain + # external references. By default, it excludes all resources. + restoreStatus: + # Array of resources to include in the restore status. Just like above, + # resources may be shortcuts (for example 'po' for 'pods') or fully-qualified. + # If unspecified, no resources are included. Optional. + includedResources: + - workflows + # Array of resources to exclude from the restore status. Resources may be + # shortcuts (for example 'po' for 'pods') or fully-qualified. + # If unspecified, all resources are excluded. Optional. + excludedResources: [] + # Whether or not to include cluster-scoped resources. Valid values are true, false, and # null/unset. If true, all cluster-scoped resources are included (subject to included/excluded # resources and the label selector). If false, no cluster-scoped resources are included. If unset, From 200769a077d337a6536906d75c23c27df75b393f Mon Sep 17 00:00:00 2001 From: Rafael Leal Date: Tue, 24 May 2022 00:14:02 -0300 Subject: [PATCH 175/366] Add changelog Signed-off-by: Rafael Leal --- changelogs/unreleased/4785-RafaeLeal | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4785-RafaeLeal diff --git a/changelogs/unreleased/4785-RafaeLeal b/changelogs/unreleased/4785-RafaeLeal new file mode 100644 index 0000000000..450fed9c35 --- /dev/null +++ b/changelogs/unreleased/4785-RafaeLeal @@ -0,0 +1 @@ +Add ability to restore status on selected resources From e5821f19d2f0a1913e5188fee3a34b27cb6d3993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Wed, 25 May 2022 16:34:57 +0800 Subject: [PATCH 176/366] Bump up kind version to support Kubernetes 1.24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump up kind version to support Kubernetes 1.24 Signed-off-by: Wenkai Yin(尹文开) --- .github/workflows/e2e-test-kind.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index f889f9e7cf..3f079d3339 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -64,12 +64,13 @@ jobs: #- 1.15.12 - 1.16.15 - 1.17.17 - - 1.18.15 - - 1.19.7 - - 1.20.2 - - 1.21.1 - - 1.22.0 - - 1.23.0 + - 1.18.20 + - 1.19.16 + - 1.20.15 + - 1.21.12 + - 1.22.9 + - 1.23.6 + - 1.24.0 fail-fast: false steps: - name: Set up Go @@ -84,7 +85,7 @@ jobs: docker run -d --rm -p 9000:9000 -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -e "MINIO_DEFAULT_BUCKETS=bucket,additional-bucket" bitnami/minio:2021.6.17-debian-10-r7 - uses: engineerd/setup-kind@v0.5.0 with: - version: "v0.11.1" + version: "v0.14.0" image: "kindest/node:v${{ matrix.k8s }}" - name: Fetch built CLI id: cli-cache From 383eec1eed9ff1f594a71e5ecb934063c95b2043 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Sun, 22 May 2022 21:44:44 +0800 Subject: [PATCH 177/366] Update the doc of CSI support We have made a few changes to the CSI plugin to provide official support for AWS/Azure. This commit makes change to the docs to reflect those changes. Signed-off-by: Daniel Jiang --- site/content/docs/main/csi.md | 33 +++++++++++++++++---------------- site/data/docs/main-toc.yml | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/site/content/docs/main/csi.md b/site/content/docs/main/csi.md index 5c2950b352..80198c4c97 100644 --- a/site/content/docs/main/csi.md +++ b/site/content/docs/main/csi.md @@ -1,5 +1,5 @@ --- -title: "Beta Container Storage Interface Snapshot Support in Velero" +title: "Container Storage Interface Snapshot Support in Velero" layout: docs --- @@ -7,16 +7,14 @@ Integrating Container Storage Interface (CSI) snapshot support into Velero enabl By supporting CSI snapshot APIs, Velero can support any volume provider that has a CSI driver, without requiring a Velero-specific plugin to be available. This page gives an overview of how to add support for CSI snapshots to Velero through CSI plugins. For more information about specific components, see the [plugin repo](https://github.com/vmware-tanzu/velero-plugin-for-csi/). -The Velero Container Storage Interface (CSI) plugins are in beta. It's not recommended for use in a production environment and the `EnableCSI` feature flag is disabled by default. The Velero team plans to follow the upstream Kubernetes support level for CSI volume snapshotting, and the Velero CSI plugins will reach general availability sometime after volume snapshotting is GA in upstream Kubernetes. - -**Note:** The AWS, Microsoft Azure, and Google Cloud Platform (GCP) Velero plugins version 1.4 and later are able to snapshot and restore persistent volumes provisioned by a CSI driver via the APIs of the cloud provider, without having to install Velero CSI plugins. See the [AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws), [Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure), and [Google Cloud Platform (GCP)](https://github.com/vmware-tanzu/velero-plugin-for-gcp) Velero plugin repo for more information on supported CSI drivers. - ## Prerequisites - 1. Your cluster is Kubernetes version 1.17 or greater. - 1. Your cluster is running a CSI driver capable of support volume snapshots at the [v1beta1 API level](https://kubernetes.io/blog/2019/12/09/kubernetes-1-17-feature-cis-volume-snapshot-beta/). + 1. Your cluster is Kubernetes version 1.20 or greater. + 1. Your cluster is running a CSI driver capable of support volume snapshots at the [v1 API level](https://kubernetes.io/blog/2020/12/10/kubernetes-1.20-volume-snapshot-moves-to-ga/). 1. When restoring CSI VolumeSnapshots across clusters, the name of the CSI driver in the destination cluster is the same as that on the source cluster to ensure cross cluster portability of CSI VolumeSnapshots +**NOTE:** Not all cloud provider's CSI drivers guarantee snapshot durability, meaning that the VolumeSnapshot and VolumeSnapshotContent objects may be stored in the same object storage system location as the original PersistentVolume and may be vulnerable to data loss. You should refer to your cloud provider's documentation for more information on configuring snapshot durability. Since v0.3.0 the velero team will provide official support for CSI plugin when they are used with AWS and Azure drivers. + ## Installing Velero with CSI support To integrate Velero with the CSI volume snapshot APIs, you must enable the `EnableCSI` feature flag and install the Velero [CSI plugins][2] on the Velero server. @@ -26,7 +24,7 @@ Both of these can be added with the `velero install` command. ```bash velero install \ --features=EnableCSI \ ---plugins=,velero/velero-plugin-for-csi:v0.1.0 \ +--plugins=,velero/velero-plugin-for-csi:v0.3.0 \ ... ``` @@ -37,12 +35,13 @@ See [Enabling Features][1] for more information about managing client-side featu This section documents some of the choices made during implementation of the Velero [CSI plugins][2]: -1. VolumeSnapshots created by the Velero CSI plugins are retained only for the lifetime of the backup even if the `DeletionPolicy` on the VolumeSnapshotClass is set to `Retain`. To accomplish this, during deletion of the backup the prior to deleting the VolumeSnapshot, VolumeSnapshotContent object is patched to set its `DeletionPolicy` to `Delete`. Deleting the VolumeSnapshot object will result in cascade delete of the VolumeSnapshotContent and the snapshot in the storage provider. -1. VolumeSnapshotContent objects created during a `velero backup` that are dangling, unbound to a VolumeSnapshot object, will be discovered, using labels, and deleted on backup deletion. -1. The Velero CSI plugins, to backup CSI backed PVCs, will choose the VolumeSnapshotClass in the cluster that has the same driver name and also has the `velero.io/csi-volumesnapshot-class` label set on it, like -```yaml -velero.io/csi-volumesnapshot-class: "true" -``` + 1. VolumeSnapshots created by the Velero CSI plugins are retained only for the lifetime of the backup even if the `DeletionPolicy` on the VolumeSnapshotClass is set to `Retain`. To accomplish this, during deletion of the backup the prior to deleting the VolumeSnapshot, VolumeSnapshotContent object is patched to set its `DeletionPolicy` to `Delete`. Deleting the VolumeSnapshot object will result in cascade delete of the VolumeSnapshotContent and the snapshot in the storage provider. + 1. VolumeSnapshotContent objects created during a `velero backup` that are dangling, unbound to a VolumeSnapshot object, will be discovered, using labels, and deleted on backup deletion. + 1. The Velero CSI plugins, to backup CSI backed PVCs, will choose the VolumeSnapshotClass in the cluster that has the same driver name and also has the `velero.io/csi-volumesnapshot-class` label set on it, like + ```yaml + velero.io/csi-volumesnapshot-class: "true" + ``` + 1. The VolumeSnapshot objects will be removed from the cluster after the backup is uploaded to the object storage, so that the namespace that is backed up can be deleted without removing the snapshot in the storage provider if the `DeletionPolicy` is `Delete. ## How it Works - Overview @@ -59,9 +58,9 @@ Once an ID is generated and the storage system marks the snapshot as usable for Velero will include the generated VolumeSnapshot and VolumeSnapshotContent objects in the backup tarball, as well as upload all VolumeSnapshots and VolumeSnapshotContents objects in a JSON file to the object storage system. -**NOTE:** Not all cloud provider's CSI drivers guarantee snapshot durability, meaning that the VolumeSnapshot and VolumeSnapshotContent objects may be stored in the same object storage system location as the original PersistentVolume and may be vulnerable to data loss. You should refer to your cloud provider's documentation for more information on configuring snapshot durability. +When Velero synchronizes backups into a new cluster, VolumeSnapshotContent objects and the VolumeSnapshotClass that is chosen to take +snapshot will be synced into the cluster as well, so that Velero can manage backup expiration appropriately. -When Velero synchronizes backups into a new cluster, VolumeSnapshotContent objects will be synced into the cluster as well, so that Velero can manage backup expiration appropriately. The `DeletionPolicy` on the VolumeSnapshotContent will be the same as the `DeletionPolicy` on the VolumeSnapshotClass that was used to create the VolumeSnapshot. Setting a `DeletionPolicy` of `Retain` on the VolumeSnapshotClass will preserve the volume snapshot in the storage system for the lifetime of the Velero backup and will prevent the deletion of the volume snapshot, in the storage system, in the event of a disaster where the namespace with the VolumeSnapshot object may be lost. @@ -69,6 +68,8 @@ When the Velero backup expires, the VolumeSnapshot objects will be deleted and t For more details on how each plugin works, see the [CSI plugin repo][2]'s documentation. +**Note:** The AWS, Microsoft Azure, and Google Cloud Platform (GCP) Velero plugins version 1.4 and later are able to snapshot and restore persistent volumes provisioned by a CSI driver via the APIs of the cloud provider, without having to install Velero CSI plugins. See the [AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws), [Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure), and [Google Cloud Platform (GCP)](https://github.com/vmware-tanzu/velero-plugin-for-gcp) Velero plugin repo for more information on supported CSI drivers. + [1]: customize-installation.md#enable-server-side-features [2]: https://github.com/vmware-tanzu/velero-plugin-for-csi/ [3]: https://hub.docker.com/repository/docker/velero/velero-plugin-for-csi diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index 7d21ed6cfc..2b014fce1a 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -45,7 +45,7 @@ toc: url: /restore-hooks - page: Run in any namespace url: /namespace - - page: CSI Support (beta) + - page: CSI Support url: /csi - page: Verifying Self-signed Certificates url: /self-signed-certificates From 9bb0ed5e420f0b15180b78bc6c01ab0fe2673dcd Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 26 May 2022 20:29:04 +0800 Subject: [PATCH 178/366] Add Installation Examples Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 119 ++++++++++++++++-- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 4df9d4265e..59d64b0b8a 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -287,9 +287,8 @@ Kopia’s debug logs will be written to the same log file as Velero server or Po As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. Specifically, we will have the “Legacy” option/mode in two places: -- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. For details of installation, see below Installation section. -- Add a mode value in the BackupRepository CRs and PodVolumeBackup CRs. - +- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. +- Add a "legacy-mode" value in the BackupRepository CRs and PodVolumeBackup CRs. The value could be added as a tag in the CRs' spec. If the tag is missing, it by default means "legacy-mode=true", so the CRs were created to manage the Restic repository/backup using Restic. The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. ### Upgrade @@ -307,10 +306,116 @@ As a side effect, when upgrading from an old release, even though the path is no Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation -The “legacy” flag will be set into Velero server deployment and PodVolumeBackup daemonset deployment so that both the RepositoryProvider and UploaderProvider see the flag. -The same “legacy” option will be added for Velero’s installation, including CLI installation and Helm Chart Installation. Then we need to transfer users’ selection into the two deployments mentioned above: -- Helm Chart Installation: add a “Legacy” value into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. -- CLI Installation: add the “Legacy” option into the installation command line, and then set the flag when creating the two deployments accordingly. Users could change the option at the time of installation. + We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has below two values: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + ``` + spec: + containers: + - args: + - server + - --features= + - --legacy + command: + - /velero +``` +``` + spec: + containers: + - args: + - restic + - server + - --features= + - --legacy + command: + - /velero +``` +The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +``` +spec: + tags: + legacy-mode: true +``` +``` +spec: + tags: + backup: bakup-testns-36 + backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c + ns: testns-36 + pod: deployment-2636-68b9697c56-6hpz5 + pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 + pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + volume: volume1 + legacy-mode: true +``` + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + ``` + spec: + containers: + - args: + - server + - --features= + - debug + - --legacy=false + command: + - /velero +``` +``` + spec: + containers: + - args: + - restic + - server + - --features= + - debug + - --legacy=false + command: + - /velero +``` +The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +``` +spec: + tags: + legacy-mode: false +``` +``` +spec: + tags: + backup: bakup-testns-36 + backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c + ns: testns-36 + pod: deployment-2636-68b9697c56-6hpz5 + pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 + pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + volume: volume1 + legacy-mode: false +``` +We will add the flag for both CLI installation and Helm Chart Installation. Specifically: +- Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: +``` + command: + - /velero + args: + - server + {{- with .Values.configuration }} + {{- if .pod-volume-backup-uploader "restic" }} + - --legacy + {{- end }} +``` + +``` + command: + - /velero + args: + - restic + - server + {{- with .Values.configuration }} + {{- if .pod-volume-backup-uploader "restic" }} + - --legacy + {{- end }} +``` +- CLI Installation: add the "--pod-volume-backup-uploader" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below: +```velero install --pod-volume-backup-uploader=restic``` +```velero install --pod-volume-backup-uploader=kopia``` ## User Experience Changes Below user experiences are changed for this design: From 0df7dc50705d527c588b075c9829bf768b6ff069 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Thu, 26 May 2022 22:54:16 -0400 Subject: [PATCH 179/366] add site bio/img for maintainer/contributor shubham-pampattiwar Signed-off-by: Shubham Pampattiwar --- .../contributors/02-shubham-pampattiwar.md | 7 +++++++ .../img/contributors/shubham-pampattiwar.png | Bin 0 -> 181869 bytes 2 files changed, 7 insertions(+) create mode 100644 site/content/contributors/02-shubham-pampattiwar.md create mode 100644 site/static/img/contributors/shubham-pampattiwar.png diff --git a/site/content/contributors/02-shubham-pampattiwar.md b/site/content/contributors/02-shubham-pampattiwar.md new file mode 100644 index 0000000000..e6bb3a4747 --- /dev/null +++ b/site/content/contributors/02-shubham-pampattiwar.md @@ -0,0 +1,7 @@ +--- +first_name: Shubham +last_name: Pampattiwar +image: /img/contributors/shubham-pampattiwar.png +github_handle: shubham-pampattiwar +--- +Engineer diff --git a/site/static/img/contributors/shubham-pampattiwar.png b/site/static/img/contributors/shubham-pampattiwar.png new file mode 100644 index 0000000000000000000000000000000000000000..70e288d25e0a18d659f2a8c8580266eaf9e74312 GIT binary patch literal 181869 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
    $w#bbF%dMAS?bwlFQRMozt?Ljz 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%0D~LiE6X691`go$sQV9&);R(??c~wK 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 Z{|_4bC<)o=jXnSX002ovPDHLkV1mu|OgR7m literal 0 HcmV?d00001 From 22e0afaa2a85054a4f32084906d242766646a192 Mon Sep 17 00:00:00 2001 From: Abigail McCarthy <20771501+a-mccarthy@users.noreply.github.com> Date: Sun, 29 May 2022 22:58:21 -0400 Subject: [PATCH 180/366] Add more details about restore workflow (#4928) * Add more details about restore workflow Signed-off-by: Abigail McCarthy --- site/content/docs/main/how-velero-works.md | 35 ++- site/content/docs/main/resource-filtering.md | 2 +- site/content/docs/main/restore-reference.md | 220 +++++++++++++++---- 3 files changed, 201 insertions(+), 56 deletions(-) diff --git a/site/content/docs/main/how-velero-works.md b/site/content/docs/main/how-velero-works.md index c3ce76c8a2..19fc89a94f 100644 --- a/site/content/docs/main/how-velero-works.md +++ b/site/content/docs/main/how-velero-works.md @@ -28,15 +28,6 @@ The **schedule** operation allows you to back up your data at recurring interval Velero saves backups created from a schedule with the name `-`, where `` is formatted as *YYYYMMDDhhmmss*. For more information see the [Backup Reference documentation](backup-reference.md). -## Restores - -The **restore** operation allows you to restore all of the objects and persistent volumes from a previously created backup. You can also restore only a filtered subset of objects and persistent volumes. Velero supports multiple namespace remapping--for example, in a single restore, objects in namespace "abc" can be recreated under namespace "def", and the objects in namespace "123" under "456". - -The default name of a restore is `-`, where `` is formatted as *YYYYMMDDhhmmss*. You can also specify a custom name. A restored object also includes a label with key `velero.io/restore-name` and value ``. - -By default, backup storage locations are created in read-write mode. However, during a restore, you can configure a backup storage location to be in read-only mode, which disables backup creation and deletion for the storage location. This is useful to ensure that no backups are inadvertently created or deleted during a restore scenario. - -You can optionally specify restore hooks to be executed during a restore or after resources are restored. For example, you might need to perform a custom database restore operation before the database application containers start. [More about restore hooks][11]. ## Backup workflow @@ -54,6 +45,32 @@ By default, `velero backup create` makes disk snapshots of any persistent volume ![19] +## Restores + +The **restore** operation allows you to restore all of the objects and persistent volumes from a previously created backup. You can also restore only a [filtered](resource-filtering.md) subset of objects and persistent volumes. Velero supports multiple namespace remapping--for example, in a single restore, objects in namespace "abc" can be recreated under namespace "def", and the objects in namespace "123" under "456". + +The default name of a restore is `-`, where `` is formatted as *YYYYMMDDhhmmss*. You can also specify a custom name. A restored object also includes a label with key `velero.io/restore-name` and value ``. + +By default, backup storage locations are created in read-write mode. However, during a restore, you can configure a backup storage location to be in read-only mode, which disables backup creation and deletion for the storage location. This is useful to ensure that no backups are inadvertently created or deleted during a restore scenario. + +You can optionally specify [restore hooks][11] to be executed during a restore or after resources are restored. For example, you might need to perform a custom database restore operation before the database application containers start. + +### Restore workflow + +When you run `velero restore create`: + +1. The Velero client makes a call to the Kubernetes API server to create a [`Restore`](api-types/restore.md) object. + +1. The `RestoreController` notices the new Restore object and performs validation. + +1. The `RestoreController` fetches the backup information from the object storage service. It then runs some preprocessing on the backed up resources to make sure the resources will work on the new cluster. For example, using the [backed-up API versions](#backed-up-api-versions) to verify that the restore resource will work on the target cluster. + +1. The `RestoreController` starts the restore process, restoring each eligible resource one at a time. + +By default, Velero performs a non-destructive restore, meaning that it won't delete any data on the target cluster. If a resource in the backup already exists in the target cluster, Velero will skip that resource. You can configure Velero to use an update policy instead using the [`--existing-resource-policy`](restore-reference.md#restore-existing-resource-policy) restore flag. When this flag is set to `update`, Velero will attempt to update an existing resource in the target cluster to match the resource from the backup. + +For more details about the Velero restore process, see the [Restore Reference](restore-reference.md) page. + ## Backed-up API versions Velero backs up resources using the Kubernetes API server's *preferred version* for each group/resource. When restoring a resource, this same API group/version must exist in the target cluster in order for the restore to be successful. diff --git a/site/content/docs/main/resource-filtering.md b/site/content/docs/main/resource-filtering.md index 18da47c891..02ae3d68d0 100644 --- a/site/content/docs/main/resource-filtering.md +++ b/site/content/docs/main/resource-filtering.md @@ -5,7 +5,7 @@ layout: docs *Filter objects by namespace, type, or labels.* -This page describes how to use the include and exclude flags with the `velero backup` and `velero restore` commands. Velero includes all objects in a backup or restore when no filtering options are used. +This page describes how to use the include and exclude flags with the `velero backup` and `velero restore` commands. By default Velero includes all objects in a backup or restore when no filtering options are used. ## Includes diff --git a/site/content/docs/main/restore-reference.md b/site/content/docs/main/restore-reference.md index bb14977bc5..0511f6512f 100644 --- a/site/content/docs/main/restore-reference.md +++ b/site/content/docs/main/restore-reference.md @@ -3,27 +3,12 @@ title: "Restore Reference" layout: docs --- -## Restoring Into a Different Namespace - -Velero can restore resources into a different namespace than the one they were backed up from. To do this, use the `--namespace-mappings` flag: - -```bash -velero restore create RESTORE_NAME \ - --from-backup BACKUP_NAME \ - --namespace-mappings old-ns-1:new-ns-1,old-ns-2:new-ns-2 -``` -## What happens when user removes restore objects -A **restore** object represents the restore operation. There are two types of deletion for restore objects: -1. Deleting with **`velero restore delete`**. -This command will delete the custom resource representing it, along with its individual log and results files. But, it will not delete any objects that were created by it from your cluster. -2. Deleting with **`kubectl -n velero delete restore`**. -This command will delete the custom resource representing the restore, but will not delete log/results files from object storage, or any objects that were created during the restore in your cluster. +The page outlines how to use the `velero restore` command, configuration options for restores, and describes the main process Velero uses to perform restores. ## Restore command-line options -To see all commands for restores, run : `velero restore --help` -To see all options associated with a specific command, provide the --help flag to that command. For example, **`velero restore create --help`** shows all options associated with the **create** command. +To see all commands for restores, run `velero restore --help`. -To list all options of restore, use **`velero restore --help`** +To see all options associated with a specific command, provide the `--help` flag to that command. For example, `velero restore create --help` shows all options associated with the `create` command. ```Usage: velero restore [command] @@ -36,57 +21,127 @@ Available Commands: logs Get restore logs ``` -## What happens to NodePorts when restoring Services +## Detailed Restore workflow -**Auto assigned** NodePorts **deleted** by default and Services get new **auto assigned** nodePorts after restore. +The following is an overview of Velero's restore process that starts after you run `velero restore create`. -**Explicitly specified** NodePorts auto detected using **`last-applied-config`** annotation and **preserved** after restore. NodePorts can be explicitly specified as .spec.ports[*].nodePort field on Service definition. +1. The Velero client makes a call to the Kubernetes API server to create a [`Restore`](api-types/restore.md) object. -#### Always Preserve NodePorts +1. The `RestoreController` notices the new Restore object and performs validation. -It is not always possible to set nodePorts explicitly on some big clusters because of operation complexity. Official Kubernetes documents states that preventing port collisions is responsibility of the user when explicitly specifying nodePorts: +1. The `RestoreController` fetches basic information about the backup being restored, like the [BackupStorageLocation](locations.md) (BSL). It also fetches a tarball of the cluster resources in the backup, any volumes that will be restored using Restic, and any volume snapshots to be restored. -``` -If you want a specific port number, you can specify a value in the `nodePort` field. The control plane will either allocate you that port or report that the API transaction failed. This means that you need to take care of possible port collisions yourself. You also have to use a valid port number, one that's inside the range configured for NodePort use. +1. The `RestoreController` then extracts the tarball of backup cluster resources to the /tmp folder and performs some pre-processing on the resources, including: + + * Sorting the resources to help Velero decide the [restore order](#resource-restore-order) to use. + + * Attempting to discover the resources by their Kubernetes [Group Version Resource (GVR)](https://kubernetes.io/docs/reference/using-api/api-concepts/). If a resource is not discoverable, Velero will exclude it from the restore. See more about how [Velero backs up API versions](#backed-up-api-versions). + + * Applying any configured [resource filters](resource-filtering.md). + + * Verify the target namespace, if you have configured [`--namespace-mappings`](#restoring-into-a-different-namespace) restore option. + + +1. The `RestoreController` begins restoring the eligible resources one at a time. Velero extracts the current resource into a Kubernetes resource object. Depending on the type of resource and restore options you specified, Velero will make the following modifications to the resource or preparations to the target cluster before attempting to create the resource: + + * The `RestoreController` makes sure the target namespace exists. If the target namespace does not exist, then the `RestoreController` will create a new one on the cluster. + + * If the resource is a Persistent Volume (PV), the `RestoreController` will [rename](#persistent-volume-rename) the PV and [remap](#restoring-into-a-different-namespace) its namespace. + + * If the resource is a Persistent Volume Claim (PVC), the `RestoreController` will modify the [PVC metadata](#pvc-restore). + + * Execute the resource’s `RestoreItemAction` [custom plugins](custom-plugins/), if you have configured one. + + * Update the resource object’s namespace if you've configured [namespace remapping](#restoring-into-a-different-namespace). + + * The `RestoreController` adds a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` with the restore name to the resource. This can help you easily identify restored resources and which backup they were restored from. + +1. The `RestoreController` creates the resource object on the target cluster. If the resource is a PV then the `RestoreController` will restore the PV data from the [durable snapshot](#durable-snapshot-pv-restore), [Restic](#restic-pv-restore), or [CSI snapshot](#csi-pv-restore) depending on how the PV was backed up. + + If the resource already exists in the target cluster, which is determined by the Kubernetes API during resource creation, the `RestoreController` will skip the resource. The only [exception](#restore-existing-resource-policy) are Service Accounts, which Velero will attempt to merge differences between the backed up ServiceAccount into the ServiceAccount on the target cluster. You can [change the default existing resource restore policy](#restore-existing-resource-policy) to update resources instead of skipping them using the `--existing-resource-policy`. + +1. Once the resource is created on the target cluster, Velero may take some additional steps or wait for additional processes to complete before moving onto the next resource to restore. -https://kubernetes.io/docs/concepts/services-networking/service/#nodeport + * If the resource is a Pod, the `RestoreController` will execute any [Restore Hooks](restore-hooks.md) and wait for the hook to finish. + * If the resource is a PV restored by Restic, the `RestoreController` waits for Restic’s restore to complete. The `RestoreController` sets a timeout for any resources restored with Restic during a restore. The default timeout is 4 hours, but you can configure this be setting using `--restic-timeout` restore option. + * If the resource is a Custom Resource Definition, the `RestoreController` waits for its availability in the cluster. The timeout is 1 minute. + + If any failures happen finishing these steps, the `RestoreController` will log an error in the restore result and will continue restoring. + +## Restore order + +By default, Velero will restore resources in the following order: + +* Custom Resource Definitions +* Mamespaces +* StorageClasses +* VolumeSnapshotClass +* VolumeSnapshotContents +* VolumeSnapshots +* PersistentVolumes +* PersistentVolumeClaims +* Secrets +* ConfigMaps +* ServiceAccounts +* LimitRanges +* Pods +* ReplicaSets +* Clusters +* ClusterResourceSets + +It's recommended that you use the default order for your restores. You are able to customize this order if you need to by setting the `--restore-resource-priorities` flag on the Velero server and specifying a different resource order. This customized order will apply to all future restores. You don't have to specify all resources in the `--restore-resource-priorities` flag. Velero will append resources not listed to the end of your customized list in alphabetical order. + +```shell +velero server \ +--restore-resource-priorities=customresourcedefinitions,namespaces,storageclasses,\ +volumesnapshotclass.snapshot.storage.k8s.io,volumesnapshotcontents.snapshot.storage.k8s.io,\ +volumesnapshots.snapshot.storage.k8s.io,persistentvolumes,persistentvolumeclaims,secrets,\ +configmaps,serviceaccounts,limitranges,pods,replicasets.apps,clusters.cluster.x-k8s.io,\ +clusterresourcesets.addons.cluster.x-k8s.io ``` -The clusters which are not explicitly specifying nodePorts still may need to restore original NodePorts in case of disaster. Auto assigned nodePorts most probably defined on Load Balancers which located front side of cluster. Changing all these nodePorts on Load Balancers is another operation complexity after disaster if nodePorts are changed. -Velero has a flag to let user deciding the preservation of nodePorts. **`velero restore create`** sub command has **`--preserve-nodeports`** flag to **preserve** Service nodePorts **always** regardless of nodePorts **explicitly specified** or **not**. This flag used for preserving the original nodePorts from backup and can be used as **`--preserve-nodeports`** or **`--preserve-nodeports=true`** +## Restoring Persistent Volumes and Persistent Volume Claims -If this flag given and/or set to true, Velero does not remove the nodePorts when restoring Service and tries to use the nodePorts which written on backup. +Velero has three approaches when restoring a PV, depending on how the backup was taken. -Trying to preserve nodePorts may cause **port conflicts** when restoring on situations below: +1. When restoring a snapshot, Velero statically creates the PV and then binds it to a restored PVC. Velero's PV rename and remap process is used only in this case because this is the only case where Velero creates the PV resource directly. +1. When restoring with Restic, Velero uses Kubernetes’ [dynamic provision process](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) to provision the PV after creating the PVC. In this case, the PV object is not actually created by Velero. +1. When restoring with the [CSI plugin](csi.md), the PV is created from a CSI snapshot by the CSI driver. Velero doesn’t create the PV directly. Instead Velero creates a PVC with its DataSource referring to the CSI VolumeSnapshot object. -- If the nodePort from the backup already allocated on the target cluster then Velero prints error log as shown below and continue to restore operation. +### Snapshot PV Restore - ``` - time="2020-11-23T12:58:31+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123125825 +PV data backed up by durable snapshots is restored by VolumeSnapshot plugins. Velero calls the plugins’ interface to create a volume from a snapshot. The plugin returns the volume’s `volumeID`. This ID is created by storage vendors and will be updated in the PV object created by Velero, so that the PV object is connected to the volume restored from a snapshot. - time="2020-11-23T12:58:31+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123125825 +### Restic PV Restore - time="2020-11-23T12:58:31+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123125825 +For more information on Restic restores, see the [Restic integration](restic.md#restore) page. - time="2020-11-23T12:58:31+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is already allocated" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123125825 - ``` +### CSI PV Restore +A PV backed up by CSI snapshots is restored by the [CSI plugin](csi). This happens when restoring the PVC object that has been snapshotted by CSI. The CSI VolumeSnapshot object name is specified with the PVC during backup as the annotation `velero.io/volume-snapshot-name`. After validating the VolumeSnapshot object, Velero updates the PVC by adding a `DataSource` field and setting its value to the VolumeSnapshot name. +### Persistent Volume Rename -- If the nodePort from the backup is not in the nodePort range of target cluster then Velero prints error log as below and continue to restore operation. Kubernetes default nodePort range is 30000-32767 but on the example cluster nodePort range is 20000-22767 and tried to restore Service with nodePort 31536 +When restoring PVs, if the PV being restored does not exist on the target cluster, Velero will create the PV using the name from the backup. Velero will rename a PV before restoring if both of the following conditions are met: - ``` - time="2020-11-23T13:09:17+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123130915 +1. The PV already exists on the target cluster. +1. The PV’s claim namespace has been [remapped](#restoring-into-a-different-namespace). - time="2020-11-23T13:09:17+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123130915 +If both conditions are met, Velero will create the PV with a new name. The new name is the prefix `velero-clone-` and a random UUID. Velero also preserves the original name of the PV by adding an annotation `velero.io/original-pv-name` to the restored PV object. - time="2020-11-23T13:09:17+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123130915 +If you attempt to restore the PV's referenced PVC into its original namespace without remapping the namespace, Velero will not rename the PV. If a PV's referenced PVC exists already for that namespace, the restored PV creation attempt will fail, with an `Already Exist` error from the Kubernetes API Server. - time="2020-11-23T13:09:17+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is not in the valid range. The range of valid ports is 20000-22767" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123130915 - ``` +### PVC Restore -## Changing PV/PVC Storage Classes +PVC objects are created the same way as other Kubernetes resources during a restore, with some specific changes: +* For a dynamic binding PVCs, Velero removes the fields related to bindings from the PVC object. This enables the default Kubernetes [dynamic binding process](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#binding) to be used for this PVC. The fields include: + * volumeName + * pv.kubernetes.io/bind-completed annotation + * pv.kubernetes.io/bound-by-controller annotation +* For a PVC that is bound by Velero Restore, if the target PV has been renamed by the [PV restore process](#persistent-volume-rename), the RestoreController renames the `volumeName` field of the PVC object. + +### Changing PV/PVC Storage Classes Velero can change the storage class of persistent volumes and persistent volume claims during restores. To configure a storage class mapping, create a config map in the Velero namespace like the following: @@ -115,7 +170,7 @@ data: : ``` -## Changing PVC selected-node +### Changing PVC selected-node Velero can update the selected-node annotation of persistent volume claim during restores, if selected-node doesn't exist in the cluster then it will remove the selected-node annotation from PersistentVolumeClaim. To configure a node mapping, create a config map in the Velero namespace like the following: @@ -142,3 +197,76 @@ data: # node name and the value is the new node name. : ``` + +## Restoring into a different namespace + +Velero can restore resources into a different namespace than the one they were backed up from. To do this, use the `--namespace-mappings` flag: + +```bash +velero restore create \ + --from-backup \ + --namespace-mappings old-ns-1:new-ns-1,old-ns-2:new-ns-2 +``` + +For example, A Persistent Volume object has a reference to the Persistent Volume Claim’s namespace in the field `Spec.ClaimRef.Namespace`. If you specify that Velero should remap the target namespace during the restore, Velero will change the `Spec.ClaimRef.Namespace` field on the PV object from `old-ns-1` to `new-ns-1`. + +## Restore existing resource policy + +By default, Velero is configured to be non-destructive during a restore. This means that it will never overwrite data that already exists in your cluster. When Velero attempts to create a resource during a restore, the resource being restored is compared to the existing resources on the target cluster by the Kubernetes API Server. If the resource already exists in the target cluster, Velero skips restoring the current resource and moves onto the next resource to restore, without making any changes to the target cluster. + +An exception to the default restore policy is ServiceAccounts. When restoring a ServiceAccount that already exists on the target cluster, Velero will attempt to merge the fields of the ServiceAccount from the backup into the existing ServiceAccount. Secrets and ImagePullSecrets are appended from the backed-up ServiceAccount. Velero adds any non-existing labels and annotations from the backed-up ServiceAccount to the existing resource, leaving the existing labels and annotations in place. + +You can change this policy for a restore by using the `--existing-resource-policy` restore flag. The available options are `none` (default) and `update`. If you choose to `update` existing resources during a restore (`--existing-resource-policy=update`), Velero will attempt to update an existing resource to match the resource being restored: + +* If the existing resource in the target cluster is the same as the resource Velero is attempting to restore, Velero will add a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` label with the restore name to the existing resource. If patching the labels fails, Velero adds a restore error and continues restoring the next resource. + +* If the existing resource in the target cluster is different from the backup, Velero will first try to patch the existing resource to match the backup resource. If the patch is successful, Velero will add a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` label with the restore name to the existing resource. If the patch fails, Velero adds a restore warning and tries to add the `velero.io/backup-name` and `velero.io/restore-name` labels on the resource. If the labels patch also fails, then Velero logs a restore error and continues restoring the next resource. + +You can also configure the existing resource policy in a [Restore](api-types/restore.md) object. + +## Removing a Restore object + +There are two ways to delete a Restore object: + +1. Deleting with `velero restore delete` will delete the Custom Resource representing the restore, along with its individual log and results files. It will not delete any objects that were created by the restore in your cluster. +2. Deleting with `kubectl -n velero delete restore` will delete the Custom Resource representing the restore. It will not delete restore log or results files from object storage, or any objects that were created during the restore in your cluster. + +## What happens to NodePorts when restoring Services + +During a restore, Velero deletes **Auto assigned** NodePorts by default and Services get new **auto assigned** nodePorts after restore. + +Velero auto detects **explicitly specified** NodePorts using **`last-applied-config`** annotation and they are **preserved** after restore. NodePorts can be explicitly specified as `.spec.ports[*].nodePort` field on Service definition. + +### Always Preserve NodePorts + +It is not always possible to set nodePorts explicitly on some big clusters because of operational complexity. As the Kubernetes [NodePort documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) states, "if you want a specific port number, you can specify a value in the `nodePort` field. The control plane will either allocate you that port or report that the API transaction failed. This means that you need to take care of possible port collisions yourself. You also have to use a valid port number, one that's inside the range configured for NodePort use."" + +The clusters which are not explicitly specifying nodePorts may still need to restore original NodePorts in the event of a disaster. Auto assigned nodePorts are typically defined on Load Balancers located in front of cluster. Changing all these nodePorts on Load Balancers is another operation complexity you are responsible for updating after disaster if nodePorts are changed. + +Use the `velero restore create ` command's `--preserve-nodeports` flag to preserve Service nodePorts always, regardless of whether nodePorts are explicitly specified or not. This flag is used for preserving the original nodePorts from a backup and can be used as `--preserve-nodeports` or `--preserve-nodeports=true`. If this flag is present, Velero will not remove the nodePorts when restoring a Service, but will try to use the nodePorts from the backup. + +Trying to preserve nodePorts may cause port conflicts when restoring on situations below: + +- If the nodePort from the backup is already allocated on the target cluster then Velero prints error log as shown below and continues the restore operation. + + ``` + time="2020-11-23T12:58:31+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is already allocated" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123125825 + ``` + +- If the nodePort from the backup is not in the nodePort range of target cluster then Velero prints error log as below and continues with the restore operation. Kubernetes default nodePort range is 30000-32767 but on the example cluster nodePort range is 20000-22767 and tried to restore Service with nodePort 31536. + + ``` + time="2020-11-23T13:09:17+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is not in the valid range. The range of valid ports is 20000-22767" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123130915 + ``` From 56f72797db51d1d07659956e0bc8a8cae04796d2 Mon Sep 17 00:00:00 2001 From: Ming Date: Sat, 4 Jun 2022 05:33:48 +0000 Subject: [PATCH 181/366] Fix nightly running failure Signed-off-by: Ming --- test/e2e/backups/deletion.go | 19 +++------ test/e2e/backups/ttl.go | 17 ++++---- test/e2e/basic/resources-check/namespaces.go | 2 +- test/e2e/bsl-mgmt/deletion.go | 8 ++-- .../e2e/orderedresources/ordered_resources.go | 15 +++++++ test/e2e/upgrade/upgrade.go | 2 +- test/e2e/util/kibishii/kibishii_utils.go | 2 +- test/e2e/util/providers/common.go | 31 -------------- test/e2e/util/velero/velero_utils.go | 41 ++++++++++++------- 9 files changed, 62 insertions(+), 75 deletions(-) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index a531865f19..89d1186318 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -25,7 +25,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/pkg/errors" - waitutil "k8s.io/apimachinery/pkg/util/wait" . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" @@ -147,7 +146,7 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa if useVolumeSnapshots { snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, deletionTest, backupName, KibishiiPodNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - err = WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { @@ -159,7 +158,7 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa return err } if useVolumeSnapshots { - err = WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + err = SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, backupName, snapshotCheckPoint) if err != nil { @@ -172,17 +171,9 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa return err } if useVolumeSnapshots { - err = waitutil.PollImmediate(30*time.Second, 3*time.Minute, - func() (bool, error) { - err := SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, - bslConfig, backupName, snapshotCheckPoint) - if err == nil { - return true, nil - } - return false, err - }) - if err != nil { + if err := SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + bslConfig, backupName, snapshotCheckPoint); err != nil { return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index 6b024808e7..439ada6939 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -52,8 +52,8 @@ func (b *TTL) Init() { b.testNS = "backup-ttl-test-" + UUIDgen.String() b.backupName = "backup-ttl-test-" + UUIDgen.String() b.restoreName = "restore-ttl-test-" + UUIDgen.String() - b.ctx, _ = context.WithTimeout(context.Background(), time.Duration(time.Minute*30)) - b.ttl = time.Duration(10 * time.Minute) + b.ctx, _ = context.WithTimeout(context.Background(), time.Duration(time.Hour)) + b.ttl = time.Duration(20 * time.Minute) } @@ -87,14 +87,13 @@ func TTLTest() { It("Backups in object storage should be synced to a new Velero successfully", func() { test.Init() - oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS), func() { - Expect(CreateNamespace(oneHourTimeout, client, test.testNS)).To(Succeed(), + Expect(CreateNamespace(test.ctx, client, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) }) By("Deploy sample workload of Kibishii", func() { - Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, + Expect(KibishiiPrepareBeforeBackup(test.ctx, client, VeleroCfg.CloudProvider, test.testNS, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) }) @@ -108,7 +107,7 @@ func TTLTest() { BackupCfg.TTL = test.ttl By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + Expect(VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") return "Fail to backup workload" }) @@ -119,14 +118,14 @@ func TTLTest() { if VeleroCfg.CloudProvider == "vsphere" { // TODO - remove after upload progress monitoring is implemented By("Waiting for vSphere uploads to complete", func() { - Expect(WaitForVSphereUploadCompletion(oneHourTimeout, time.Hour, + Expect(WaitForVSphereUploadCompletion(test.ctx, time.Hour, test.testNS)).To(Succeed()) }) } snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 2, test.testNS, test.backupName, KibishiiPodNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, test.backupName, snapshotCheckPoint)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") } @@ -183,7 +182,7 @@ func TTLTest() { By("PersistentVolume snapshots should be deleted", func() { if useVolumeSnapshots { - Expect(WaitUntilSnapshotsNotExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, test.backupName, snapshotCheckPoint)).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") } diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index 366e3adfb2..ae6e15455a 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -57,7 +57,7 @@ func (m *MultiNSBackup) Init() error { } } else { m.NamespacesTotal = 2 - m.TimeoutDuration = time.Minute * 5 + m.TimeoutDuration = time.Minute * 10 m.TestMsg = &TestMSG{ Text: "When I create 2 namespaces should be successfully backed up and restored", FailedMSG: "Failed to successfully backup and restore multiple namespaces", diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 91307f19e0..fd75ba1801 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -220,7 +220,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_1), func() { snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.AdditionalBSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) @@ -236,7 +236,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { BSLConfig = VeleroCfg.BSLConfig } - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, BSLCredentials, VeleroCfg.AdditionalBSLBucket, BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) }) @@ -318,7 +318,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) @@ -333,7 +333,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { } snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, BSLCredentials, VeleroCfg.AdditionalBSLBucket, BSLConfig, backupName_2, snapshotCheckPoint)).To(Succeed()) }) diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go index ff141470d2..2a9fe4c975 100644 --- a/test/e2e/orderedresources/ordered_resources.go +++ b/test/e2e/orderedresources/ordered_resources.go @@ -69,6 +69,8 @@ func ScheduleOrderedResources() { Expect(DeleteNamespace(test.Ctx, test.Client, test.Namespace, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.Namespace)) err = VeleroScheduleDelete(test.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.ScheduleName) Expect(err).To(Succeed(), fmt.Sprintf("Failed to delete schedule with err %v", err)) + err = test.DeleteBackups() + Expect(err).To(Succeed(), fmt.Sprintf("Failed to delete backups with err %v", err)) }() By(fmt.Sprintf("Prepare workload as target to backup in base namespace %s", test.Namespace), func() { @@ -189,3 +191,16 @@ func (o *OrderedResources) CreateResources() error { } return nil } + +func (o *OrderedResources) DeleteBackups() error { + backupList := new(velerov1api.BackupList) + if err := o.Client.Kubebuilder.List(o.Ctx, backupList, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + return fmt.Errorf("failed to list backup object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) + } + for _, backup := range backupList.Items { + if err := VeleroBackupDelete(o.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup.Name); err != nil { + return err + } + } + return nil +} diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 794feba25a..7461697a25 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -190,7 +190,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade snapshotCheckPoint, err := GetSnapshotCheckPoint(client, VeleroCfg, 2, upgradeNamespace, backupName, KibishiiPodNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") - Expect(WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, backupName, snapshotCheckPoint)).To(Succeed()) }) diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 45d4d8f8ee..8406cda55b 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -96,7 +96,7 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re if err != nil { return errors.Wrap(err, "Fail to get snapshot checkpoint") } - err = WaitUntilSnapshotsExistInCloud(VeleroCfg.CloudProvider, + err = SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, veleroCfg.BSLConfig, backupName, snapshotCheckPoint) if err != nil { diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index ee5dd47da0..4ca8ac1436 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -24,8 +24,6 @@ import ( "github.com/pkg/errors" - waitutil "k8s.io/apimachinery/pkg/util/wait" - . "github.com/vmware-tanzu/velero/test/e2e" velero "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -111,35 +109,6 @@ func DeleteObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPr return nil } -func WaitUntilSnapshotsExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { - err := waitutil.PollImmediate(30*time.Second, 3*time.Minute, - func() (bool, error) { - err := SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, - bslConfig, backupName, snapshotCheckPoint) - if err == nil { - return true, nil - } - return false, err - }) - return err -} - -func WaitUntilSnapshotsNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { - err := waitutil.PollImmediate(30*time.Second, 3*time.Minute, - func() (bool, error) { - snapshotCheckPoint.ExpectCount = 0 - err := SnapshotsShouldNotExistInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, - bslConfig, backupName, snapshotCheckPoint) - if err == nil { - return true, nil - } - return false, err - }) - return err -} - func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName string, snapshotCheckPoint SnapshotCheckPoint) error { fmt.Printf("|| VERIFICATION || - Snapshots should not exist in cloud, backup %s\n", backupName) snapshotCheckPoint.ExpectCount = 0 diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 0a4a014b35..30126d219c 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -235,20 +235,23 @@ func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st func checkSchedulePhase(ctx context.Context, veleroCLI, veleroNamespace, scheduleName string) error { checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "schedule", "get", scheduleName, "-ojson") - jsonBuf, err := CMDExecWithOutput(checkCMD) - if err != nil { - return err - } - schedule := velerov1api.Schedule{} - err = json.Unmarshal(*jsonBuf, &schedule) - if err != nil { - return err - } + return wait.PollImmediate(time.Second*5, time.Minute*2, func() (bool, error) { + jsonBuf, err := CMDExecWithOutput(checkCMD) + if err != nil { + return false, err + } + schedule := velerov1api.Schedule{} + err = json.Unmarshal(*jsonBuf, &schedule) + if err != nil { + return false, err + } - if schedule.Status.Phase != velerov1api.SchedulePhaseEnabled { - return errors.Errorf("Unexpected restore phase got %s, expecting %s", schedule.Status.Phase, velerov1api.SchedulePhaseEnabled) - } - return nil + if schedule.Status.Phase != velerov1api.SchedulePhaseEnabled { + fmt.Printf("Unexpected schedule phase got %s, expecting %s, still waiting...", schedule.Status.Phase, velerov1api.SchedulePhaseEnabled) + return false, nil + } + return true, nil + }) } func CheckScheduleWithResourceOrder(ctx context.Context, veleroCLI, veleroNamespace, scheduleName string, order map[string]string) error { @@ -370,6 +373,16 @@ func VeleroBackupExec(ctx context.Context, veleroCLI string, veleroNamespace str return checkBackupPhase(ctx, veleroCLI, veleroNamespace, backupName, velerov1api.BackupPhaseCompleted) } +func VeleroBackupDelete(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error { + args := []string{"--namespace", veleroNamespace, "delete", "backup", backupName, "--confirm"} + return VeleroCmdExec(ctx, veleroCLI, args) +} + +func VeleroRestoreDelete(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string) error { + args := []string{"--namespace", veleroNamespace, "delete", "restore", restoreName, "--confirm"} + return VeleroCmdExec(ctx, veleroCLI, args) +} + func VeleroScheduleDelete(ctx context.Context, veleroCLI string, veleroNamespace string, scheduleName string) error { args := []string{"--namespace", veleroNamespace, "delete", "schedule", scheduleName, "--confirm"} return VeleroCmdExec(ctx, veleroCLI, args) @@ -514,7 +527,7 @@ func VeleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNa // WaitForVSphereUploadCompletion waits for uploads started by the Velero Plug-in for vSphere to complete // TODO - remove after upload progress monitoring is implemented func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, namespace string) error { - err := wait.PollImmediate(time.Minute, timeout, func() (bool, error) { + err := wait.PollImmediate(time.Second*5, timeout, func() (bool, error) { checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.spec.resourceHandle.name}{\"=\"}{.status.phase}{\"\\n\"}{end}'") fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) From 6fa4d7d606c5fe813bde9521b0223dc8071678a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 6 Jun 2022 22:56:29 +0800 Subject: [PATCH 182/366] Mark in-progress CRs as failed when starting the server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark in-progress CRs as failed when starting the server Fixes #4953 Signed-off-by: Wenkai Yin(尹文开) --- pkg/cmd/cli/restic/server.go | 99 ++++++++++++++++++- pkg/cmd/server/server.go | 65 ++++++++++++ pkg/controller/backup_controller.go | 22 +---- pkg/controller/backup_controller_test.go | 27 +---- .../pod_volume_backup_controller.go | 17 +--- .../pod_volume_backup_controller_test.go | 6 +- .../pod_volume_restore_controller.go | 31 ++---- .../pod_volume_restore_controller_test.go | 65 ++---------- pkg/controller/restore_controller.go | 21 +--- pkg/controller/restore_controller_test.go | 31 +----- 10 files changed, 201 insertions(+), 183 deletions(-) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 5a37ffee8f..b056c21898 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -22,7 +22,9 @@ import ( "net/http" "os" "strings" + "time" + "github.com/apex/log" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" @@ -32,11 +34,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -50,6 +54,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" + "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -105,6 +110,7 @@ type resticServer struct { metrics *metrics.ServerMetrics metricsAddress string namespace string + nodeName string } func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAddress string) (*resticServer, error) { @@ -121,11 +127,13 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAd v1.AddToScheme(scheme) storagev1api.AddToScheme(scheme) + nodeName := os.Getenv("NODE_NAME") + // use a field selector to filter to only pods scheduled on this node. cacheOption := cache.Options{ SelectorsByObject: cache.SelectorsByObject{ &v1.Pod{}: { - Field: fields.Set{"spec.nodeName": os.Getenv("NODE_NAME")}.AsSelector(), + Field: fields.Set{"spec.nodeName": nodeName}.AsSelector(), }, }, } @@ -145,6 +153,7 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAd mgr: mgr, metricsAddress: metricAddress, namespace: factory.Namespace(), + nodeName: nodeName, } // the cache isn't initialized yet when "validatePodVolumesHostPath" is called, the client returned by the manager cannot @@ -173,7 +182,9 @@ func (s *resticServer) run() { }() s.metrics = metrics.NewResticServerMetrics() s.metrics.RegisterAllMetrics() - s.metrics.InitResticMetricsForNode(os.Getenv("NODE_NAME")) + s.metrics.InitResticMetricsForNode(s.nodeName) + + s.markInProgressCRsFailed() s.logger.Info("Starting controllers") @@ -193,7 +204,7 @@ func (s *resticServer) run() { Clock: clock.RealClock{}, Metrics: s.metrics, CredsFileStore: credentialFileStore, - NodeName: os.Getenv("NODE_NAME"), + NodeName: s.nodeName, FileSystem: filesystem.NewFileSystem(), ResticExec: restic.BackupExec{}, Log: s.logger, @@ -229,7 +240,7 @@ func (s *resticServer) validatePodVolumesHostPath(client kubernetes.Interface) e } } - pods, err := client.CoreV1().Pods("").List(s.ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("spec.nodeName=%s,status.phase=Running", os.Getenv("NODE_NAME"))}) + pods, err := client.CoreV1().Pods("").List(s.ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("spec.nodeName=%s,status.phase=Running", s.nodeName)}) if err != nil { return errors.WithStack(err) } @@ -259,3 +270,83 @@ func (s *resticServer) validatePodVolumesHostPath(client kubernetes.Interface) e return nil } + +// if there is a restarting during the reconciling of pvbs/pvrs/etc, these CRs may be stuck in progress status +// markInProgressCRsFailed tries to mark the in progress CRs as failed when starting the server to avoid the issue +func (s *resticServer) markInProgressCRsFailed() { + // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here + client, err := ctrlclient.New(s.mgr.GetConfig(), ctrlclient.Options{Scheme: s.mgr.GetScheme()}) + if err != nil { + log.WithError(errors.WithStack(err)).Error("failed to create client") + return + } + + s.markInProgressPVBsFailed(client) + + s.markInProgressPVRsFailed(client) +} + +func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { + pvbs := &velerov1api.PodVolumeBackupList{} + if err := client.List(s.ctx, pvbs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { + log.WithError(errors.WithStack(err)).Error("failed to list podvolumebackups") + return + } + for _, pvb := range pvbs.Items { + if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseInProgress { + log.Debugf("the status of podvolumebackup %q is %q, skip", pvb.GetName(), pvb.Status.Phase) + continue + } + if pvb.Spec.Node != s.nodeName { + log.Debugf("the node of podvolumebackup %q is %q, not %q, skip", pvb.GetName(), pvb.Spec.Node, s.nodeName) + continue + } + updated := pvb.DeepCopy() + updated.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed + updated.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, updated.Status.Phase) + updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := kube.Patch(s.ctx, &pvb, updated, client); err != nil { + log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) + continue + } + log.WithField("podvolumebackup", pvb.GetName()).Warn(updated.Status.Message) + } +} + +func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { + pvrs := &velerov1api.PodVolumeRestoreList{} + if err := client.List(s.ctx, pvrs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { + log.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") + return + } + for _, pvr := range pvrs.Items { + if pvr.Status.Phase != velerov1api.PodVolumeRestorePhaseInProgress { + log.Debugf("the status of podvolumerestore %q is %q, skip", pvr.GetName(), pvr.Status.Phase) + continue + } + + pod := &v1.Pod{} + if err := client.Get(s.ctx, types.NamespacedName{ + Namespace: pvr.Spec.Pod.Namespace, + Name: pvr.Spec.Pod.Name, + }, pod); err != nil { + log.WithError(errors.WithStack(err)).Errorf("failed to get pod \"%s/%s\" of podvolumerestore %q", + pvr.Spec.Pod.Namespace, pvr.Spec.Pod.Name, pvr.GetName()) + continue + } + if pod.Spec.NodeName != s.nodeName { + log.Debugf("the node of pod referenced by podvolumebackup %q is %q, not %q, skip", pvr.GetName(), pod.Spec.NodeName, s.nodeName) + continue + } + + updated := pvr.DeepCopy() + updated.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed + updated.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, updated.Status.Phase) + updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := kube.Patch(s.ctx, &pvr, updated, client); err != nil { + log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) + continue + } + log.WithField("podvolumerestore", pvr.GetName()).Warn(updated.Status.Message) + } +} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index be59459c14..3c83ba75a3 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -74,6 +74,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/vmware-tanzu/velero/internal/storage" @@ -378,6 +379,8 @@ func (s *server) run() error { return err } + markInProgressCRsFailed(s.ctx, s.mgr.GetConfig(), s.mgr.GetScheme(), s.namespace, s.logger) + if err := s.runControllers(s.config.defaultVolumeSnapshotLocations); err != nil { return err } @@ -925,3 +928,65 @@ func (w *CSIInformerFactoryWrapper) WaitForCacheSync(stopCh <-chan struct{}) map } return nil } + +// if there is a restarting during the reconciling of backups/restores/etc, these CRs may be stuck in progress status +// markInProgressCRsFailed tries to mark the in progress CRs as failed when starting the server to avoid the issue +func markInProgressCRsFailed(ctx context.Context, cfg *rest.Config, scheme *runtime.Scheme, namespace string, log logrus.FieldLogger) { + // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here + client, err := ctrlclient.New(cfg, ctrlclient.Options{Scheme: scheme}) + if err != nil { + log.WithError(errors.WithStack(err)).Error("failed to create client") + return + } + + markInProgressBackupsFailed(ctx, client, namespace, log) + + markInProgressRestoresFailed(ctx, client, namespace, log) +} + +func markInProgressBackupsFailed(ctx context.Context, client ctrlclient.Client, namespace string, log logrus.FieldLogger) { + backups := &velerov1api.BackupList{} + if err := client.List(ctx, backups, &ctrlclient.MatchingFields{"metadata.namespace": namespace}); err != nil { + log.WithError(errors.WithStack(err)).Error("failed to list backups") + return + } + + for _, backup := range backups.Items { + if backup.Status.Phase != velerov1api.BackupPhaseInProgress { + log.Debugf("the status of backup %q is %q, skip", backup.GetName(), backup.Status.Phase) + continue + } + updated := backup.DeepCopy() + updated.Status.Phase = velerov1api.BackupPhaseFailed + updated.Status.FailureReason = fmt.Sprintf("get a backup with status %q during the server starting, mark it as %q", velerov1api.BackupPhaseInProgress, updated.Status.Phase) + updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&backup)); err != nil { + log.WithError(errors.WithStack(err)).Errorf("failed to patch backup %q", backup.GetName()) + continue + } + log.WithField("backup", backup.GetName()).Warn(updated.Status.FailureReason) + } +} + +func markInProgressRestoresFailed(ctx context.Context, client ctrlclient.Client, namespace string, log logrus.FieldLogger) { + restores := &velerov1api.RestoreList{} + if err := client.List(ctx, restores, &ctrlclient.MatchingFields{"metadata.namespace": namespace}); err != nil { + log.WithError(errors.WithStack(err)).Error("failed to list restores") + return + } + for _, restore := range restores.Items { + if restore.Status.Phase != velerov1api.RestorePhaseInProgress { + log.Debugf("the status of restore %q is %q, skip", restore.GetName(), restore.Status.Phase) + continue + } + updated := restore.DeepCopy() + updated.Status.Phase = velerov1api.RestorePhaseFailed + updated.Status.FailureReason = fmt.Sprintf("get a restore with status %q during the server starting, mark it as %q", velerov1api.RestorePhaseInProgress, updated.Status.Phase) + updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&restore)); err != nil { + log.WithError(errors.WithStack(err)).Errorf("failed to patch restore %q", restore.GetName()) + continue + } + log.WithField("restore", restore.GetName()).Warn(updated.Status.FailureReason) + } +} diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 4aea251563..aeadc85004 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -157,12 +157,13 @@ func NewBackupController( backup := obj.(*velerov1api.Backup) switch backup.Status.Phase { - case "", velerov1api.BackupPhaseNew, velerov1api.BackupPhaseInProgress: + case "", velerov1api.BackupPhaseNew: + // only process new backups default: c.logger.WithFields(logrus.Fields{ "backup": kubeutil.NamespaceAndName(backup), "phase": backup.Status.Phase, - }).Debug("Backup is not new or in-progress, skipping") + }).Debug("Backup is not new, skipping") return } @@ -250,22 +251,7 @@ func (c *backupController) processBackup(key string) error { // this key (even though it was a no-op). switch original.Status.Phase { case "", velerov1api.BackupPhaseNew: - case velerov1api.BackupPhaseInProgress: - // A backup may stay in-progress forever because of - // 1) the controller restarts during the processing of a backup - // 2) the backup with in-progress status isn't updated to completed or failed status successfully - // So we try to mark such Backups as failed to avoid it - updated := original.DeepCopy() - updated.Status.Phase = velerov1api.BackupPhaseFailed - updated.Status.FailureReason = fmt.Sprintf("got a Backup with unexpected status %q, this may be due to a restart of the controller during the backing up, mark it as %q", - velerov1api.BackupPhaseInProgress, updated.Status.Phase) - updated.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - _, err = patchBackup(original, updated, c.client) - if err != nil { - return errors.Wrapf(err, "error updating Backup status to %s", updated.Status.Phase) - } - log.Warn(updated.Status.FailureReason) - return nil + // only process new backups default: return nil } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index fa1a95a382..29e7ecc9c5 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -94,6 +94,11 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { key: "velero/backup-1", backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Result(), }, + { + name: "InProgress backup is not processed", + key: "velero/backup-1", + backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result(), + }, { name: "Completed backup is not processed", key: "velero/backup-1", @@ -135,28 +140,6 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { } } -func TestMarkInProgressBackupAsFailed(t *testing.T) { - backup := defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result() - clientset := fake.NewSimpleClientset(backup) - sharedInformers := informers.NewSharedInformerFactory(clientset, 0) - logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatText) - - c := &backupController{ - genericController: newGenericController("backup-test", logger), - client: clientset.VeleroV1(), - lister: sharedInformers.Velero().V1().Backups().Lister(), - clock: &clock.RealClock{}, - } - require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup)) - - err := c.processBackup(fmt.Sprintf("%s/%s", backup.Namespace, backup.Name)) - require.Nil(t, err) - - res, err := clientset.VeleroV1().Backups(backup.Namespace).Get(context.TODO(), backup.Name, metav1.GetOptions{}) - require.NoError(t, err) - assert.Equal(t, velerov1api.BackupPhaseFailed, res.Status.Phase) -} - func TestProcessBackupValidationFailures(t *testing.T) { defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 3656bd081f..b8089a8352 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -93,20 +93,9 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ switch pvb.Status.Phase { case "", velerov1api.PodVolumeBackupPhaseNew: - case velerov1api.PodVolumeBackupPhaseInProgress: - original := pvb.DeepCopy() - pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed - pvb.Status.Message = fmt.Sprintf("got a PodVolumeBackup with unexpected status %q, this may be due to a restart of the controller during the backing up, mark it as %q", - velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) - pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - if err := kube.Patch(ctx, original, &pvb, r.Client); err != nil { - log.WithError(err).Error("error updating PodVolumeBackup status") - return ctrl.Result{}, err - } - log.Warn(pvb.Status.Message) - return ctrl.Result{}, nil + // Only process new items. default: - log.Debug("PodVolumeBackup is not new or in-progress, not processing") + log.Debug("PodVolumeBackup is not new, not processing") return ctrl.Result{}, nil } @@ -298,7 +287,7 @@ func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.Po func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed - pvb.Status.Message = msg + pvb.Status.Message = errors.WithMessage(err, msg).Error() pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} if err = kube.Patch(ctx, original, pvb, r.Client); err != nil { diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index 1a67b6995f..ffc5f662cb 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -179,16 +179,16 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), expectedRequeue: ctrl.Result{}, }), - Entry("in progress phase pvb on same node should be marked as failed", request{ + Entry("in progress phase pvb on same node should not be processed", request{ pvb: pvbBuilder(). Phase(velerov1api.PodVolumeBackupPhaseInProgress). Node("test_node"). Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - expectedProcessed: true, + expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). - Phase(velerov1api.PodVolumeBackupPhaseFailed). + Phase(velerov1api.PodVolumeBackupPhaseInProgress). Result(), expectedRequeue: ctrl.Result{}, }), diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index a35ca74673..6666e32baf 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -134,6 +134,11 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req } func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logrus.FieldLogger, pvr *velerov1api.PodVolumeRestore) (bool, *corev1api.Pod, error) { + if !isPVRNew(pvr) { + log.Debug("PodVolumeRestore is not new, skip") + return false, nil, nil + } + // we filter the pods during the initialization of cache, if we can get a pod here, the pod must be in the same node with the controller // so we don't need to compare the node anymore pod := &corev1api.Pod{} @@ -146,28 +151,6 @@ func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logr return false, nil, err } - // the status checking logic must be put after getting the PVR's pod because that the getting pod logic - // makes sure the PVR's pod is on the same node with the controller. The controller should only process - // the PVRs on the same node - switch pvr.Status.Phase { - case "", velerov1api.PodVolumeRestorePhaseNew: - case velerov1api.PodVolumeRestorePhaseInProgress: - original := pvr.DeepCopy() - pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed - pvr.Status.Message = fmt.Sprintf("got a PodVolumeRestore with unexpected status %q, this may be due to a restart of the controller during the restoring, mark it as %q", - velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) - pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if err := kube.Patch(ctx, original, pvr, c.Client); err != nil { - log.WithError(err).Error("Unable to update status to failed") - return false, nil, err - } - log.Warn(pvr.Status.Message) - return false, nil, nil - default: - log.Debug("PodVolumeRestore is not new or in-progress, skip") - return false, nil, nil - } - if !isResticInitContainerRunning(pod) { log.Debug("Pod is not running restic-wait init container, skip") return false, nil, nil @@ -209,6 +192,10 @@ func (c *PodVolumeRestoreReconciler) findVolumeRestoresForPod(pod client.Object) return requests } +func isPVRNew(pvr *velerov1api.PodVolumeRestore) bool { + return pvr.Status.Phase == "" || pvr.Status.Phase == velerov1api.PodVolumeRestorePhaseNew +} + func isResticInitContainerRunning(pod *corev1api.Pod) bool { // Restic wait container can be anywhere in the list of init containers, but must be running. i := getResticInitContainerIndex(pod) diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 220e004263..69bc313a34 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -27,7 +27,6 @@ import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -44,81 +43,45 @@ func TestShouldProcess(t *testing.T) { obj *velerov1api.PodVolumeRestore pod *corev1api.Pod shouldProcessed bool - expectedPhase velerov1api.PodVolumeRestorePhase }{ { - name: "Unable to get pvr's pod should not be processed", + name: "InProgress phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ - Spec: velerov1api.PodVolumeRestoreSpec{ - Pod: corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pod-1", - }, - }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: "", + Phase: velerov1api.PodVolumeRestorePhaseInProgress, }, }, shouldProcessed: false, }, { - name: "InProgress phase pvr should be marked as failed", + name: "Completed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ ObjectMeta: metav1.ObjectMeta{ Namespace: "velero", Name: "pvr-1", }, - Spec: velerov1api.PodVolumeRestoreSpec{ - Pod: corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pod-1", - }, - }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseInProgress, - }, - }, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", + Phase: velerov1api.PodVolumeRestorePhaseCompleted, }, }, shouldProcessed: false, - expectedPhase: velerov1api.PodVolumeRestorePhaseFailed, }, { - name: "Completed phase pvr should not be processed", + name: "Failed phase pvr should not be processed", obj: &velerov1api.PodVolumeRestore{ ObjectMeta: metav1.ObjectMeta{ Namespace: "velero", Name: "pvr-1", }, - Spec: velerov1api.PodVolumeRestoreSpec{ - Pod: corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pod-1", - }, - }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseCompleted, - }, - }, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", + Phase: velerov1api.PodVolumeRestorePhaseFailed, }, }, shouldProcessed: false, }, { - name: "Failed phase pvr should not be processed", + name: "Unable to get pvr's pod should not be processed", obj: &velerov1api.PodVolumeRestore{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: "pvr-1", - }, Spec: velerov1api.PodVolumeRestoreSpec{ Pod: corev1api.ObjectReference{ Namespace: "ns-1", @@ -126,13 +89,7 @@ func TestShouldProcess(t *testing.T) { }, }, Status: velerov1api.PodVolumeRestoreStatus{ - Phase: velerov1api.PodVolumeRestorePhaseFailed, - }, - }, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns-1", - Name: "pod-1", + Phase: "", }, }, shouldProcessed: false, @@ -244,12 +201,6 @@ func TestShouldProcess(t *testing.T) { shouldProcess, _, _ := c.shouldProcess(ctx, c.logger, ts.obj) require.Equal(t, ts.shouldProcessed, shouldProcess) - if len(ts.expectedPhase) > 0 { - pvr := &velerov1api.PodVolumeRestore{} - err := c.Client.Get(ctx, types.NamespacedName{Namespace: ts.obj.Namespace, Name: ts.obj.Name}, pvr) - require.Nil(t, err) - assert.Equal(t, ts.expectedPhase, pvr.Status.Phase) - } }) } } diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 910bd56cd7..092b90002f 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -144,12 +144,13 @@ func NewRestoreController( restore := obj.(*api.Restore) switch restore.Status.Phase { - case "", api.RestorePhaseNew, api.RestorePhaseInProgress: + case "", api.RestorePhaseNew: + // only process new restores default: c.logger.WithFields(logrus.Fields{ "restore": kubeutil.NamespaceAndName(restore), "phase": restore.Status.Phase, - }).Debug("Restore is not new or in-progress, skipping") + }).Debug("Restore is not new, skipping") return } @@ -201,21 +202,7 @@ func (c *restoreController) processQueueItem(key string) error { // is ("" | New) switch restore.Status.Phase { case "", api.RestorePhaseNew: - case api.RestorePhaseInProgress: - // A restore may stay in-progress forever because of - // 1) the controller restarts during the processing of a restore - // 2) the restore with in-progress status isn't updated to completed or failed status successfully - // So we try to mark such restores as failed to avoid it - updated := restore.DeepCopy() - updated.Status.Phase = api.RestorePhaseFailed - updated.Status.FailureReason = fmt.Sprintf("got a Restore with unexpected status %q, this may be due to a restart of the controller during the restore, mark it as %q", - api.RestorePhaseInProgress, updated.Status.Phase) - _, err = patchRestore(restore, updated, c.restoreClient) - if err != nil { - return errors.Wrapf(err, "error updating Restore status to %s", updated.Status.Phase) - } - log.Warn(updated.Status.FailureReason) - return nil + // only process new restores default: return nil } diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index d7ee568a5c..16dd444e94 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -20,7 +20,6 @@ import ( "bytes" "context" "encoding/json" - "fmt" "io/ioutil" "testing" "time" @@ -171,6 +170,11 @@ func TestProcessQueueItemSkips(t *testing.T) { restoreKey: "foo/bar", expectError: true, }, + { + name: "restore with phase InProgress does not get processed", + restoreKey: "foo/bar", + restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseInProgress).Result(), + }, { name: "restore with phase Completed does not get processed", restoreKey: "foo/bar", @@ -222,31 +226,6 @@ func TestProcessQueueItemSkips(t *testing.T) { } } -func TestMarkInProgressRestoreAsFailed(t *testing.T) { - var ( - restore = builder.ForRestore("velero", "bar").Phase(velerov1api.RestorePhaseInProgress).Result() - client = fake.NewSimpleClientset(restore) - sharedInformers = informers.NewSharedInformerFactory(client, 0) - logger = velerotest.NewLogger() - ) - - c := restoreController{ - genericController: newGenericController("restore-test", logger), - restoreClient: client.VeleroV1(), - restoreLister: sharedInformers.Velero().V1().Restores().Lister(), - } - - err := sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore) - require.Nil(t, err) - - err = c.processQueueItem(fmt.Sprintf("%s/%s", restore.Namespace, restore.Name)) - require.Nil(t, err) - - res, err := c.restoreClient.Restores(restore.Namespace).Get(context.Background(), restore.Name, metav1.GetOptions{}) - require.Nil(t, err) - assert.Equal(t, velerov1api.RestorePhaseFailed, res.Status.Phase) -} - func TestProcessQueueItem(t *testing.T) { defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result() From febe79f3340b46414928c550d5ba6f475d7ae860 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 7 Jun 2022 19:44:02 +0800 Subject: [PATCH 183/366] Fix E2E test [Backups][Deletion][Restic] on GCP. Signed-off-by: Xun Jiang --- changelogs/unreleased/4968-jxun | 1 + test/e2e/util/providers/gcloud_utils.go | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/4968-jxun diff --git a/changelogs/unreleased/4968-jxun b/changelogs/unreleased/4968-jxun new file mode 100644 index 0000000000..5a47988625 --- /dev/null +++ b/changelogs/unreleased/4968-jxun @@ -0,0 +1 @@ +Fix E2E test [Backups][Deletion][Restic] on GCP. \ No newline at end of file diff --git a/test/e2e/util/providers/gcloud_utils.go b/test/e2e/util/providers/gcloud_utils.go index cc6f2acee4..c7388cdb07 100644 --- a/test/e2e/util/providers/gcloud_utils.go +++ b/test/e2e/util/providers/gcloud_utils.go @@ -77,30 +77,26 @@ func (s GCSStorage) DeleteObjectsInBucket(cloudCredentialsFile, bslBucket, bslPr } bucket := client.Bucket(bslBucket) iter := bucket.Objects(context.Background(), q) - deleted := false for { obj, err := iter.Next() - if err == iterator.Done { - fmt.Println(err) - if !deleted { - return errors.New("|| UNEXPECTED ||Backup object is not exist and was not deleted in object store") - } - return nil - } if err != nil { + fmt.Printf("GCP bucket iterator exists due to %s\n", err) + if err == iterator.Done { + return nil + } return errors.WithStack(err) } + if obj.Name == bslPrefix { fmt.Println("Ignore GCS prefix itself") continue } // Only delete folder named as backupObject under prefix if strings.Contains(obj.Name, bslPrefix+backupObject+"/") { + fmt.Printf("Delete item: %s\n", obj.Name) if err = bucket.Object(obj.Name).Delete(ctx); err != nil { return errors.Wrapf(err, fmt.Sprintf("Fail to delete object %s in bucket %s", obj.Name, bslBucket)) } - fmt.Printf("Delete item: %s\n", obj.Name) - deleted = true } } } From 34087fe5f4c83df1cd926dab26dc4469074919cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 7 Jun 2022 21:07:44 +0800 Subject: [PATCH 184/366] Disable status as sub resource in CRDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When enabling the status as sub resource in CRD, the status will be ignored when creating the CR with status, this will cause issues when syncing backups/pvbs Fixes #4950 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/4972-ywk253100 | 1 + .../velero.io_backupstoragelocations.yaml | 3 +- .../bases/velero.io_deletebackuprequests.yaml | 3 +- .../v1/bases/velero.io_downloadrequests.yaml | 2 - .../v1/bases/velero.io_podvolumebackups.yaml | 3 +- .../v1/bases/velero.io_podvolumerestores.yaml | 3 +- .../bases/velero.io_resticrepositories.yaml | 3 +- config/crd/v1/bases/velero.io_schedules.yaml | 3 +- .../bases/velero.io_serverstatusrequests.yaml | 2 - config/crd/v1/crds/crds.go | 16 ++--- go.mod | 3 - go.sum | 69 ------------------- .../velero/v1/backupstoragelocation_types.go | 1 - .../velero/v1/delete_backup_request_types.go | 1 - pkg/apis/velero/v1/download_request_types.go | 1 - pkg/apis/velero/v1/pod_volume_backup_types.go | 1 - pkg/apis/velero/v1/pod_volume_restore_type.go | 1 - pkg/apis/velero/v1/restic_repository_types.go | 1 - pkg/apis/velero/v1/schedule_types.go | 1 - .../velero/v1/server_status_request_types.go | 1 - pkg/cmd/cli/restic/server.go | 25 ++++--- pkg/controller/backup_controller.go | 24 +------ pkg/controller/backup_deletion_controller.go | 9 +-- .../backup_storage_location_controller.go | 11 +-- pkg/controller/download_request_controller.go | 11 +-- .../download_request_controller_test.go | 4 +- .../pod_volume_backup_controller.go | 8 +-- .../pod_volume_restore_controller.go | 8 +-- .../restic_repository_controller.go | 52 ++++++-------- .../restic_repository_controller_test.go | 23 ++----- pkg/controller/schedule_controller.go | 15 ++-- .../server_status_request_controller.go | 12 +--- pkg/util/kube/utils.go | 10 --- pkg/util/kube/utils_test.go | 23 ------- 34 files changed, 82 insertions(+), 272 deletions(-) create mode 100644 changelogs/unreleased/4972-ywk253100 diff --git a/changelogs/unreleased/4972-ywk253100 b/changelogs/unreleased/4972-ywk253100 new file mode 100644 index 0000000000..f15a06d83e --- /dev/null +++ b/changelogs/unreleased/4972-ywk253100 @@ -0,0 +1 @@ +Disable status as sub resource in CRDs \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml index 002126d046..ac2362c2b6 100644 --- a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml @@ -172,8 +172,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml index 076e0af46c..c860885933 100644 --- a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml +++ b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml @@ -72,8 +72,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_downloadrequests.yaml b/config/crd/v1/bases/velero.io_downloadrequests.yaml index 45f229c44b..4bc7463217 100644 --- a/config/crd/v1/bases/velero.io_downloadrequests.yaml +++ b/config/crd/v1/bases/velero.io_downloadrequests.yaml @@ -85,8 +85,6 @@ spec: type: object served: true storage: true - subresources: - status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_podvolumebackups.yaml b/config/crd/v1/bases/velero.io_podvolumebackups.yaml index efd4f219c6..69993f0d4c 100644 --- a/config/crd/v1/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/v1/bases/velero.io_podvolumebackups.yaml @@ -186,8 +186,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index c07c1c25f2..6f77cb67c9 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -166,8 +166,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_resticrepositories.yaml b/config/crd/v1/bases/velero.io_resticrepositories.yaml index f9a534cb1e..2517661369 100644 --- a/config/crd/v1/bases/velero.io_resticrepositories.yaml +++ b/config/crd/v1/bases/velero.io_resticrepositories.yaml @@ -85,8 +85,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index de2734d40c..c391b1f5f1 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -464,8 +464,7 @@ spec: type: object served: true storage: true - subresources: - status: {} + subresources: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/bases/velero.io_serverstatusrequests.yaml b/config/crd/v1/bases/velero.io_serverstatusrequests.yaml index 7efbd9694a..5c05af450a 100644 --- a/config/crd/v1/bases/velero.io_serverstatusrequests.yaml +++ b/config/crd/v1/bases/velero.io_serverstatusrequests.yaml @@ -77,8 +77,6 @@ spec: type: object served: true storage: true - subresources: - status: {} status: acceptedNames: kind: "" diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 314c400b8b..748e958c12 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,15 +30,15 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMs\xdc6\f\xbd\xef\xaf\xc0\xa4\x87\xb43\x916\x99\x1e\xda\xd9[\xeb\xe4\x90i\xea\xf1\xac]_:=p)\xacĚ\"Y\x02\\\xc7\xfd\xf5\x1d\x90\xd2~h\xb5\xb6s(o\x02A\xf0\x11xx\xa4\x16UU-T0\xf7\x18\xc9x\xb7\x02\x15\f~et\xf2E\xf5\xc3\xcfT\x1b\xbf\xdc}X<\x18\u05ec\xe0*\x11\xfb~\x8d\xe4S\xd4\xf8\x11\xb7\xc6\x196\xde-zd\xd5(V\xab\x05\x80rγ\x123\xc9'\x80\xf6\x8e\xa3\xb7\x16cբ\xab\x1f\xd2\x067\xc9\xd8\x06c\x0e>n\xbd{_\xffT\xbf_\x00\xe8\x88y\xf9\x9d\xe9\x91X\xf5a\x05.Y\xbb\x00p\xaa\xc7\x154\xfe\xd1Y\xaf\x9a\x88\xff$$\xa6z\x87\x16\xa3\xaf\x8d_P@-\x9b\xb6ѧ\xb0\x82\xc3DY;\x00*\x87\xf98\x84Y\x970y\xc6\x1a\xe2\xdf\xe6f\xbf\x98\xc1#\xd8\x14\x95=\a\x91'ɸ6Y\x15Ϧ\x17\x00\xa4}\xc0\x15\\\v\x8c\xa046\v\x80\xe1\xec\x19V5\x9cn\xf7\xa1\x84\xd2\x1d\xf6\xaa\xe0\x05\xf0\x01\xdd/7\x9f\xef\u007f\xbc=1\x034H:\x9a\xc09\x83\x13\xcc`\b\x14\f\b\x80\xfd\x1e\x14(\a*\xb2\xd9*Ͱ\x8d\xbe\x87\x8d\xd2\x0f)\xec\xa3\x02\xf8\xcdߨ\x19\x88}T-\xbe\x03J\xba\x03%\xf1\x8a+X\xdf\xc2\xd6X\xac\xf7\x8bB\xf4\x01#\x9b1\xcbe\x1c\x91\xeb\xc8:\x01\xfeV\xceV\xbc\xa0\x11V!\x01w8\xe6\a\x9b!\x1d\xe0\xb7\xc0\x9d!\x88\x18\"\x12\xba³\x93\xc0 N\xca\r'\xa8\xe1\x16\xa3\x84\x01\xea|\xb2\x8d\x90q\x87\x91!\xa2\xf6\xad3\xff\xeec\x93dH6\xb5\x8aG:\x1c\x86q\x8c\xd1)\v;e\x13\xbe\x03\xe5\x1a\xe8\xd5\x13D\xccyJ\xee(^v\xa1\x1a~\xf7\x11\xc1\xb8\xad_A\xc7\x1ch\xb5\\\xb6\x86ǦҾ\xef\x933\xfc\xb4\xcc\xfda6\x89}\xa4e\x83;\xb4K2m\xa5\xa2\xee\f\xa3\xe6\x14q\xa9\x82\xa92t\x97\x1b\xab\xee\x9b\xef\xe2І\xf4\xf6\x04+?\t͈\xa3q\xed\xd1D\xe6\xfc3\x15\x10\xd6\x17\u0094\xa5\xe5\x14\x87D\x8bI\xb2\xb3\xfet{\a\xe3ֹ\x18\xd3\xec\x17\xe6\xec\x17ҡ\x04\x920\xe3\xb6\x18K\x113\xf3$&\xba&x\xe38\u007fhk\xd0M\xd3Oi\xd3\x1b\xa6\x91\xccR\xab\x1a\xae\xb2\xd2\xc0\x06!\x85F165|vp\xa5z\xb4W\x8a\xf0\u007f/\x80d\x9a*I\xec\xebJp,\x92S璵\xa3\x89Q\xc9.\xd4k\xd2\xea\xb7\x01\xb5TO\x12(+\xcd\xd6\xe8\xdc\x1a\xb0\xf5\x11ԡ\xf3\x87\x04\xd6'\x91\xe7;7\x83S\xb1E\x9eZ'X\uec93l\xffةS\xa1\xf9\x1e\xeb\xb6\x16\xad\xa0\x01HQ\x8f\x1f곈\x971\xc0,{g\x91\x8c$\x964H^E\nD\xa4\x8e1\x9do-\x03]\xea\xe77\xa8\xe0\u05cc\xf9\x8bo\x9f\x9d\xbf\xf2\x8e\x85\xee\xcf:\xdd{\x9bz\xbcu*P\xe7_\xf0\xfd\xccؿ\xces\xbc\x90\xf7\x97Թ\xe3\x1aE\xca\xf1\xf2!\x06\x875R\xb2\x17\xb6\xbb@\xebq\xe4\xeb\xeb\xe5\x1a\xc9\x058\xd6H\x96\x14MG\x90gAt\xc8H\ayy4\xdc\xcdF\x04x\xec\x8c\xee\xf2\xc2\\`Q.\"\xafMցo\x87/}a\"ΐ\xac\xca\xe4\x9b1\v\xf83\xf3\x85n\xbe\xb4A5tث\x14\x81\x15'\xfa\x06M\xc8\xfec\xaau\x8a\x11\x1d\x0fQ\xf2\x1d9]\xf0ZQ\x18;\xe9\x8f\xf5\x97\x17\x94\xe1\xe3\xc13\xbf\x02\x95q\x05M\x88X\x91i\xe5f\x979цܳ\xe7\xc9(\xe3\xf4\xa5q\x9a\xa8ي\xe2\xd7`bV\xc0\x17 ~\xda;\x16\x01CW.\xa7\xe9[*\aD\xca\x17\xbfV\xd3'\x87\x8c\rB\x83\x16\x19\x1b\xd8<\x15%~\"\xc6\xfe\x1c\xf7\xd6\xc7^\xf1\n\xe4Ҫ\xd8\xcc\xd0H\u07bbjcq\x05\x1c\xd3%\x96\xcd\x1e\xcb\f\xb8\xa8\x97\x15\\\xe3\xe3\x8c\xf5&z\x8dDx\xdeF\x17O2\xdb\x04gF\x92\x97Es\x94\xa5\xe1\xc1:X\x0e-\xa3\xb4\xc6\xc0\xd8\\O\xff\x02\u07bc9y\xd6\xe7O\xed]c\xca\x0f\f\xfc\xf9עD\xc5\xe6~|\xad\x8b\xf1\xbf\x00\x00\x00\xff\xff\xef_\x1d\xd3:\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ars\xf4\\d\xb50\xe1\x8f?Ϛ5*\x92\x04\vW\xa5y\xb5߶??\xe7\x1f\xe1\xe9z\xfe\x99h\xe5\xddn{\x05\xff\xfd?gP\x99\xe3\x87\xf0>=U\xfe_\x00\x00\x00\xff\xff\x11z\x10\b\t`\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V͎\xe44\x10\xbe\xe7)J\xcba/$\xbd+\x0e\xa0\xdc\xd0\xc0a\x05\x8cFӫ\xb9 \x0en\xa7\xd2mƱCU\xb9\x87\x06\xf1\xee\xc8vҝN\xd2̀\x84o\xb1\xeb端~REY\x96\x85\xea\xcd\x13\x12\x1b\xefjP\xbd\xc1\xdf\x05]\xfc\xe2\xea\xf9\x1b\xae\x8c\xdf\x1c?\x16\xcf\xc655\xdc\x05\x16\xdf=\"\xfb@\x1a\xbf\xc3\xd68#ƻ\xa2CQ\x8d\x12U\x17\x00\xca9/*^s\xfc\x04\xd0\xde\tyk\x91\xca=\xba\xea9\xecp\x17\x8cm\x90\x92\xf1\xd1\xf5\xf1C\xf5u\xf5\xa1\x00ЄI\xfd\xb3\xe9\x90Eu}\r.X[\x008\xd5a\r\x8c\x14\x95DI`\xc2\xdf\x02\xb2puD\x8b\xe4+\xe3\v\xeeQG\xc7{\xf2\xa1\xaf\xe1\xf2\x90\xf5\aP9\xa0m2\xb5M\xa6\x1e\xb3\xa9\xf4j\r\xcb\x0f\xb7$~4\x83To\x03)\xbb\x0e(\t\xf0\xc1\x93\xdc_\x9c\x96\xc0L\xf9Ÿ}\xb0\x8aV\x95\v\x00־\xc7\x1a\x92n\xaf46\x05\xc0\xc0T\xb2U\x0e\\\x1c?fs\xfa\x80\x9d\xcaN\x00|\x8f\xeeۇOO_m\xaf\xae\x01\x1adM\xa6\x97\xc4\xf7Jd`\x18\x14\f(@<(\xad\x91\x19t B'\x90Q\x82q\xad\xa7.\xe5\xe8l\x1a@\xed|\x10\x90\x03\xc2S\xa2|\x88\xac:\x8b\xf4\xe4{$1#\x1b\x83ڥ\xfa&\xb73\xac\xefc8Y\n\x9aXv\xc8\xc9\xd3@\t6\x03\x03\xe0[\x90\x83a \xec\t\x19\x9d\xccQ&~ZP\x0e\xfc\xeeW\xd4R\r9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } diff --git a/go.mod b/go.mod index cef992cbc2..640e78078d 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,6 @@ require ( k8s.io/client-go v0.22.2 k8s.io/klog v1.0.0 k8s.io/kube-aggregator v0.19.12 - sigs.k8s.io/cluster-api v1.0.0 sigs.k8s.io/controller-runtime v0.10.2 sigs.k8s.io/yaml v1.3.0 ) @@ -63,7 +62,6 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect @@ -71,7 +69,6 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect - github.com/gobuffalo/flect v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect diff --git a/go.sum b/go.sum index 8f61a62ce4..eccd53764e 100644 --- a/go.sum +++ b/go.sum @@ -34,7 +34,6 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -96,9 +95,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -121,7 +117,6 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.28.2 h1:j5IXG9CdyLfcVfICqo1PXVv+rua+QQHbkXuvuU/JF+8= @@ -138,7 +133,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bombsimon/logrusr v1.1.0 h1:Y03FI4Z/Shyrc9jF26vuaUbnPxC5NMJnTtJA/3Lihq8= github.com/bombsimon/logrusr v1.1.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= @@ -149,7 +143,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -162,10 +155,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0= -github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.13 h1:ld5RswmH1xjqBUEukw4QxC1PakLNNoVlsZEV8FGwoV8= -github.com/coredns/corefile-migration v1.0.13/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -184,17 +173,13 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/drone/envsubst/v2 v2.0.0-20210615175204-7bf45dbf5372/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= @@ -213,14 +198,10 @@ github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMi github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -228,7 +209,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -264,8 +244,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= -github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -309,7 +287,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -326,8 +303,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -371,7 +346,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -381,22 +355,17 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -407,11 +376,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -465,7 +431,6 @@ github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyP github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -478,7 +443,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= @@ -488,34 +452,27 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= @@ -540,7 +497,6 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -557,13 +513,10 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -573,7 +526,6 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -599,17 +551,14 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -634,7 +583,6 @@ github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= @@ -650,7 +598,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -744,7 +691,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -812,7 +758,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -891,8 +836,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -992,7 +935,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1196,7 +1138,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1238,7 +1179,6 @@ k8s.io/apimachinery v0.19.12/go.mod h1:9eb44nUQSsz9QZiilFRuMj3ZbTmoWolU8S2gnXoRM k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.19.12/go.mod h1:ldZAZTNIKfMMv/UUEhk6UyTXC0/34iRdNFHo+MJOPc4= -k8s.io/apiserver v0.22.2 h1:TdIfZJc6YNhu2WxeAOWq1TvukHF0Sfx0+ln4XK9qnL4= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= k8s.io/cli-runtime v0.22.2 h1:fsd9rFk9FSaVq4SUq1fM27c8CFGsYZUJ/3BkgmjYWuY= k8s.io/cli-runtime v0.22.2/go.mod h1:tkm2YeORFpbgQHEK/igqttvPTRIHFRz5kATlw53zlMI= @@ -1246,15 +1186,12 @@ k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= k8s.io/client-go v0.19.12/go.mod h1:BAGKQraZ6fDmXhT46pGXWZQQqN7P4E0BJux0+9O6Gt0= k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= -k8s.io/cluster-bootstrap v0.22.2 h1:jP6Nkp3CdSfr50cAn/7WGsNS52zrwMhvr0V+E3Vkh/w= -k8s.io/cluster-bootstrap v0.22.2/go.mod h1:ZkmQKprEqvrUccMnbRHISsMscA1dsQ8SffM9nHq6CgE= k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/code-generator v0.19.12/go.mod h1:ADrDvaUQWGn4a8lX0ONtzb7uFmDRQOMSYIMk1qWIAx8= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.19.12/go.mod h1:tpwExE0sY3A7CwtlxGL7SnQOdQfUlnFybT6GmAD+z/s= k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= -k8s.io/component-helpers v0.22.2/go.mod h1:+N61JAR9aKYSWbnLA88YcFr9K/6ISYvRNybX7QW7Rs8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1269,8 +1206,6 @@ k8s.io/kube-aggregator v0.19.12/go.mod h1:K76wPd03pSHEmS1FgJOcpryac5C3va4cbCvSu+ k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubectl v0.22.2/go.mod h1:BApg2j0edxLArCOfO0ievI27EeTQqBDMNU9VQH734iQ= -k8s.io/metrics v0.22.2/go.mod h1:GUcsBtpsqQD1tKFS/2wCKu4ZBowwRncLOJH1rgWs3uw= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= @@ -1280,13 +1215,9 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/cluster-api v1.0.0 h1:GcVA2ObQTXo/+jzSLWPy4Bd3NeiwJyAB8n19kyJIotA= -sigs.k8s.io/cluster-api v1.0.0/go.mod h1:V230kMSaYENTUcx1QRkoRCklb3vfphQGV3/z4ODNGWo= sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= -sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs= -sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index 6a0a917341..e44671222d 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -102,7 +102,6 @@ type BackupStorageLocationStatus struct { // +kubebuilder:resource:shortName=bsl // +kubebuilder:object:generate=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Backup Storage Location status such as Available/Unavailable" // +kubebuilder:printcolumn:name="Last Validated",type="date",JSONPath=".status.lastValidationTime",description="LastValidationTime is the last time the backup store location was validated" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" diff --git a/pkg/apis/velero/v1/delete_backup_request_types.go b/pkg/apis/velero/v1/delete_backup_request_types.go index 6e14d28d7e..8c7b1fa098 100644 --- a/pkg/apis/velero/v1/delete_backup_request_types.go +++ b/pkg/apis/velero/v1/delete_backup_request_types.go @@ -56,7 +56,6 @@ type DeleteBackupRequestStatus struct { // +kubebuilder:object:root=true // +kubebuilder:object:generate=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="BackupName",type="string",JSONPath=".spec.backupName",description="The name of the backup to be deleted" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="The status of the deletion request" diff --git a/pkg/apis/velero/v1/download_request_types.go b/pkg/apis/velero/v1/download_request_types.go index 7ca5dcb17e..a8e1a51b82 100644 --- a/pkg/apis/velero/v1/download_request_types.go +++ b/pkg/apis/velero/v1/download_request_types.go @@ -85,7 +85,6 @@ type DownloadRequestStatus struct { // +kubebuilder:object:root=true // +kubebuilder:object:generate=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // DownloadRequest is a request to download an artifact from backup object storage, such as a backup // log file. diff --git a/pkg/apis/velero/v1/pod_volume_backup_types.go b/pkg/apis/velero/v1/pod_volume_backup_types.go index 64f18f7316..c532f096de 100644 --- a/pkg/apis/velero/v1/pod_volume_backup_types.go +++ b/pkg/apis/velero/v1/pod_volume_backup_types.go @@ -112,7 +112,6 @@ type PodVolumeBackupStatus struct { // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:object:root=true // +kubebuilder:object:generate=true -// +kubebuilder:subresource:status type PodVolumeBackup struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/apis/velero/v1/pod_volume_restore_type.go b/pkg/apis/velero/v1/pod_volume_restore_type.go index 95f51cf50b..45bca8e248 100644 --- a/pkg/apis/velero/v1/pod_volume_restore_type.go +++ b/pkg/apis/velero/v1/pod_volume_restore_type.go @@ -87,7 +87,6 @@ type PodVolumeRestoreStatus struct { // +kubebuilder:object:generate=true // +kubebuilder:object:root=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.pod.namespace",description="Namespace of the pod containing the volume to be restored" // +kubebuilder:printcolumn:name="Pod",type="string",JSONPath=".spec.pod.name",description="Name of the pod containing the volume to be restored" // +kubebuilder:printcolumn:name="Volume",type="string",JSONPath=".spec.volume",description="Name of the volume to be restored" diff --git a/pkg/apis/velero/v1/restic_repository_types.go b/pkg/apis/velero/v1/restic_repository_types.go index e075f5f9eb..8e315592fd 100644 --- a/pkg/apis/velero/v1/restic_repository_types.go +++ b/pkg/apis/velero/v1/restic_repository_types.go @@ -71,7 +71,6 @@ type ResticRepositoryStatus struct { // +kubebuilder:object:root=true // +kubebuilder:object:generate=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type ResticRepository struct { diff --git a/pkg/apis/velero/v1/schedule_types.go b/pkg/apis/velero/v1/schedule_types.go index ab950eab7f..077a375ecf 100644 --- a/pkg/apis/velero/v1/schedule_types.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -83,7 +83,6 @@ type ScheduleStatus struct { // +kubebuilder:object:generate=true // +kubebuilder:object:root=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="Status of the schedule" // +kubebuilder:printcolumn:name="Schedule",type="string",JSONPath=".spec.schedule",description="A Cron expression defining when to run the Backup" // +kubebuilder:printcolumn:name="LastBackup",type="date",JSONPath=".status.lastBackup",description="The last time a Backup was run for this schedule" diff --git a/pkg/apis/velero/v1/server_status_request_types.go b/pkg/apis/velero/v1/server_status_request_types.go index a87f6500e8..98e15a0b59 100644 --- a/pkg/apis/velero/v1/server_status_request_types.go +++ b/pkg/apis/velero/v1/server_status_request_types.go @@ -28,7 +28,6 @@ import ( // +kubebuilder:resource:shortName=ssr // +kubebuilder:object:generate=true // +kubebuilder:storageversion -// +kubebuilder:subresource:status // ServerStatusRequest is a request to access current status information about // the Velero server. diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index b056c21898..ae593306b0 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -54,7 +54,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" - "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -301,15 +300,15 @@ func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { log.Debugf("the node of podvolumebackup %q is %q, not %q, skip", pvb.GetName(), pvb.Spec.Node, s.nodeName) continue } - updated := pvb.DeepCopy() - updated.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed - updated.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, updated.Status.Phase) - updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := kube.Patch(s.ctx, &pvb, updated, client); err != nil { + original := pvb.DeepCopy() + pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed + pvb.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) + pvb.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := client.Patch(s.ctx, &pvb, ctrlclient.MergeFrom(original)); err != nil { log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) continue } - log.WithField("podvolumebackup", pvb.GetName()).Warn(updated.Status.Message) + log.WithField("podvolumebackup", pvb.GetName()).Warn(pvb.Status.Message) } } @@ -339,14 +338,14 @@ func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { continue } - updated := pvr.DeepCopy() - updated.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed - updated.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, updated.Status.Phase) - updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := kube.Patch(s.ctx, &pvr, updated, client); err != nil { + original := pvr.DeepCopy() + pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed + pvr.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) + pvr.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} + if err := client.Patch(s.ctx, &pvr, ctrlclient.MergeFrom(original)); err != nil { log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) continue } - log.WithField("podvolumerestore", pvr.GetName()).Warn(updated.Status.Message) + log.WithField("podvolumerestore", pvr.GetName()).Warn(pvr.Status.Message) } } diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index aeadc85004..d9a90b8ef2 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -70,7 +70,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" - "sigs.k8s.io/cluster-api/util/patch" kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -943,10 +942,9 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api // in backup deletion. if modifyVSCFlag { logger.Debugf("Patching VolumeSnapshotContent %s", vsc.Name) - _, err := c.patchVolumeSnapshotContent(vsc, func(req *snapshotv1api.VolumeSnapshotContent) { - req.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentRetain - }) - if err != nil { + original := vsc.DeepCopy() + vsc.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentRetain + if err := c.kbClient.Patch(context.Background(), vsc, kbclient.MergeFrom(original)); err != nil { logger.Errorf("fail to modify VolumeSnapshotContent %s DeletionPolicy to Retain: %s", vsc.Name, err.Error()) return } @@ -972,22 +970,6 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api wg.Wait() } -func (c *backupController) patchVolumeSnapshotContent(req *snapshotv1api.VolumeSnapshotContent, mutate func(*snapshotv1api.VolumeSnapshotContent)) (*snapshotv1api.VolumeSnapshotContent, error) { - patchHelper, err := patch.NewHelper(req, c.kbClient) - if err != nil { - return nil, errors.Wrap(err, "fail to get patch helper.") - } - - // Mutate - mutate(req) - - if err := patchHelper.Patch(context.TODO(), req); err != nil { - return nil, errors.Wrapf(err, "fail to patch VolumeSnapshotContent %s", req.Name) - } - - return req, nil -} - // recreateVolumeSnapshotContent will delete then re-create VolumeSnapshotContent, // because some parameter in VolumeSnapshotContent Spec is immutable, e.g. VolumeSnapshotRef // and Source. Source is updated to let csi-controller thinks the VSC is statically provsisioned with VS. diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 67c53dd7b4..9cbfe1e8d9 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" kubeerrs "k8s.io/apimachinery/pkg/util/errors" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "github.com/vmware-tanzu/velero/internal/delete" @@ -458,13 +457,9 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba } func (r *backupDeletionReconciler) patchDeleteBackupRequest(ctx context.Context, req *velerov1api.DeleteBackupRequest, mutate func(*velerov1api.DeleteBackupRequest)) (*velerov1api.DeleteBackupRequest, error) { - patchHelper, err := patch.NewHelper(req, r.Client) - if err != nil { - return nil, errors.Wrap(err, "unable to get the patch helper") - } - // Mutate + original := req.DeepCopy() mutate(req) - if err := patchHelper.Patch(ctx, req); err != nil { + if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil { return nil, errors.Wrap(err, "error patching the deletebackuprquest") } return req, nil diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index 627247be10..ec35a89167 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -26,7 +26,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" @@ -102,12 +101,8 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr } func() { - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(&location, r.Client) - if err != nil { - log.WithError(err).Error("Error getting a patch helper to update BackupStorageLocation") - return - } + var err error + original := location.DeepCopy() defer func() { location.Status.LastValidationTime = &metav1.Time{Time: time.Now().UTC()} if err != nil { @@ -121,7 +116,7 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable location.Status.Message = "" } - if err := patchHelper.Patch(r.Ctx, &location); err != nil { + if err := r.Client.Patch(r.Ctx, &location, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating BackupStorageLocation phase") } }() diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index b83b91ab19..a633daf0d2 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -25,7 +25,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -68,16 +67,10 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, errors.WithStack(err) } - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(downloadRequest, r.Client) - if err != nil { - log.WithError(err).Error("Error getting a patch helper to update this resource") - return ctrl.Result{}, errors.WithStack(err) - } - + original := downloadRequest.DeepCopy() defer func() { // Always attempt to Patch the downloadRequest object and status after each reconciliation. - if err := patchHelper.Patch(ctx, downloadRequest); err != nil { + if err := r.Client.Patch(ctx, downloadRequest, kbclient.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating download request") return } diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index af16f2a3ee..f84bdaed0c 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -143,9 +143,9 @@ var _ = Describe("Download Request Reconciler", func() { Expect(apierrors.IsNotFound(err)).To(BeTrue()) } else { if test.downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseProcessed { - Expect(instance).To(Equal(test.downloadRequest)) + Expect(instance.Status).To(Equal(test.downloadRequest.Status)) } else { - Expect(instance).ToNot(Equal(test.downloadRequest)) + Expect(instance.Status).ToNot(Equal(test.downloadRequest.Status)) } Expect(err).To(BeNil()) } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index b8089a8352..b1121d5e57 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -105,7 +105,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseInProgress pvb.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} - if err := kube.Patch(ctx, original, &pvb, r.Client); err != nil { + if err := r.Client.Patch(ctx, &pvb, client.MergeFrom(original)); err != nil { log.WithError(err).Error("error updating PodVolumeBackup status") return ctrl.Result{}, err } @@ -181,7 +181,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ if emptySnapshot { pvb.Status.Message = "volume was empty so no snapshot was taken" } - if err = kube.Patch(ctx, original, &pvb, r.Client); err != nil { + if err = r.Client.Patch(ctx, &pvb, client.MergeFrom(original)); err != nil { log.WithError(err).Error("error updating PodVolumeBackup status") return ctrl.Result{}, err } @@ -278,7 +278,7 @@ func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.Po return func(progress velerov1api.PodVolumeOperationProgress) { original := pvb.DeepCopy() pvb.Status.Progress = progress - if err := kube.Patch(context.Background(), original, pvb, r.Client); err != nil { + if err := r.Client.Patch(context.Background(), pvb, client.MergeFrom(original)); err != nil { log.WithError(err).Error("error update progress") } } @@ -290,7 +290,7 @@ func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pv pvb.Status.Message = errors.WithMessage(err, msg).Error() pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - if err = kube.Patch(ctx, original, pvb, r.Client); err != nil { + if err = r.Client.Patch(ctx, pvb, client.MergeFrom(original)); err != nil { log.WithError(err).Error("error updating PodVolumeBackup status") return ctrl.Result{}, err } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 6666e32baf..2b9cfd4073 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -104,7 +104,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req original := pvr.DeepCopy() pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress pvr.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()} - if err = kube.Patch(ctx, original, pvr, c.Client); err != nil { + if err = c.Patch(ctx, pvr, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Unable to update status to in progress") return ctrl.Result{}, err } @@ -114,7 +114,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed pvr.Status.Message = err.Error() pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if e := kube.Patch(ctx, original, pvr, c.Client); e != nil { + if e := c.Patch(ctx, pvr, client.MergeFrom(original)); e != nil { log.WithError(err).Error("Unable to update status to failed") } @@ -125,7 +125,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req original = pvr.DeepCopy() pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if err = kube.Patch(ctx, original, pvr, c.Client); err != nil { + if err = c.Patch(ctx, pvr, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Unable to update status to completed") return ctrl.Result{}, err } @@ -334,7 +334,7 @@ func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api. return func(progress velerov1api.PodVolumeOperationProgress) { original := req.DeepCopy() req.Status.Progress = progress - if err := kube.Patch(context.Background(), original, req, c.Client); err != nil { + if err := c.Patch(context.Background(), req, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Unable to update PodVolumeRestore progress") } } diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 7ac899f40d..d4d0ef68df 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -88,15 +87,8 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(resticRepo, r.Client) - if err != nil { - log.WithError(err).Error("Error getting a patch helper to update restic repository resource") - return ctrl.Result{}, errors.WithStack(err) - } - if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.ResticRepositoryPhaseNew { - if err = r.initializeRepo(ctx, resticRepo, log, patchHelper); err != nil { + if err := r.initializeRepo(ctx, resticRepo, log); err != nil { log.WithError(err).Error("error initialize repository") return ctrl.Result{}, errors.WithStack(err) } @@ -114,15 +106,15 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) switch resticRepo.Status.Phase { case velerov1api.ResticRepositoryPhaseReady: - return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, patchHelper, log) + return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, log) case velerov1api.ResticRepositoryPhaseNotReady: - return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, patchHelper, log) + return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, log) } return ctrl.Result{}, nil } -func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger, patchHelper *patch.Helper) error { +func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid @@ -132,12 +124,12 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { - return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) + return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { rr.Status.Message = err.Error() rr.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady @@ -148,7 +140,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { rr.Spec.ResticIdentifier = repoIdentifier if rr.Spec.MaintenanceFrequency.Duration <= 0 { @@ -159,10 +151,10 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } if err := ensureRepo(req, r.repositoryManager); err != nil { - return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) + return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { rr.Status.Phase = velerov1api.ResticRepositoryPhaseReady rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) @@ -187,7 +179,7 @@ func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.Repositor return nil } -func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { log.Debug("resticRepositoryController.runMaintenanceIfDue") now := r.clock.Now() @@ -204,12 +196,12 @@ func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *vel log.Debug("Pruning repo") if err := r.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - if patchErr := patchHelper.Patch(ctx, req); patchErr != nil { - req.Status.Message = err.Error() - return patchErr - } + return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + rr.Status.Message = err.Error() + }) } - return r.patchResticRepository(ctx, req, patchHelper, log, func(rr *velerov1api.ResticRepository) { + + return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { rr.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } @@ -218,7 +210,7 @@ func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool { return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now) } -func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil @@ -229,9 +221,9 @@ func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *veler // we need to ensure it (first check, if check fails, attempt to init) // because we don't know if it's been successfully initialized yet. if err := ensureRepo(req, r.repositoryManager); err != nil { - return r.patchResticRepository(ctx, req, patchHelper, log, repoNotReady(err.Error())) + return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, patchHelper, log, repoReady()) + return r.patchResticRepository(ctx, req, repoReady()) } func repoNotReady(msg string) func(*velerov1api.ResticRepository) { @@ -251,11 +243,11 @@ func repoReady() func(*velerov1api.ResticRepository) { // patchResticRepository mutates req with the provided mutate function, and patches it // through the Kube API. After executing this function, req will be updated with both // the mutation and the results of the Patch() API call. -func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, patchHelper *patch.Helper, log logrus.FieldLogger, mutate func(*velerov1api.ResticRepository)) error { +func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error { + original := req.DeepCopy() mutate(req) - if err := patchHelper.Patch(ctx, req); err != nil { - log.WithError(err).Errorf("error updating restic repository resource %s in namespace %s with err %s", req.Name, req.Namespace, err.Error()) - return err + if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil { + return errors.Wrap(err, "error patching ResticRepository") } return nil } diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index 92dcaae9e5..2e6b4308a3 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -21,7 +21,6 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -63,12 +62,10 @@ func TestPatchResticRepository(t *testing.T) { reconciler := mockResticRepoReconciler(t, rr, "", nil, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) - patchHelper, err := patch.NewHelper(rr, reconciler.Client) - assert.NoError(t, err) - err = reconciler.patchResticRepository(context.Background(), rr, patchHelper, reconciler.logger, repoReady()) + err = reconciler.patchResticRepository(context.Background(), rr, repoReady()) assert.NoError(t, err) assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) - err = reconciler.patchResticRepository(context.Background(), rr, patchHelper, reconciler.logger, repoNotReady("not ready")) + err = reconciler.patchResticRepository(context.Background(), rr, repoNotReady("not ready")) assert.NoError(t, err) assert.NotEqual(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) } @@ -78,13 +75,11 @@ func TestCheckNotReadyRepo(t *testing.T) { reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) - patchHelper, err := patch.NewHelper(rr, reconciler.Client) - assert.NoError(t, err) - err = reconciler.checkNotReadyRepo(context.TODO(), rr, patchHelper, reconciler.logger) + err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhase("")) rr.Spec.ResticIdentifier = "s3:test.amazonaws.com/bucket/restic" - err = reconciler.checkNotReadyRepo(context.TODO(), rr, patchHelper, reconciler.logger) + err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) } @@ -94,16 +89,14 @@ func TestRunMaintenanceIfDue(t *testing.T) { reconciler := mockResticRepoReconciler(t, rr, "PruneRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) - patchHelper, err := patch.NewHelper(rr, reconciler.Client) - assert.NoError(t, err) lastTm := rr.Status.LastMaintenanceTime - err = reconciler.runMaintenanceIfDue(context.TODO(), rr, patchHelper, reconciler.logger) + err = reconciler.runMaintenanceIfDue(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) assert.NotEqual(t, rr.Status.LastMaintenanceTime, lastTm) rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} lastTm = rr.Status.LastMaintenanceTime - err = reconciler.runMaintenanceIfDue(context.TODO(), rr, patchHelper, reconciler.logger) + err = reconciler.runMaintenanceIfDue(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) assert.Equal(t, rr.Status.LastMaintenanceTime, lastTm) } @@ -114,8 +107,6 @@ func TestInitializeRepo(t *testing.T) { reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) - patchHelper, err := patch.NewHelper(rr, reconciler.Client) - assert.NoError(t, err) locations := &velerov1api.BackupStorageLocation{ Spec: velerov1api.BackupStorageLocationSpec{ Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, @@ -128,7 +119,7 @@ func TestInitializeRepo(t *testing.T) { err = reconciler.Client.Create(context.TODO(), locations) assert.NoError(t, err) - err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger, patchHelper) + err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) } diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index 92207b07a6..e62969b27d 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -27,7 +27,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -99,10 +98,7 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c c.metrics.InitSchedule(schedule.Name) - patchHelper, err := patch.NewHelper(schedule, c.Client) - if err != nil { - return ctrl.Result{}, errors.Wrapf(err, "error new patch helper for schedule %s", req.String()) - } + original := schedule.DeepCopy() // validation - even if the item is Enabled, we can't trust it // so re-validate @@ -118,7 +114,7 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // update status if it's changed if currentPhase != schedule.Status.Phase { - if err = patchHelper.Patch(ctx, schedule); err != nil { + if err := c.Patch(ctx, schedule, client.MergeFrom(original)); err != nil { return ctrl.Result{}, errors.Wrapf(err, "error updating phase of schedule %s to %s", req.String(), schedule.Status.Phase) } } @@ -200,13 +196,10 @@ func (c *scheduleReconciler) submitBackupIfDue(ctx context.Context, item *velero return errors.Wrap(err, "error creating Backup") } - patchHelper, err := patch.NewHelper(item, c.Client) - if err != nil { - return errors.Wrap(err, "error creating patch helper") - } + original := item.DeepCopy() item.Status.LastBackup = &metav1.Time{Time: now} - if err := patchHelper.Patch(ctx, item); err != nil { + if err := c.Patch(ctx, item, client.MergeFrom(original)); err != nil { return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", item.Status.LastBackup) } diff --git a/pkg/controller/server_status_request_controller.go b/pkg/controller/server_status_request_controller.go index f1872b3043..73b693968e 100644 --- a/pkg/controller/server_status_request_controller.go +++ b/pkg/controller/server_status_request_controller.go @@ -26,7 +26,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" - "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -88,20 +87,13 @@ func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl. switch statusRequest.Status.Phase { case "", velerov1api.ServerStatusRequestPhaseNew: log.Info("Processing new ServerStatusRequest") - - // Initialize the patch helper. - patchHelper, err := patch.NewHelper(statusRequest, r.Client) - if err != nil { - log.WithError(err).Error("Error getting a patch helper to update this resource") - return ctrl.Result{}, err - } - + original := statusRequest.DeepCopy() statusRequest.Status.ServerVersion = buildinfo.Version statusRequest.Status.Phase = velerov1api.ServerStatusRequestPhaseProcessed statusRequest.Status.ProcessedTimestamp = &metav1.Time{Time: r.Clock.Now()} statusRequest.Status.Plugins = velero.GetInstalledPluginInfo(r.PluginRegistry) - if err := patchHelper.Patch(r.Ctx, statusRequest); err != nil { + if err := r.Client.Patch(r.Ctx, statusRequest, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating ServerStatusRequest status") return ctrl.Result{RequeueAfter: statusRequestResyncPeriod}, err } diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 48ea88e159..24b2ef6c70 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -33,7 +33,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -240,12 +239,3 @@ func IsCRDReady(crd *unstructured.Unstructured) (bool, error) { return false, fmt.Errorf("unable to handle CRD with version %s", ver) } } - -// Patch the given object -func Patch(ctx context.Context, original, updated client.Object, client client.Client) error { - helper, err := patch.NewHelper(original, client) - if err != nil { - return err - } - return helper.Patch(ctx, updated) -} diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index afece7f83e..4a6db60695 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -34,7 +34,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/vmware-tanzu/velero/pkg/builder" @@ -426,25 +425,3 @@ func TestIsCRDReady(t *testing.T) { _, err = IsCRDReady(obj) assert.NotNil(t, err) } - -func TestPatch(t *testing.T) { - original := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "pod", - }, - } - cli := fake.NewClientBuilder().WithObjects(original).Build() - - updated := original.DeepCopy() - updated.SetLabels(map[string]string{"key": "value"}) - - ctx := context.Background() - err := Patch(ctx, original, updated, cli) - require.Nil(t, err) - - pod := &corev1.Pod{} - err = cli.Get(ctx, types.NamespacedName{Namespace: "default", Name: "pod"}, pod) - require.Nil(t, err) - assert.Equal(t, 1, len(pod.GetLabels())) -} From c2ddc6ead4799f1e7a7cedee35dde25dd73d7494 Mon Sep 17 00:00:00 2001 From: Ming Date: Thu, 9 Jun 2022 03:52:34 +0000 Subject: [PATCH 185/366] Fix ordered resources cmd Stdout already set error Signed-off-by: Ming --- test/e2e/util/velero/velero_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 30126d219c..92171c7655 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -234,8 +234,8 @@ func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st } func checkSchedulePhase(ctx context.Context, veleroCLI, veleroNamespace, scheduleName string) error { - checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "schedule", "get", scheduleName, "-ojson") return wait.PollImmediate(time.Second*5, time.Minute*2, func() (bool, error) { + checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "schedule", "get", scheduleName, "-ojson") jsonBuf, err := CMDExecWithOutput(checkCMD) if err != nil { return false, err From f5649bcc1fad5c2b58d6b25b175abdc560111728 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 9 Jun 2022 16:26:05 +0800 Subject: [PATCH 186/366] Undo setting the VSC's deletion policy during backup It's not necessary to set the deletion policy as the delete item action plugin in CSI plugin will set it to Delete when the backup is deleted. Signed-off-by: Daniel Jiang --- pkg/controller/backup_controller.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 4aea251563..cea4d80c68 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -1045,8 +1045,6 @@ func (c *backupController) recreateVolumeSnapshotContent(vsc *snapshotv1api.Volu Namespace: "ns-" + string(vsc.UID), Name: "name-" + string(vsc.UID), } - // Revert DeletionPolicy to Delete - vsc.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentDelete // ResourceVersion shouldn't exist for new creation. vsc.ResourceVersion = "" _, err = c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Create(context.TODO(), vsc, metav1.CreateOptions{}) From d16bdf1f8003efe969b30e71ab3d17b257d9e8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 10 Jun 2022 16:43:54 +0800 Subject: [PATCH 187/366] Bugs fixing: use patch to update status and set default GC period MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Use patch rather status patch in backup sync controller as we have disable status as sub resource 2. Set the GC period with default value if it isn't set Signed-off-by: Wenkai Yin(尹文开) --- pkg/controller/backup_sync_controller.go | 2 +- pkg/controller/gc_controller.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 2a96b139d5..c19badd6e3 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -337,7 +337,7 @@ func (c *backupSyncController) run() { // update the location's last-synced time field statusPatch := client.MergeFrom(location.DeepCopy()) location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} - if err := c.kbClient.Status().Patch(context.Background(), &location, statusPatch); err != nil { + if err := c.kbClient.Patch(context.Background(), &location, statusPatch); err != nil { log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time") continue } diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 05c28ad30f..b8297aa2a9 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -79,7 +79,7 @@ func NewGCController( c.syncHandler = c.processQueueItem c.resyncPeriod = frequency - if c.resyncPeriod < 0 { + if c.resyncPeriod <= 0 { c.resyncPeriod = defaultGCFrequency } logger.Infof("Garbage collection frequency: %s", c.resyncPeriod.String()) From dd984031f4fbe227c8b25153112abbae710faef5 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 13 Jun 2022 01:13:16 +0800 Subject: [PATCH 188/366] Update release instruction Update the release steps to reflect the change in the `tag-release.sh`, that the release branch must be created manually before RC is tagged. Signed-off-by: Daniel Jiang --- site/content/docs/main/release-instructions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/site/content/docs/main/release-instructions.md b/site/content/docs/main/release-instructions.md index fa20006948..608b441687 100644 --- a/site/content/docs/main/release-instructions.md +++ b/site/content/docs/main/release-instructions.md @@ -98,15 +98,16 @@ https://github.com/vmware-tanzu/velero/blob/release-1.7/Dockerfile#L53-L54 - If the dry-run fails with random errors, try running it again. #### Steps -1. Create a tagged release in dry-run mode +1. Manually create the release branch on Github, in the form like `release-$major.$minor` +1. Create a tagged release in dry-run mode - This won't push anything to GitHub. - - Run `VELERO_VERSION=v1.0.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ./hack/release-tools/tag-release.sh`. + - Run `VELERO_VERSION=v1.9.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ON_RELEASE_BRANCH=TRUE ./hack/release-tools/tag-release.sh`. - Fix any issue. 1. Create a tagged release and push it to GitHub - - Run `VELERO_VERSION=v1.0.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ./hack/release-tools/tag-release.sh publish`. + - Run `VELERO_VERSION=v1.9.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ON_RELEASE_BRANCH=TRUE ./hack/release-tools/tag-release.sh publish`. 1. Publish the release - Navigate to the draft GitHub release at https://github.com/vmware-tanzu/velero/releases and edit the release. - - If this is a patch release (e.g. `v1.4.1`), note that the full `CHANGELOG-1.4.md` contents will be included in the body of the GitHub release. You need to delete the previous releases' content (e.g. `v1.2.0`'s changelog) so that only the latest patch release's changelog shows. + - If this is a patch release (e.g. `v1.9.1`), note that the full `CHANGELOG-1.9.md` contents will be included in the body of the GitHub release. You need to delete the previous releases' content (e.g. `v1.9.0`'s changelog) so that only the latest patch release's changelog shows. - Do a quick review for formatting. - **Note:** the `goreleaser` process should have detected if it's a pre-release version and, if so, checked the box at the bottom of the GitHub release page appropriately, but it's always worth double-checking. - Verify that GitHub has built and pushed all the images (it takes a while): https://github.com/vmware-tanzu/velero/actions From 806234e043251a3007d03748299a745b2af02521 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 13 Jun 2022 11:29:57 +0800 Subject: [PATCH 189/366] Add more information for failing to get path or snapshot in restic backup and restore. Signed-off-by: Xun Jiang --- changelogs/unreleased/4988-jxun | 1 + pkg/controller/pod_volume_backup_controller.go | 2 +- pkg/controller/pod_volume_restore_controller.go | 2 +- pkg/restic/exec_commands.go | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/4988-jxun diff --git a/changelogs/unreleased/4988-jxun b/changelogs/unreleased/4988-jxun new file mode 100644 index 0000000000..204c8efb35 --- /dev/null +++ b/changelogs/unreleased/4988-jxun @@ -0,0 +1 @@ +Add more information for failing to get path or snapshot in restic backup and restore. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index b1121d5e57..322592f808 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -211,7 +211,7 @@ func (r *PodVolumeBackupReconciler) singlePathMatch(path string) (string, error) } if len(matches) != 1 { - return "", errors.Errorf("expected one matching path, got %d", len(matches)) + return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) } return matches[0], nil diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 2b9cfd4073..3315dae3ba 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -222,7 +222,7 @@ func singlePathMatch(path string) (string, error) { } if len(matches) != 1 { - return "", errors.Errorf("expected one matching path, got %d", len(matches)) + return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) } return matches[0], nil diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index a0d16d8d58..7dd0057c0f 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -63,7 +63,7 @@ func GetSnapshotID(snapshotIdCmd *Command) (string, error) { } if len(snapshots) != 1 { - return "", errors.Errorf("expected one matching snapshot, got %d", len(snapshots)) + return "", errors.Errorf("expected one matching snapshot by command: %s, got %d", snapshotIdCmd.String(), len(snapshots)) } return snapshots[0].ShortID, nil From 463202951d221b871cf53efc324fe12e6bb07a47 Mon Sep 17 00:00:00 2001 From: danfengl Date: Sat, 11 Jun 2022 08:40:19 +0000 Subject: [PATCH 190/366] Enhance checkpoint of bsl deletion Signed-off-by: danfengl --- test/e2e/backups/sync_backups.go | 2 +- test/e2e/bsl-mgmt/deletion.go | 5 ++-- test/e2e/util/velero/velero_utils.go | 34 ++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index cc3fefb23f..37162bac14 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -161,5 +161,5 @@ func BackupsSyncTest() { } func (b *SyncBackups) IsBackupsSynced() error { - return WaitForBackupCreated(b.ctx, VeleroCfg.VeleroCLI, b.backupName, 10*time.Minute) + return WaitForBackupToBeCreated(b.ctx, VeleroCfg.VeleroCLI, b.backupName, 10*time.Minute) } diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index fd75ba1801..b605313461 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -252,12 +252,12 @@ func BslDeletionTest(useVolumeSnapshots bool) { } By(fmt.Sprintf("Backup 1 %s should be created.", backupName_1), func() { - Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, + Expect(WaitForBackupToBeCreated(context.Background(), VeleroCfg.VeleroCLI, backupName_1, 10*time.Minute)).To(Succeed()) }) By(fmt.Sprintf("Backup 2 %s should be created.", backupName_2), func() { - Expect(WaitForBackupCreated(context.Background(), VeleroCfg.VeleroCLI, + Expect(WaitForBackupToBeCreated(context.Background(), VeleroCfg.VeleroCLI, backupName_2, 10*time.Minute)).To(Succeed()) }) @@ -280,6 +280,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { By(fmt.Sprintf("Delete one of backup locations - %s", backupLocation_1), func() { Expect(DeleteBslResource(context.Background(), VeleroCfg.VeleroCLI, backupLocation_1)).To(Succeed()) + Expect(WaitForBackupsToBeDeleted(context.Background(), VeleroCfg.VeleroCLI, backupsInBSL1, 10*time.Minute)).To(Succeed()) }) By("Get all backups from 2 BSLs after deleting one of them", func() { diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 92171c7655..aa4d8eec43 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -705,6 +705,7 @@ func getVeleroCliTarball(cliTarballUrl string) (*os.File, error) { return tmpfile, nil } + func DeleteBackupResource(ctx context.Context, veleroCLI string, backupName string) error { args := []string{"backup", "delete", backupName, "--confirm"} @@ -771,19 +772,48 @@ func WaitBackupDeleted(ctx context.Context, veleroCLI string, backupName string, }) } -func WaitForBackupCreated(ctx context.Context, veleroCLI string, backupName string, timeout time.Duration) error { +func WaitForExpectedStateOfBackup(ctx context.Context, veleroCLI string, backupName string, + timeout time.Duration, existing bool) error { return wait.PollImmediate(10*time.Second, timeout, func() (bool, error) { if exist, err := IsBackupExist(ctx, veleroCLI, backupName); err != nil { return false, err } else { - if exist { + msg := "does not exist" + if existing { + msg = "was found" + } + if exist == existing { + fmt.Println("Backup <" + backupName + "> " + msg) return true, nil } else { + fmt.Println("Backup <" + backupName + "> " + msg) return false, nil } } }) } + +func WaitForBackupToBeCreated(ctx context.Context, veleroCLI string, backupName string, timeout time.Duration) error { + return WaitForExpectedStateOfBackup(ctx, veleroCLI, backupName, timeout, true) +} + +func WaitForBackupToBeDeleted(ctx context.Context, veleroCLI string, backupName string, timeout time.Duration) error { + return WaitForExpectedStateOfBackup(ctx, veleroCLI, backupName, timeout, false) +} + +func WaitForBackupsToBeDeleted(ctx context.Context, veleroCLI string, backups []string, timeout time.Duration) error { + var err error + for _, backupName := range backups { + fmt.Println("Waiting for deletion of backup <" + backupName + ">") + err = WaitForExpectedStateOfBackup(ctx, veleroCLI, backupName, timeout, false) + if err != nil { + return err + } + } + fmt.Println("All backups were deleted.") + return nil +} + func GetBackupsFromBsl(ctx context.Context, veleroCLI, bslName string) ([]string, error) { args1 := []string{"get", "backups"} if strings.TrimSpace(bslName) != "" { From 423e7f9f63bb437a66fc0ad7fef1cb68c8b337f1 Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 13 Jun 2022 09:23:42 +0000 Subject: [PATCH 191/366] Add Changelog and Docs for v1.9 Signed-off-by: Ming --- CHANGELOG.md | 5 +- changelogs/CHANGELOG-1.9.md | 104 ++++ changelogs/unreleased/4084-MatthieuFin | 1 - changelogs/unreleased/4296-tobiasgiese | 1 - changelogs/unreleased/4436-fgold | 1 - changelogs/unreleased/4497-dkeven | 1 - changelogs/unreleased/4517-reasonerjt | 1 - changelogs/unreleased/4518-ywk253100 | 1 - changelogs/unreleased/4545-mqiu | 1 - changelogs/unreleased/4550-dbrekau | 1 - changelogs/unreleased/4584-bynare | 1 - changelogs/unreleased/4591-mqiu | 1 - changelogs/unreleased/4623-jxun | 1 - .../unreleased/4628-shubham-pampattiwar | 1 - changelogs/unreleased/4634-qiuming-best | 1 - changelogs/unreleased/4639-jxun | 1 - .../unreleased/4650-shubham-pampattiwar | 1 - changelogs/unreleased/4655-ywk253100 | 1 - changelogs/unreleased/4660-jxun | 1 - changelogs/unreleased/4661-jxun | 1 - changelogs/unreleased/4680-jxun | 1 - changelogs/unreleased/4686-reasonerjt | 1 - changelogs/unreleased/4692-jxun | 1 - changelogs/unreleased/4694-j4m3s-s | 1 - changelogs/unreleased/4704-reasonerjt | 1 - changelogs/unreleased/4719-ywk253100 | 1 - changelogs/unreleased/4725-jxun | 1 - changelogs/unreleased/4740-phuongatemc | 2 - changelogs/unreleased/4743-sseago | 1 - changelogs/unreleased/4748-ywk253100 | 1 - changelogs/unreleased/4757-kaovilai | 1 - changelogs/unreleased/4769-half-life666 | 1 - changelogs/unreleased/4778-jxun | 1 - changelogs/unreleased/4779-reasonerjt | 1 - changelogs/unreleased/4785-RafaeLeal | 1 - changelogs/unreleased/4797-jxun | 1 - changelogs/unreleased/4800-reasonerjt | 1 - changelogs/unreleased/4817-yuvalman | 1 - changelogs/unreleased/4818-jxun | 1 - changelogs/unreleased/4831-qiuming-best | 1 - changelogs/unreleased/4832-reasonerjt | 1 - changelogs/unreleased/4833-ywk253100 | 1 - changelogs/unreleased/4838-ywk253100 | 1 - changelogs/unreleased/4839-jxun | 1 - changelogs/unreleased/4854-jxun | 1 - changelogs/unreleased/4855-reasonerjt | 1 - changelogs/unreleased/4858-jxun | 1 - changelogs/unreleased/4859-qiuming-best | 1 - changelogs/unreleased/4866-reasonerjt | 1 - changelogs/unreleased/4872-big-appled | 1 - changelogs/unreleased/4887-reasonerjt | 1 - changelogs/unreleased/4889-jxun | 1 - changelogs/unreleased/4890-sseago | 1 - changelogs/unreleased/4893-ywk253100 | 1 - changelogs/unreleased/4894-jxun | 1 - changelogs/unreleased/4897-ywk253100 | 1 - changelogs/unreleased/4898-ywk253100 | 1 - changelogs/unreleased/4913-qiuming-best | 1 - changelogs/unreleased/4914-jxun | 1 - changelogs/unreleased/4920-ywk253100 | 1 - changelogs/unreleased/4968-jxun | 1 - changelogs/unreleased/4972-ywk253100 | 1 - changelogs/unreleased/4988-jxun | 1 - site/config.yaml | 3 +- .../{upgrade-to-1.8.md => upgrade-to-1.9.md} | 22 +- site/content/docs/v1.9/_index.md | 58 ++ site/content/docs/v1.9/api-types/README.md | 21 + site/content/docs/v1.9/api-types/_index.md | 19 + site/content/docs/v1.9/api-types/backup.md | 157 +++++ .../v1.9/api-types/backupstoragelocation.md | 54 ++ site/content/docs/v1.9/api-types/restore.md | 193 ++++++ site/content/docs/v1.9/api-types/schedule.md | 142 +++++ .../v1.9/api-types/volumesnapshotlocation.md | 40 ++ site/content/docs/v1.9/backup-hooks.md | 111 ++++ site/content/docs/v1.9/backup-reference.md | 91 +++ site/content/docs/v1.9/basic-install.md | 73 +++ site/content/docs/v1.9/build-from-source.md | 200 +++++++ site/content/docs/v1.9/code-standards.md | 151 +++++ .../docs/v1.9/contributions/ibm-config.md | 101 ++++ .../15ccaacf00640a04ae29ceed4c86195b.png | Bin 0 -> 132711 bytes .../1d53b0115644d43657c2a5ece805c9b4.png | Bin 0 -> 74478 bytes .../69194157ccd5e377d1e7d914fd8c0336.png | Bin 0 -> 32817 bytes .../9015313121ed7987558c88081b052574.png | Bin 0 -> 417063 bytes .../ceaca9ce6bc92bdce987c63d2fe71561.png | Bin 0 -> 292269 bytes .../e8c2ab4e5e31d1370c62fad25059a8a8.png | Bin 0 -> 65320 bytes .../e932223585c0b19891cc085ad7f438e1.png | Bin 0 -> 284090 bytes .../eb2bbabae48b188748f5278bedf177f1.png | Bin 0 -> 126032 bytes .../effe8a0a7ce3aa8e422db00bfdddc375.png | Bin 0 -> 85131 bytes .../f0fff5228527edc72d6e71a50d5dc966.png | Bin 0 -> 49558 bytes site/content/docs/v1.9/contributions/minio.md | 296 ++++++++++ .../docs/v1.9/contributions/oracle-config.md | 248 ++++++++ .../docs/v1.9/contributions/tencent-config.md | 168 ++++++ site/content/docs/v1.9/csi.md | 75 +++ site/content/docs/v1.9/custom-plugins.md | 115 ++++ .../docs/v1.9/customize-installation.md | 390 +++++++++++++ site/content/docs/v1.9/debugging-install.md | 74 +++ site/content/docs/v1.9/debugging-restores.md | 105 ++++ site/content/docs/v1.9/development.md | 56 ++ site/content/docs/v1.9/disaster-case.md | 44 ++ .../v1.9/enable-api-group-versions-feature.md | 115 ++++ site/content/docs/v1.9/examples.md | 70 +++ site/content/docs/v1.9/how-velero-works.md | 114 ++++ site/content/docs/v1.9/image-tagging.md | 24 + site/content/docs/v1.9/img/README.md | 1 + site/content/docs/v1.9/img/backup-process.png | Bin 0 -> 33630 bytes .../docs/v1.9/img/gv_priority1-caseA.png | Bin 0 -> 57254 bytes .../docs/v1.9/img/gv_priority1-caseB.png | Bin 0 -> 87058 bytes .../docs/v1.9/img/gv_priority2-caseC.png | Bin 0 -> 42106 bytes .../docs/v1.9/img/gv_priority3-caseD.png | Bin 0 -> 69760 bytes site/content/docs/v1.9/img/velero.png | Bin 0 -> 45564 bytes site/content/docs/v1.9/locations.md | 262 +++++++++ site/content/docs/v1.9/maintainers.md | 37 ++ site/content/docs/v1.9/manual-testing.md | 92 +++ site/content/docs/v1.9/migration-case.md | 104 ++++ site/content/docs/v1.9/namespace.md | 22 + site/content/docs/v1.9/on-premises.md | 95 +++ site/content/docs/v1.9/output-file-format.md | 224 +++++++ site/content/docs/v1.9/overview-plugins.md | 29 + .../docs/v1.9/plugin-release-instructions.md | 30 + site/content/docs/v1.9/rbac.md | 50 ++ .../content/docs/v1.9/release-instructions.md | 177 ++++++ site/content/docs/v1.9/release-schedule.md | 15 + site/content/docs/v1.9/resource-filtering.md | 143 +++++ site/content/docs/v1.9/restic.md | 549 ++++++++++++++++++ site/content/docs/v1.9/restore-hooks.md | 261 +++++++++ site/content/docs/v1.9/restore-reference.md | 272 +++++++++ site/content/docs/v1.9/run-locally.md | 53 ++ .../docs/v1.9/self-signed-certificates.md | 65 +++ site/content/docs/v1.9/start-contributing.md | 34 ++ site/content/docs/v1.9/style-guide.md | 344 +++++++++++ site/content/docs/v1.9/support-process.md | 50 ++ site/content/docs/v1.9/supported-providers.md | 70 +++ site/content/docs/v1.9/tilt.md | 210 +++++++ site/content/docs/v1.9/troubleshooting.md | 220 +++++++ site/content/docs/v1.9/uninstalling.md | 11 + site/content/docs/v1.9/upgrade-to-1.9.md | 94 +++ site/content/docs/v1.9/velero-install.md | 49 ++ .../docs/v1.9/vendoring-dependencies.md | 21 + site/content/docs/v1.9/website-guidelines.md | 45 ++ site/data/docs/main-toc.yml | 4 +- site/data/docs/toc-mapping.yml | 1 + site/data/docs/v1-9-toc.yml | 97 ++++ 142 files changed, 7156 insertions(+), 76 deletions(-) create mode 100644 changelogs/CHANGELOG-1.9.md delete mode 100644 changelogs/unreleased/4084-MatthieuFin delete mode 100644 changelogs/unreleased/4296-tobiasgiese delete mode 100644 changelogs/unreleased/4436-fgold delete mode 100644 changelogs/unreleased/4497-dkeven delete mode 100644 changelogs/unreleased/4517-reasonerjt delete mode 100644 changelogs/unreleased/4518-ywk253100 delete mode 100644 changelogs/unreleased/4545-mqiu delete mode 100644 changelogs/unreleased/4550-dbrekau delete mode 100644 changelogs/unreleased/4584-bynare delete mode 100644 changelogs/unreleased/4591-mqiu delete mode 100644 changelogs/unreleased/4623-jxun delete mode 100644 changelogs/unreleased/4628-shubham-pampattiwar delete mode 100644 changelogs/unreleased/4634-qiuming-best delete mode 100644 changelogs/unreleased/4639-jxun delete mode 100644 changelogs/unreleased/4650-shubham-pampattiwar delete mode 100644 changelogs/unreleased/4655-ywk253100 delete mode 100644 changelogs/unreleased/4660-jxun delete mode 100644 changelogs/unreleased/4661-jxun delete mode 100644 changelogs/unreleased/4680-jxun delete mode 100644 changelogs/unreleased/4686-reasonerjt delete mode 100644 changelogs/unreleased/4692-jxun delete mode 100644 changelogs/unreleased/4694-j4m3s-s delete mode 100644 changelogs/unreleased/4704-reasonerjt delete mode 100644 changelogs/unreleased/4719-ywk253100 delete mode 100644 changelogs/unreleased/4725-jxun delete mode 100644 changelogs/unreleased/4740-phuongatemc delete mode 100644 changelogs/unreleased/4743-sseago delete mode 100644 changelogs/unreleased/4748-ywk253100 delete mode 100644 changelogs/unreleased/4757-kaovilai delete mode 100644 changelogs/unreleased/4769-half-life666 delete mode 100644 changelogs/unreleased/4778-jxun delete mode 100644 changelogs/unreleased/4779-reasonerjt delete mode 100644 changelogs/unreleased/4785-RafaeLeal delete mode 100644 changelogs/unreleased/4797-jxun delete mode 100644 changelogs/unreleased/4800-reasonerjt delete mode 100644 changelogs/unreleased/4817-yuvalman delete mode 100644 changelogs/unreleased/4818-jxun delete mode 100644 changelogs/unreleased/4831-qiuming-best delete mode 100644 changelogs/unreleased/4832-reasonerjt delete mode 100644 changelogs/unreleased/4833-ywk253100 delete mode 100644 changelogs/unreleased/4838-ywk253100 delete mode 100644 changelogs/unreleased/4839-jxun delete mode 100644 changelogs/unreleased/4854-jxun delete mode 100644 changelogs/unreleased/4855-reasonerjt delete mode 100644 changelogs/unreleased/4858-jxun delete mode 100644 changelogs/unreleased/4859-qiuming-best delete mode 100644 changelogs/unreleased/4866-reasonerjt delete mode 100644 changelogs/unreleased/4872-big-appled delete mode 100644 changelogs/unreleased/4887-reasonerjt delete mode 100644 changelogs/unreleased/4889-jxun delete mode 100644 changelogs/unreleased/4890-sseago delete mode 100644 changelogs/unreleased/4893-ywk253100 delete mode 100644 changelogs/unreleased/4894-jxun delete mode 100644 changelogs/unreleased/4897-ywk253100 delete mode 100644 changelogs/unreleased/4898-ywk253100 delete mode 100644 changelogs/unreleased/4913-qiuming-best delete mode 100644 changelogs/unreleased/4914-jxun delete mode 100644 changelogs/unreleased/4920-ywk253100 delete mode 100644 changelogs/unreleased/4968-jxun delete mode 100644 changelogs/unreleased/4972-ywk253100 delete mode 100644 changelogs/unreleased/4988-jxun rename site/content/docs/main/{upgrade-to-1.8.md => upgrade-to-1.9.md} (83%) create mode 100644 site/content/docs/v1.9/_index.md create mode 100644 site/content/docs/v1.9/api-types/README.md create mode 100644 site/content/docs/v1.9/api-types/_index.md create mode 100644 site/content/docs/v1.9/api-types/backup.md create mode 100644 site/content/docs/v1.9/api-types/backupstoragelocation.md create mode 100644 site/content/docs/v1.9/api-types/restore.md create mode 100644 site/content/docs/v1.9/api-types/schedule.md create mode 100644 site/content/docs/v1.9/api-types/volumesnapshotlocation.md create mode 100644 site/content/docs/v1.9/backup-hooks.md create mode 100644 site/content/docs/v1.9/backup-reference.md create mode 100644 site/content/docs/v1.9/basic-install.md create mode 100644 site/content/docs/v1.9/build-from-source.md create mode 100644 site/content/docs/v1.9/code-standards.md create mode 100644 site/content/docs/v1.9/contributions/ibm-config.md create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/15ccaacf00640a04ae29ceed4c86195b.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/1d53b0115644d43657c2a5ece805c9b4.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/69194157ccd5e377d1e7d914fd8c0336.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/9015313121ed7987558c88081b052574.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/ceaca9ce6bc92bdce987c63d2fe71561.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/e8c2ab4e5e31d1370c62fad25059a8a8.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/e932223585c0b19891cc085ad7f438e1.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/eb2bbabae48b188748f5278bedf177f1.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/effe8a0a7ce3aa8e422db00bfdddc375.png create mode 100644 site/content/docs/v1.9/contributions/img-for-tencent/f0fff5228527edc72d6e71a50d5dc966.png create mode 100644 site/content/docs/v1.9/contributions/minio.md create mode 100644 site/content/docs/v1.9/contributions/oracle-config.md create mode 100644 site/content/docs/v1.9/contributions/tencent-config.md create mode 100644 site/content/docs/v1.9/csi.md create mode 100644 site/content/docs/v1.9/custom-plugins.md create mode 100644 site/content/docs/v1.9/customize-installation.md create mode 100644 site/content/docs/v1.9/debugging-install.md create mode 100644 site/content/docs/v1.9/debugging-restores.md create mode 100644 site/content/docs/v1.9/development.md create mode 100644 site/content/docs/v1.9/disaster-case.md create mode 100644 site/content/docs/v1.9/enable-api-group-versions-feature.md create mode 100644 site/content/docs/v1.9/examples.md create mode 100644 site/content/docs/v1.9/how-velero-works.md create mode 100644 site/content/docs/v1.9/image-tagging.md create mode 100644 site/content/docs/v1.9/img/README.md create mode 100644 site/content/docs/v1.9/img/backup-process.png create mode 100644 site/content/docs/v1.9/img/gv_priority1-caseA.png create mode 100644 site/content/docs/v1.9/img/gv_priority1-caseB.png create mode 100644 site/content/docs/v1.9/img/gv_priority2-caseC.png create mode 100644 site/content/docs/v1.9/img/gv_priority3-caseD.png create mode 100644 site/content/docs/v1.9/img/velero.png create mode 100644 site/content/docs/v1.9/locations.md create mode 100644 site/content/docs/v1.9/maintainers.md create mode 100644 site/content/docs/v1.9/manual-testing.md create mode 100644 site/content/docs/v1.9/migration-case.md create mode 100644 site/content/docs/v1.9/namespace.md create mode 100644 site/content/docs/v1.9/on-premises.md create mode 100644 site/content/docs/v1.9/output-file-format.md create mode 100644 site/content/docs/v1.9/overview-plugins.md create mode 100644 site/content/docs/v1.9/plugin-release-instructions.md create mode 100644 site/content/docs/v1.9/rbac.md create mode 100644 site/content/docs/v1.9/release-instructions.md create mode 100644 site/content/docs/v1.9/release-schedule.md create mode 100644 site/content/docs/v1.9/resource-filtering.md create mode 100644 site/content/docs/v1.9/restic.md create mode 100644 site/content/docs/v1.9/restore-hooks.md create mode 100644 site/content/docs/v1.9/restore-reference.md create mode 100644 site/content/docs/v1.9/run-locally.md create mode 100644 site/content/docs/v1.9/self-signed-certificates.md create mode 100644 site/content/docs/v1.9/start-contributing.md create mode 100644 site/content/docs/v1.9/style-guide.md create mode 100644 site/content/docs/v1.9/support-process.md create mode 100644 site/content/docs/v1.9/supported-providers.md create mode 100644 site/content/docs/v1.9/tilt.md create mode 100644 site/content/docs/v1.9/troubleshooting.md create mode 100644 site/content/docs/v1.9/uninstalling.md create mode 100644 site/content/docs/v1.9/upgrade-to-1.9.md create mode 100644 site/content/docs/v1.9/velero-install.md create mode 100644 site/content/docs/v1.9/vendoring-dependencies.md create mode 100644 site/content/docs/v1.9/website-guidelines.md create mode 100644 site/data/docs/v1-9-toc.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dbfe3e1a7..302a7bfeb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## Current release: - * [CHANGELOG-1.7.md][17] + * [CHANGELOG-1.9.md][19] ## Older releases: + * [CHANGELOG-1.8.md][18] * [CHANGELOG-1.6.md][16] * [CHANGELOG-1.5.md][15] * [CHANGELOG-1.4.md][14] @@ -20,6 +21,8 @@ * [CHANGELOG-0.3.md][1] +[19]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.9.md +[18]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.8.md [17]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.7.md [16]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.6.md [15]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.5.md diff --git a/changelogs/CHANGELOG-1.9.md b/changelogs/CHANGELOG-1.9.md new file mode 100644 index 0000000000..9f887aaf54 --- /dev/null +++ b/changelogs/CHANGELOG-1.9.md @@ -0,0 +1,104 @@ +## v1.9.0 +### 2022-06-13 + +### Download +https://github.com/vmware-tanzu/velero/releases/tag/v1.9.0 + +### Container Image +`velero/velero:v1.9.0` + +### Documentation +https://velero.io/docs/v1.9/ + +### Upgrading +https://velero.io/docs/v1.9/upgrade-to-1.9/ + +### Highlights + +#### Improvement to the CSI plugin +- Bump up to the CSI volume snapshot v1 API +- No VolumeSnapshot will be left in the source namespace of the workload +- Report metrics for CSI snapshots + +More improvements please refer to [CSI plugin improvement](https://github.com/vmware-tanzu/velero/issues?q=is%3Aissue+label%3A%22CSI+plugin+-+GA+-+phase1%22+is%3Aclosed) + +With these improvements we'll provide official support for CSI snapshots on AKS/EKS clusters. (with CSI plugin v0.3.0) + +#### Refactor the controllers using Kubebuilder v3 +In this release we continued our code modernization work, rewriting some controllers using Kubebuilder v3. This work is ongoing and we will continue to make progress in future releases. + +#### Optionally restore status on selected resources +Options are added to the CLI and Restore spec to control the group of resources whose status will be restored. + +#### ExistingResourcePolicy in the restore API +Users can choose to overwrite or patch the existing resources during restore by setting this policy. + +#### Upgrade integrated Restic version and add skip TLS validation in Restic command +Upgrade integrated Restic version, which will resolve some of the CVEs, and support skip TLS validation in Restic backup/restore. + +#### Breaking changes +With bumping up the API to v1 in CSI plugin, the v0.3.0 CSI plugin will only work for Kubernetes v1.20+ + +### All changes + + * restic: add full support for setting SecurityContext for restore init container from configMap. (#4084, @MatthieuFin) + * Add metrics backup_items_total and backup_items_errors (#4296, @tobiasgiese) + * Convert PodVolumebackup controller to the Kubebuilder framework (#4436, @fgold) + * Skip not mounted volumes when backing up (#4497, @dkeven) + * Update doc for v1.8 (#4517, @reasonerjt) + * Fix bug to make the restic prune frequency configurable (#4518, @ywk253100) + * Add E2E test of backups sync from BSL (#4545, @mqiu) + * Fix: OrderedResources in Schedules (#4550, @dbrekau) + * Skip volumes of non-running pods when backing up (#4584, @bynare) + * E2E SSR test add retry mechanism and logs (#4591, @mqiu) + * Add pushing image to GCR in github workflow to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. (#4623, @jxun) + * Add existingResourcePolicy to Restore API (#4628, @shubham-pampattiwar) + * Fix E2E backup namespaces test (#4634, @qiuming-best) + * Update image used by E2E test to gcr.io (#4639, @jxun) + * Add multiple label selector support to Velero Backup and Restore APIs (#4650, @shubham-pampattiwar) + * Convert Pod Volume Restore resource/controller to the Kubebuilder framework (#4655, @ywk253100) + * Update --use-owner-references-in-backup description in velero command line. (#4660, @jxun) + * Avoid overwritten hook's exec.container parameter when running pod command executor. (#4661, @jxun) + * Support regional pv for GKE (#4680, @jxun) + * Bypass the remap CRD version plugin when v1beta1 CRD is not supported (#4686, @reasonerjt) + * Add GINKGO_SKIP to support skip specific case in e2e test. (#4692, @jxun) + * Add --pod-labels flag to velero install (#4694, @j4m3s-s) + * Enable coverage in test.sh and upload to codecov (#4704, @reasonerjt) + * Mark the BSL as "Unavailable" when gets any error and add a new field "Message" to the status to record the error message (#4719, @ywk253100) + * Support multiple skip option for E2E test (#4725, @jxun) + * Add PriorityClass to the AdditionalItems of Backup's PodAction and Restore's PodAction plugin to backup and restore PriorityClass if it is used by a Pod. (#4740, @phuongatemc) + * Insert all restore errors and warnings into restore log. (#4743, @sseago) + * Refactor schedule controller with kubebuilder (#4748, @ywk253100) + * Garbage collector now adds labels to backups that failed to delete for BSLNotFound, BSLCannotGet, BSLReadOnly reasons. (#4757, @kaovilai) + * Skip podvolumerestore creation when restore excludes pv/pvc (#4769, @half-life666) + * Add parameter for e2e test to support modify kibishii install path. (#4778, @jxun) + * Ensure the restore hook applied to new namespace based on the mapping (#4779, @reasonerjt) + * Add ability to restore status on selected resources (#4785, @RafaeLeal) + * Do not take snapshot for PV to avoid duplicated snapshotting, when CSI feature is enabled. (#4797, @jxun) + * Bump up to v1 API for CSI snapshot (#4800, @reasonerjt) + * fix: delete empty backups (#4817, @yuvalman) + * Add CSI VolumeSnapshot related metrics. (#4818, @jxun) + * Fix default-backup-ttl not work (#4831, @qiuming-best) + * Make the vsc created by backup sync controller deletable (#4832, @reasonerjt) + * Make in-progress backup/restore as failed when doing the reconcile to avoid hanging in in-progress status (#4833, @ywk253100) + * Use controller-gen to generate the deep copy methods for objects (#4838, @ywk253100) + * Update integrated Restic version and add insecureSkipTLSVerify for Restic CLI. (#4839, @jxun) + * Modify CSI VolumeSnapshot metric related code. (#4854, @jxun) + * Refactor backup deletion controller based on kubebuilder (#4855, @reasonerjt) + * Remove VolumeSnapshots created during backup when CSI feature is enabled. (#4858, @jxun) + * Convert Restic Repository resource/controller to the Kubebuilder framework (#4859, @qiuming-best) + * Add ClusterClasses to the restore priority list (#4866, @reasonerjt) + * Cleanup the .velero folder after restic done (#4872, @big-appled) + * Delete orphan CSI snapshots in backup sync controller (#4887, @reasonerjt) + * Make waiting VolumeSnapshot to ready process parallel. (#4889, @jxun) + * continue rather than return for non-matching restore action label (#4890, @sseago) + * Make in-progress PVB/PVR as failed when restic controller restarts to avoid hanging backup/restore (#4893, @ywk253100) + * Refactor BSL controller with periodical enqueue source (#4894, @jxun) + * Make garbage collection for expired backups configurable (#4897, @ywk253100) + * Bump up the version of distroless to base-debian11 (#4898, @ywk253100) + * Add schedule ordered resources E2E test (#4913, @qiuming-best) + * Make velero completion zsh command output can be used by `source` command. (#4914, @jxun) + * Enhance the map flag to support parsing input value contains entry delimiters (#4920, @ywk253100) + * Fix E2E test [Backups][Deletion][Restic] on GCP. (#4968, @jxun) + * Disable status as sub resource in CRDs (#4972, @ywk253100) + * Add more information for failing to get path or snapshot in restic backup and restore. (#4988, @jxun) diff --git a/changelogs/unreleased/4084-MatthieuFin b/changelogs/unreleased/4084-MatthieuFin deleted file mode 100644 index c4bc338330..0000000000 --- a/changelogs/unreleased/4084-MatthieuFin +++ /dev/null @@ -1 +0,0 @@ -restic: add full support for setting SecurityContext for restore init container from configMap. diff --git a/changelogs/unreleased/4296-tobiasgiese b/changelogs/unreleased/4296-tobiasgiese deleted file mode 100644 index 28b8ef7634..0000000000 --- a/changelogs/unreleased/4296-tobiasgiese +++ /dev/null @@ -1 +0,0 @@ -Add metrics backup_items_total and backup_items_errors diff --git a/changelogs/unreleased/4436-fgold b/changelogs/unreleased/4436-fgold deleted file mode 100644 index ba9ac4c071..0000000000 --- a/changelogs/unreleased/4436-fgold +++ /dev/null @@ -1 +0,0 @@ -Convert PodVolumebackup controller to the Kubebuilder framework \ No newline at end of file diff --git a/changelogs/unreleased/4497-dkeven b/changelogs/unreleased/4497-dkeven deleted file mode 100644 index 63d246e42d..0000000000 --- a/changelogs/unreleased/4497-dkeven +++ /dev/null @@ -1 +0,0 @@ -Skip not mounted volumes when backing up diff --git a/changelogs/unreleased/4517-reasonerjt b/changelogs/unreleased/4517-reasonerjt deleted file mode 100644 index 570af0b5ed..0000000000 --- a/changelogs/unreleased/4517-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Update doc for v1.8 \ No newline at end of file diff --git a/changelogs/unreleased/4518-ywk253100 b/changelogs/unreleased/4518-ywk253100 deleted file mode 100644 index e1b9181743..0000000000 --- a/changelogs/unreleased/4518-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Fix bug to make the restic prune frequency configurable \ No newline at end of file diff --git a/changelogs/unreleased/4545-mqiu b/changelogs/unreleased/4545-mqiu deleted file mode 100644 index bc9eca6474..0000000000 --- a/changelogs/unreleased/4545-mqiu +++ /dev/null @@ -1 +0,0 @@ -Add E2E test of backups sync from BSL diff --git a/changelogs/unreleased/4550-dbrekau b/changelogs/unreleased/4550-dbrekau deleted file mode 100644 index dea16b5671..0000000000 --- a/changelogs/unreleased/4550-dbrekau +++ /dev/null @@ -1 +0,0 @@ -Fix: OrderedResources in Schedules diff --git a/changelogs/unreleased/4584-bynare b/changelogs/unreleased/4584-bynare deleted file mode 100644 index 008c138c44..0000000000 --- a/changelogs/unreleased/4584-bynare +++ /dev/null @@ -1 +0,0 @@ -Skip volumes of non-running pods when backing up diff --git a/changelogs/unreleased/4591-mqiu b/changelogs/unreleased/4591-mqiu deleted file mode 100644 index fb68c93050..0000000000 --- a/changelogs/unreleased/4591-mqiu +++ /dev/null @@ -1 +0,0 @@ -E2E SSR test add retry mechanism and logs diff --git a/changelogs/unreleased/4623-jxun b/changelogs/unreleased/4623-jxun deleted file mode 100644 index 56e0fe4e3d..0000000000 --- a/changelogs/unreleased/4623-jxun +++ /dev/null @@ -1 +0,0 @@ -Add pushing image to GCR in github workflow to facilitate some environments that have rate limitation to docker hub, e.g. vSphere. diff --git a/changelogs/unreleased/4628-shubham-pampattiwar b/changelogs/unreleased/4628-shubham-pampattiwar deleted file mode 100644 index acb92c9086..0000000000 --- a/changelogs/unreleased/4628-shubham-pampattiwar +++ /dev/null @@ -1 +0,0 @@ -Add existingResourcePolicy to Restore API \ No newline at end of file diff --git a/changelogs/unreleased/4634-qiuming-best b/changelogs/unreleased/4634-qiuming-best deleted file mode 100644 index b3569ea03b..0000000000 --- a/changelogs/unreleased/4634-qiuming-best +++ /dev/null @@ -1 +0,0 @@ -Fix E2E backup namespaces test diff --git a/changelogs/unreleased/4639-jxun b/changelogs/unreleased/4639-jxun deleted file mode 100644 index a027b1b00b..0000000000 --- a/changelogs/unreleased/4639-jxun +++ /dev/null @@ -1 +0,0 @@ -Update image used by E2E test to gcr.io diff --git a/changelogs/unreleased/4650-shubham-pampattiwar b/changelogs/unreleased/4650-shubham-pampattiwar deleted file mode 100644 index 59a7fd4365..0000000000 --- a/changelogs/unreleased/4650-shubham-pampattiwar +++ /dev/null @@ -1 +0,0 @@ -Add multiple label selector support to Velero Backup and Restore APIs diff --git a/changelogs/unreleased/4655-ywk253100 b/changelogs/unreleased/4655-ywk253100 deleted file mode 100644 index cfc440fe70..0000000000 --- a/changelogs/unreleased/4655-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Convert Pod Volume Restore resource/controller to the Kubebuilder framework \ No newline at end of file diff --git a/changelogs/unreleased/4660-jxun b/changelogs/unreleased/4660-jxun deleted file mode 100644 index cf09ca08e2..0000000000 --- a/changelogs/unreleased/4660-jxun +++ /dev/null @@ -1 +0,0 @@ -Update --use-owner-references-in-backup description in velero command line. \ No newline at end of file diff --git a/changelogs/unreleased/4661-jxun b/changelogs/unreleased/4661-jxun deleted file mode 100644 index 3c08a0e3c5..0000000000 --- a/changelogs/unreleased/4661-jxun +++ /dev/null @@ -1 +0,0 @@ -Avoid overwritten hook's exec.container parameter when running pod command executor. \ No newline at end of file diff --git a/changelogs/unreleased/4680-jxun b/changelogs/unreleased/4680-jxun deleted file mode 100644 index 9d6b56ba2d..0000000000 --- a/changelogs/unreleased/4680-jxun +++ /dev/null @@ -1 +0,0 @@ -Support regional pv for GKE \ No newline at end of file diff --git a/changelogs/unreleased/4686-reasonerjt b/changelogs/unreleased/4686-reasonerjt deleted file mode 100644 index ed64de8f64..0000000000 --- a/changelogs/unreleased/4686-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Bypass the remap CRD version plugin when v1beta1 CRD is not supported diff --git a/changelogs/unreleased/4692-jxun b/changelogs/unreleased/4692-jxun deleted file mode 100644 index b18f53f860..0000000000 --- a/changelogs/unreleased/4692-jxun +++ /dev/null @@ -1 +0,0 @@ -Add GINKGO_SKIP to support skip specific case in e2e test. \ No newline at end of file diff --git a/changelogs/unreleased/4694-j4m3s-s b/changelogs/unreleased/4694-j4m3s-s deleted file mode 100644 index 636d635dbb..0000000000 --- a/changelogs/unreleased/4694-j4m3s-s +++ /dev/null @@ -1 +0,0 @@ -Add --pod-labels flag to velero install diff --git a/changelogs/unreleased/4704-reasonerjt b/changelogs/unreleased/4704-reasonerjt deleted file mode 100644 index 79218ea620..0000000000 --- a/changelogs/unreleased/4704-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Enable coverage in test.sh and upload to codecov \ No newline at end of file diff --git a/changelogs/unreleased/4719-ywk253100 b/changelogs/unreleased/4719-ywk253100 deleted file mode 100644 index 96f584864a..0000000000 --- a/changelogs/unreleased/4719-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Mark the BSL as "Unavailable" when gets any error and add a new field "Message" to the status to record the error message \ No newline at end of file diff --git a/changelogs/unreleased/4725-jxun b/changelogs/unreleased/4725-jxun deleted file mode 100644 index f9e0e4d94a..0000000000 --- a/changelogs/unreleased/4725-jxun +++ /dev/null @@ -1 +0,0 @@ -Support multiple skip option for E2E test \ No newline at end of file diff --git a/changelogs/unreleased/4740-phuongatemc b/changelogs/unreleased/4740-phuongatemc deleted file mode 100644 index 0227a96655..0000000000 --- a/changelogs/unreleased/4740-phuongatemc +++ /dev/null @@ -1,2 +0,0 @@ -Add PriorityClass to the AdditionalItems of Backup's PodAction and Restore's PodAction plugin to -backup and restore PriorityClass if it is used by a Pod. diff --git a/changelogs/unreleased/4743-sseago b/changelogs/unreleased/4743-sseago deleted file mode 100644 index 8772e78085..0000000000 --- a/changelogs/unreleased/4743-sseago +++ /dev/null @@ -1 +0,0 @@ -Insert all restore errors and warnings into restore log. diff --git a/changelogs/unreleased/4748-ywk253100 b/changelogs/unreleased/4748-ywk253100 deleted file mode 100644 index 0b6ea993ff..0000000000 --- a/changelogs/unreleased/4748-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Refactor schedule controller with kubebuilder \ No newline at end of file diff --git a/changelogs/unreleased/4757-kaovilai b/changelogs/unreleased/4757-kaovilai deleted file mode 100644 index 34229c0447..0000000000 --- a/changelogs/unreleased/4757-kaovilai +++ /dev/null @@ -1 +0,0 @@ -Garbage collector now adds labels to backups that failed to delete for BSLNotFound, BSLCannotGet, BSLReadOnly reasons. \ No newline at end of file diff --git a/changelogs/unreleased/4769-half-life666 b/changelogs/unreleased/4769-half-life666 deleted file mode 100644 index 374f5ce3cb..0000000000 --- a/changelogs/unreleased/4769-half-life666 +++ /dev/null @@ -1 +0,0 @@ -Skip podvolumerestore creation when restore excludes pv/pvc diff --git a/changelogs/unreleased/4778-jxun b/changelogs/unreleased/4778-jxun deleted file mode 100644 index e805735b22..0000000000 --- a/changelogs/unreleased/4778-jxun +++ /dev/null @@ -1 +0,0 @@ -Add parameter for e2e test to support modify kibishii install path. \ No newline at end of file diff --git a/changelogs/unreleased/4779-reasonerjt b/changelogs/unreleased/4779-reasonerjt deleted file mode 100644 index 869257e84d..0000000000 --- a/changelogs/unreleased/4779-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Ensure the restore hook applied to new namespace based on the mapping \ No newline at end of file diff --git a/changelogs/unreleased/4785-RafaeLeal b/changelogs/unreleased/4785-RafaeLeal deleted file mode 100644 index 450fed9c35..0000000000 --- a/changelogs/unreleased/4785-RafaeLeal +++ /dev/null @@ -1 +0,0 @@ -Add ability to restore status on selected resources diff --git a/changelogs/unreleased/4797-jxun b/changelogs/unreleased/4797-jxun deleted file mode 100644 index 795842678b..0000000000 --- a/changelogs/unreleased/4797-jxun +++ /dev/null @@ -1 +0,0 @@ -Do not take snapshot for PV to avoid duplicated snapshotting, when CSI feature is enabled. \ No newline at end of file diff --git a/changelogs/unreleased/4800-reasonerjt b/changelogs/unreleased/4800-reasonerjt deleted file mode 100644 index c4b1922ce6..0000000000 --- a/changelogs/unreleased/4800-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Bump up to v1 API for CSI snapshot \ No newline at end of file diff --git a/changelogs/unreleased/4817-yuvalman b/changelogs/unreleased/4817-yuvalman deleted file mode 100644 index fcacccaa06..0000000000 --- a/changelogs/unreleased/4817-yuvalman +++ /dev/null @@ -1 +0,0 @@ -fix: delete empty backups diff --git a/changelogs/unreleased/4818-jxun b/changelogs/unreleased/4818-jxun deleted file mode 100644 index f3e478c706..0000000000 --- a/changelogs/unreleased/4818-jxun +++ /dev/null @@ -1 +0,0 @@ -Add CSI VolumeSnapshot related metrics. diff --git a/changelogs/unreleased/4831-qiuming-best b/changelogs/unreleased/4831-qiuming-best deleted file mode 100644 index 3e941627d7..0000000000 --- a/changelogs/unreleased/4831-qiuming-best +++ /dev/null @@ -1 +0,0 @@ -Fix default-backup-ttl not work diff --git a/changelogs/unreleased/4832-reasonerjt b/changelogs/unreleased/4832-reasonerjt deleted file mode 100644 index 07f9a34994..0000000000 --- a/changelogs/unreleased/4832-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Make the vsc created by backup sync controller deletable \ No newline at end of file diff --git a/changelogs/unreleased/4833-ywk253100 b/changelogs/unreleased/4833-ywk253100 deleted file mode 100644 index 188528745e..0000000000 --- a/changelogs/unreleased/4833-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Make in-progress backup/restore as failed when doing the reconcile to avoid hanging in in-progress status \ No newline at end of file diff --git a/changelogs/unreleased/4838-ywk253100 b/changelogs/unreleased/4838-ywk253100 deleted file mode 100644 index 70befd62ac..0000000000 --- a/changelogs/unreleased/4838-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Use controller-gen to generate the deep copy methods for objects \ No newline at end of file diff --git a/changelogs/unreleased/4839-jxun b/changelogs/unreleased/4839-jxun deleted file mode 100644 index d3ee746d82..0000000000 --- a/changelogs/unreleased/4839-jxun +++ /dev/null @@ -1 +0,0 @@ -Update integrated Restic version and add insecureSkipTLSVerify for Restic CLI. \ No newline at end of file diff --git a/changelogs/unreleased/4854-jxun b/changelogs/unreleased/4854-jxun deleted file mode 100644 index abecfa4e1e..0000000000 --- a/changelogs/unreleased/4854-jxun +++ /dev/null @@ -1 +0,0 @@ -Modify CSI VolumeSnapshot metric related code. \ No newline at end of file diff --git a/changelogs/unreleased/4855-reasonerjt b/changelogs/unreleased/4855-reasonerjt deleted file mode 100644 index 3f05d33551..0000000000 --- a/changelogs/unreleased/4855-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Refactor backup deletion controller based on kubebuilder \ No newline at end of file diff --git a/changelogs/unreleased/4858-jxun b/changelogs/unreleased/4858-jxun deleted file mode 100644 index 190637cd92..0000000000 --- a/changelogs/unreleased/4858-jxun +++ /dev/null @@ -1 +0,0 @@ -Remove VolumeSnapshots created during backup when CSI feature is enabled. \ No newline at end of file diff --git a/changelogs/unreleased/4859-qiuming-best b/changelogs/unreleased/4859-qiuming-best deleted file mode 100644 index f92fc7fa00..0000000000 --- a/changelogs/unreleased/4859-qiuming-best +++ /dev/null @@ -1 +0,0 @@ -Convert Restic Repository resource/controller to the Kubebuilder framework diff --git a/changelogs/unreleased/4866-reasonerjt b/changelogs/unreleased/4866-reasonerjt deleted file mode 100644 index 118106c269..0000000000 --- a/changelogs/unreleased/4866-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Add ClusterClasses to the restore priority list \ No newline at end of file diff --git a/changelogs/unreleased/4872-big-appled b/changelogs/unreleased/4872-big-appled deleted file mode 100644 index 739fb5ab11..0000000000 --- a/changelogs/unreleased/4872-big-appled +++ /dev/null @@ -1 +0,0 @@ -Cleanup the .velero folder after restic done \ No newline at end of file diff --git a/changelogs/unreleased/4887-reasonerjt b/changelogs/unreleased/4887-reasonerjt deleted file mode 100644 index 069850f9c5..0000000000 --- a/changelogs/unreleased/4887-reasonerjt +++ /dev/null @@ -1 +0,0 @@ -Delete orphan CSI snapshots in backup sync controller \ No newline at end of file diff --git a/changelogs/unreleased/4889-jxun b/changelogs/unreleased/4889-jxun deleted file mode 100644 index 332bf21d0a..0000000000 --- a/changelogs/unreleased/4889-jxun +++ /dev/null @@ -1 +0,0 @@ -Make waiting VolumeSnapshot to ready process parallel. \ No newline at end of file diff --git a/changelogs/unreleased/4890-sseago b/changelogs/unreleased/4890-sseago deleted file mode 100644 index 7245436dcb..0000000000 --- a/changelogs/unreleased/4890-sseago +++ /dev/null @@ -1 +0,0 @@ -continue rather than return for non-matching restore action label diff --git a/changelogs/unreleased/4893-ywk253100 b/changelogs/unreleased/4893-ywk253100 deleted file mode 100644 index 751c57bac9..0000000000 --- a/changelogs/unreleased/4893-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Make in-progress PVB/PVR as failed when restic controller restarts to avoid hanging backup/restore \ No newline at end of file diff --git a/changelogs/unreleased/4894-jxun b/changelogs/unreleased/4894-jxun deleted file mode 100644 index dcac05202d..0000000000 --- a/changelogs/unreleased/4894-jxun +++ /dev/null @@ -1 +0,0 @@ -Refactor BSL controller with periodical enqueue source \ No newline at end of file diff --git a/changelogs/unreleased/4897-ywk253100 b/changelogs/unreleased/4897-ywk253100 deleted file mode 100644 index a5f5a6809f..0000000000 --- a/changelogs/unreleased/4897-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Make garbage collection for expired backups configurable \ No newline at end of file diff --git a/changelogs/unreleased/4898-ywk253100 b/changelogs/unreleased/4898-ywk253100 deleted file mode 100644 index e0f9c89a90..0000000000 --- a/changelogs/unreleased/4898-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Bump up the version of distroless to base-debian11 \ No newline at end of file diff --git a/changelogs/unreleased/4913-qiuming-best b/changelogs/unreleased/4913-qiuming-best deleted file mode 100644 index 406319cc54..0000000000 --- a/changelogs/unreleased/4913-qiuming-best +++ /dev/null @@ -1 +0,0 @@ -Add schedule ordered resources E2E test diff --git a/changelogs/unreleased/4914-jxun b/changelogs/unreleased/4914-jxun deleted file mode 100644 index 84db5b83f5..0000000000 --- a/changelogs/unreleased/4914-jxun +++ /dev/null @@ -1 +0,0 @@ -Make velero completion zsh command output can be used by `source` command. \ No newline at end of file diff --git a/changelogs/unreleased/4920-ywk253100 b/changelogs/unreleased/4920-ywk253100 deleted file mode 100644 index 0867b6935c..0000000000 --- a/changelogs/unreleased/4920-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Enhance the map flag to support parsing input value contains entry delimiters \ No newline at end of file diff --git a/changelogs/unreleased/4968-jxun b/changelogs/unreleased/4968-jxun deleted file mode 100644 index 5a47988625..0000000000 --- a/changelogs/unreleased/4968-jxun +++ /dev/null @@ -1 +0,0 @@ -Fix E2E test [Backups][Deletion][Restic] on GCP. \ No newline at end of file diff --git a/changelogs/unreleased/4972-ywk253100 b/changelogs/unreleased/4972-ywk253100 deleted file mode 100644 index f15a06d83e..0000000000 --- a/changelogs/unreleased/4972-ywk253100 +++ /dev/null @@ -1 +0,0 @@ -Disable status as sub resource in CRDs \ No newline at end of file diff --git a/changelogs/unreleased/4988-jxun b/changelogs/unreleased/4988-jxun deleted file mode 100644 index 204c8efb35..0000000000 --- a/changelogs/unreleased/4988-jxun +++ /dev/null @@ -1 +0,0 @@ -Add more information for failing to get path or snapshot in restic backup and restore. \ No newline at end of file diff --git a/site/config.yaml b/site/config.yaml index 1a9e926e4f..a5a05965a3 100644 --- a/site/config.yaml +++ b/site/config.yaml @@ -12,9 +12,10 @@ params: hero: backgroundColor: med-blue versioning: true - latest: v1.8 + latest: v1.9 versions: - main + - v1.9 - v1.8 - v1.7 - v1.6 diff --git a/site/content/docs/main/upgrade-to-1.8.md b/site/content/docs/main/upgrade-to-1.9.md similarity index 83% rename from site/content/docs/main/upgrade-to-1.8.md rename to site/content/docs/main/upgrade-to-1.9.md index 3a1e80cf56..25186dcd23 100644 --- a/site/content/docs/main/upgrade-to-1.8.md +++ b/site/content/docs/main/upgrade-to-1.9.md @@ -1,11 +1,11 @@ --- -title: "Upgrading to Velero 1.8" +title: "Upgrading to Velero 1.9" layout: docs --- ## Prerequisites -- Velero [v1.7.x][7] installed. +- Velero [v1.8.x][8] installed. If you're not yet running at least Velero v1.6, see the following: @@ -16,12 +16,13 @@ If you're not yet running at least Velero v1.6, see the following: - [Upgrading to v1.5][5] - [Upgrading to v1.6][6] - [Upgrading to v1.7][7] +- [Upgrading to v1.8][8] Before upgrading, check the [Velero compatibility matrix](https://github.com/vmware-tanzu/velero#velero-compatabilty-matrix) to make sure your version of Kubernetes is supported by the new version of Velero. ## Instructions -1. Install the Velero v1.8 command-line interface (CLI) by following the [instructions here][0]. +1. Install the Velero v1.9 command-line interface (CLI) by following the [instructions here][0]. Verify that you've properly installed it by running: @@ -33,7 +34,7 @@ Before upgrading, check the [Velero compatibility matrix](https://github.com/vmw ```bash Client: - Version: v1.8.0 + Version: v1.9.0 Git commit: ``` @@ -43,18 +44,18 @@ Before upgrading, check the [Velero compatibility matrix](https://github.com/vmw velero install --crds-only --dry-run -o yaml | kubectl apply -f - ``` - **NOTE:** Since velero v1.8.0 only v1 CRD will be supported during installation, therefore, the v1.8.0 will only work on kubernetes version >= v1.16 + **NOTE:** Since velero v1.9.0 only v1 CRD will be supported during installation, therefore, the v1.9.0 will only work on kubernetes version >= v1.16 1. Update the container image used by the Velero deployment and, optionally, the restic daemon set: ```bash kubectl set image deployment/velero \ - velero=velero/velero:v1.8.0 \ + velero=velero/velero:v1.9.0 \ --namespace velero # optional, if using the restic daemon set kubectl set image daemonset/restic \ - restic=velero/velero:v1.8.0 \ + restic=velero/velero:v1.9.0 \ --namespace velero ``` @@ -68,11 +69,11 @@ Before upgrading, check the [Velero compatibility matrix](https://github.com/vmw ```bash Client: - Version: v1.8.0 + Version: v1.9.0 Git commit: Server: - Version: v1.8.0 + Version: v1.9.0 ``` ## Notes @@ -89,4 +90,5 @@ After upgrading, if there is a previously created backup storage location with t [5]: https://velero.io/docs/v1.5/upgrade-to-1.5 [6]: https://velero.io/docs/v1.6/upgrade-to-1.6 [7]: https://velero.io/docs/v1.7/upgrade-to-1.7 -[9]: https://velero.io/docs/v1.8/locations +[8]: https://velero.io/docs/v1.8/upgrade-to-1.8 +[9]: https://velero.io/docs/v1.9/locations \ No newline at end of file diff --git a/site/content/docs/v1.9/_index.md b/site/content/docs/v1.9/_index.md new file mode 100644 index 0000000000..911f8b571e --- /dev/null +++ b/site/content/docs/v1.9/_index.md @@ -0,0 +1,58 @@ +--- +toc: "false" +cascade: + version: main + toc: "true" +--- +![100] + +[![Build Status][1]][2] + +## Overview + +Velero (formerly Heptio Ark) gives you tools to back up and restore your Kubernetes cluster resources and persistent volumes. You can run Velero with a cloud provider or on-premises. Velero lets you: + +* Take backups of your cluster and restore in case of loss. +* Migrate cluster resources to other clusters. +* Replicate your production cluster to development and testing clusters. + +Velero consists of: + +* A server that runs on your cluster +* A command-line client that runs locally + +## Documentation + +This site is our documentation home with installation instructions, plus information about customizing Velero for your needs, architecture, extending Velero, contributing to Velero and more. + +Please use the version selector at the top of the site to ensure you are using the appropriate documentation for your version of Velero. + +## Troubleshooting + +If you encounter issues, review the [troubleshooting docs][30], [file an issue][4], or talk to us on the [#velero channel][25] on the Kubernetes Slack server. + +## Contributing + +If you are ready to jump in and test, add code, or help with documentation, follow the instructions on our [Start contributing](https://velero.io/docs/v1.9/start-contributing/) documentation for guidance on how to setup Velero for development. + +## Changelog + +See [the list of releases][6] to find out about feature changes. + +[1]: https://github.com/vmware-tanzu/velero/workflows/Main%20CI/badge.svg +[2]: https://github.com/vmware-tanzu/velero/actions?query=workflow%3A"Main+CI" + +[4]: https://github.com/vmware-tanzu/velero/issues +[6]: https://github.com/vmware-tanzu/velero/releases + +[9]: https://kubernetes.io/docs/setup/ +[10]: https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-with-homebrew-on-macos +[11]: https://kubernetes.io/docs/tasks/tools/install-kubectl/#tabset-1 +[12]: https://github.com/kubernetes/kubernetes/blob/main/cluster/addons/dns/README.md +[14]: https://github.com/kubernetes/kubernetes +[24]: https://groups.google.com/forum/#!forum/projectvelero +[25]: https://kubernetes.slack.com/messages/velero + +[30]: troubleshooting.md + +[100]: img/velero.png diff --git a/site/content/docs/v1.9/api-types/README.md b/site/content/docs/v1.9/api-types/README.md new file mode 100644 index 0000000000..54c23544dd --- /dev/null +++ b/site/content/docs/v1.9/api-types/README.md @@ -0,0 +1,21 @@ +--- +title: "Table of Contents" +layout: docs +--- + +## API types + +Here we list the API types that have some functionality that you can only configure via json/yaml vs the `velero` cli +(hooks) + +* [Backup][1] +* [Restore][2] +* [Schedule][3] +* [BackupStorageLocation][4] +* [VolumeSnapshotLocation][5] + +[1]: backup.md +[2]: restore.md +[3]: schedule.md +[4]: backupstoragelocation.md +[5]: volumesnapshotlocation.md diff --git a/site/content/docs/v1.9/api-types/_index.md b/site/content/docs/v1.9/api-types/_index.md new file mode 100644 index 0000000000..e608325006 --- /dev/null +++ b/site/content/docs/v1.9/api-types/_index.md @@ -0,0 +1,19 @@ +--- +layout: docs +title: API types +--- + +Here's a list the API types that have some functionality that you can only configure via json/yaml vs the `velero` cli +(hooks) + +* [Backup][1] +* [Restore][2] +* [Schedule][3] +* [BackupStorageLocation][4] +* [VolumeSnapshotLocation][5] + +[1]: backup.md +[2]: restore.md +[3]: schedule.md +[4]: backupstoragelocation.md +[5]: volumesnapshotlocation.md diff --git a/site/content/docs/v1.9/api-types/backup.md b/site/content/docs/v1.9/api-types/backup.md new file mode 100644 index 0000000000..37cb30bf9e --- /dev/null +++ b/site/content/docs/v1.9/api-types/backup.md @@ -0,0 +1,157 @@ +--- +title: "Backup API Type" +layout: docs +--- + +## Use + +Use the `Backup` API type to request the Velero server to perform a backup. Once created, the +Velero Server immediately starts the backup process. + +## API GroupVersion + +Backup belongs to the API group version `velero.io/v1`. + +## Definition + +Here is a sample `Backup` object with each of the fields documented: + +```yaml +# Standard Kubernetes API Version declaration. Required. +apiVersion: velero.io/v1 +# Standard Kubernetes Kind declaration. Required. +kind: Backup +# Standard Kubernetes metadata. Required. +metadata: + # Backup name. May be any valid Kubernetes object name. Required. + name: a + # Backup namespace. Must be the namespace of the Velero server. Required. + namespace: velero +# Parameters about the backup. Required. +spec: + # Array of namespaces to include in the backup. If unspecified, all namespaces are included. + # Optional. + includedNamespaces: + - '*' + # Array of namespaces to exclude from the backup. Optional. + excludedNamespaces: + - some-namespace + # Array of resources to include in the backup. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. If unspecified, all resources are included. Optional. + includedResources: + - '*' + # Array of resources to exclude from the backup. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. Optional. + excludedResources: + - storageclasses.storage.k8s.io + # Whether or not to include cluster-scoped resources. Valid values are true, false, and + # null/unset. If true, all cluster-scoped resources are included (subject to included/excluded + # resources and the label selector). If false, no cluster-scoped resources are included. If unset, + # all cluster-scoped resources are included if and only if all namespaces are included and there are + # no excluded namespaces. Otherwise, if there is at least one namespace specified in either + # includedNamespaces or excludedNamespaces, then the only cluster-scoped resources that are backed + # up are those associated with namespace-scoped resources included in the backup. For example, if a + # PersistentVolumeClaim is included in the backup, its associated PersistentVolume (which is + # cluster-scoped) would also be backed up. + includeClusterResources: null + # Individual objects must match this label selector to be included in the backup. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # Individual object when matched with any of the label selector specified in the set are to be included in the backup. Optional. + # orLabelSelectors as well as labelSelector cannot co-exist, only one of them can be specified in the backup request + orLabelSelectors: + - matchLabels: + app: velero + - matchLabels: + app: data-protection + # Whether or not to snapshot volumes. This only applies to PersistentVolumes for Azure, GCE, and + # AWS. Valid values are true, false, and null/unset. If unset, Velero performs snapshots as long as + # a persistent volume provider is configured for Velero. + snapshotVolumes: null + # Where to store the tarball and logs. + storageLocation: aws-primary + # The list of locations in which to store volume snapshots created for this backup. + volumeSnapshotLocations: + - aws-primary + - gcp-primary + # The amount of time before this backup is eligible for garbage collection. If not specified, + # a default value of 30 days will be used. The default can be configured on the velero server + # by passing the flag --default-backup-ttl. + ttl: 24h0m0s + # Whether restic should be used to take a backup of all pod volumes by default. + defaultVolumesToRestic: true + # Actions to perform at different times during a backup. The only hook supported is + # executing a command in a container in a pod using the pod exec API. Optional. + hooks: + # Array of hooks that are applicable to specific resources. Optional. + resources: + - + # Name of the hook. Will be displayed in backup log. + name: my-hook + # Array of namespaces to which this hook applies. If unspecified, the hook applies to all + # namespaces. Optional. + includedNamespaces: + - '*' + # Array of namespaces to which this hook does not apply. Optional. + excludedNamespaces: + - some-namespace + # Array of resources to which this hook applies. The only resource supported at this time is + # pods. + includedResources: + - pods + # Array of resources to which this hook does not apply. Optional. + excludedResources: [] + # This hook only applies to objects matching this label selector. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # An array of hooks to run before executing custom actions. Only "exec" hooks are supported. + pre: + - + # The type of hook. This must be "exec". + exec: + # The name of the container where the command will be executed. If unspecified, the + # first container in the pod will be used. Optional. + container: my-container + # The command to execute, specified as an array. Required. + command: + - /bin/uname + - -a + # How to handle an error executing the command. Valid values are Fail and Continue. + # Defaults to Fail. Optional. + onError: Fail + # How long to wait for the command to finish executing. Defaults to 30 seconds. Optional. + timeout: 10s + # An array of hooks to run after all custom actions and additional items have been + # processed. Only "exec" hooks are supported. + post: + # Same content as pre above. +# Status about the Backup. Users should not set any data here. +status: + # The version of this Backup. The only version supported is 1. + version: 1 + # The date and time when the Backup is eligible for garbage collection. + expiration: null + # The current phase. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. + phase: "" + # An array of any validation errors encountered. + validationErrors: null + # Date/time when the backup started being processed. + startTimestamp: 2019-04-29T15:58:43Z + # Date/time when the backup finished being processed. + completionTimestamp: 2019-04-29T15:58:56Z + # Number of volume snapshots that Velero tried to create for this backup. + volumeSnapshotsAttempted: 2 + # Number of volume snapshots that Velero successfully created for this backup. + volumeSnapshotsCompleted: 1 + # Number of warnings that were logged by the backup. + warnings: 2 + # Number of errors that were logged by the backup. + errors: 0 + # An error that caused the entire backup to fail. + failureReason: "" + +``` diff --git a/site/content/docs/v1.9/api-types/backupstoragelocation.md b/site/content/docs/v1.9/api-types/backupstoragelocation.md new file mode 100644 index 0000000000..b6c58ece71 --- /dev/null +++ b/site/content/docs/v1.9/api-types/backupstoragelocation.md @@ -0,0 +1,54 @@ +--- +title: "Velero Backup Storage Locations" +layout: docs +--- + +## Backup Storage Location + +Velero can store backups in a number of locations. These are represented in the cluster via the `BackupStorageLocation` CRD. + +Velero must have at least one `BackupStorageLocation`. By default, this is expected to be named `default`, however the name can be changed by specifying `--default-backup-storage-location` on `velero server`. Backups that do not explicitly specify a storage location will be saved to this `BackupStorageLocation`. + +A sample YAML `BackupStorageLocation` looks like the following: + +```yaml +apiVersion: velero.io/v1 +kind: BackupStorageLocation +metadata: + name: default + namespace: velero +spec: + backupSyncPeriod: 2m0s + provider: aws + objectStorage: + bucket: myBucket + credential: + name: secret-name + key: key-in-secret + config: + region: us-west-2 + profile: "default" +``` + +### Parameter Reference + +The configurable parameters are as follows: + +#### Main config parameters + +{{< table caption="Main config parameters" >}} +| Key | Type | Default | Meaning | +| --- | --- | --- | --- | +| `provider` | String | Required Field | The name for whichever object storage provider will be used to store the backups. See [your object storage provider's plugin documentation](../supported-providers) for the appropriate value to use. | +| `objectStorage` | ObjectStorageLocation | Required Field | Specification of the object storage for the given provider. | +| `objectStorage/bucket` | String | Required Field | The storage bucket where backups are to be uploaded. | +| `objectStorage/prefix` | String | Optional Field | The directory inside a storage bucket where backups are to be uploaded. | +| `objectStorage/caCert` | String | Optional Field | A base64 encoded CA bundle to be used when verifying TLS connections | +| `config` | map[string]string | None (Optional) | Provider-specific configuration keys/values to be passed to the object store plugin. See [your object storage provider's plugin documentation](../supported-providers) for details. | +| `accessMode` | String | `ReadWrite` | How Velero can access the backup storage location. Valid values are `ReadWrite`, `ReadOnly`. | +| `backupSyncPeriod` | metav1.Duration | Optional Field | How frequently Velero should synchronize backups in object storage. Default is Velero's server backup sync period. Set this to `0s` to disable sync. | +| `validationFrequency` | metav1.Duration | Optional Field | How frequently Velero should validate the object storage . Default is Velero's server validation frequency. Set this to `0s` to disable validation. Default 1 minute. | +| `credential` | [corev1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#secretkeyselector-v1-core) | Optional Field | The credential information to be used with this location. | +| `credential/name` | String | Optional Field | The name of the secret within the Velero namespace which contains the credential information. | +| `credential/key` | String | Optional Field | The key to use within the secret. | +{{< /table >}} diff --git a/site/content/docs/v1.9/api-types/restore.md b/site/content/docs/v1.9/api-types/restore.md new file mode 100644 index 0000000000..ae4354666b --- /dev/null +++ b/site/content/docs/v1.9/api-types/restore.md @@ -0,0 +1,193 @@ +--- +title: "Restore API Type" +layout: docs +--- + +## Use + +The `Restore` API type is used as a request for the Velero server to perform a Restore. Once created, the +Velero Server immediately starts the Restore process. + +## API GroupVersion + +Restore belongs to the API group version `velero.io/v1`. + +## Definition + +Here is a sample `Restore` object with each of the fields documented: + +```yaml +# Standard Kubernetes API Version declaration. Required. +apiVersion: velero.io/v1 +# Standard Kubernetes Kind declaration. Required. +kind: Restore +# Standard Kubernetes metadata. Required. +metadata: + # Restore name. May be any valid Kubernetes object name. Required. + name: a-very-special-backup-0000111122223333 + # Restore namespace. Must be the namespace of the Velero server. Required. + namespace: velero +# Parameters about the restore. Required. +spec: + # BackupName is the unique name of the Velero backup to restore from. + backupName: a-very-special-backup + # Array of namespaces to include in the restore. If unspecified, all namespaces are included. + # Optional. + includedNamespaces: + - '*' + # Array of namespaces to exclude from the restore. Optional. + excludedNamespaces: + - some-namespace + # Array of resources to include in the restore. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. If unspecified, all resources are included. Optional. + includedResources: + - '*' + # Array of resources to exclude from the restore. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. Optional. + excludedResources: + - storageclasses.storage.k8s.io + + # restoreStatus selects resources to restore not only the specification, but + # the status of the manifest. This is specially useful for CRDs that maintain + # external references. By default, it excludes all resources. + restoreStatus: + # Array of resources to include in the restore status. Just like above, + # resources may be shortcuts (for example 'po' for 'pods') or fully-qualified. + # If unspecified, no resources are included. Optional. + includedResources: + - workflows + # Array of resources to exclude from the restore status. Resources may be + # shortcuts (for example 'po' for 'pods') or fully-qualified. + # If unspecified, all resources are excluded. Optional. + excludedResources: [] + + # Whether or not to include cluster-scoped resources. Valid values are true, false, and + # null/unset. If true, all cluster-scoped resources are included (subject to included/excluded + # resources and the label selector). If false, no cluster-scoped resources are included. If unset, + # all cluster-scoped resources are included if and only if all namespaces are included and there are + # no excluded namespaces. Otherwise, if there is at least one namespace specified in either + # includedNamespaces or excludedNamespaces, then the only cluster-scoped resources that are backed + # up are those associated with namespace-scoped resources included in the restore. For example, if a + # PersistentVolumeClaim is included in the restore, its associated PersistentVolume (which is + # cluster-scoped) would also be backed up. + includeClusterResources: null + # Individual objects must match this label selector to be included in the restore. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # Individual object when matched with any of the label selector specified in the set are to be included in the restore. Optional. + # orLabelSelectors as well as labelSelector cannot co-exist, only one of them can be specified in the restore request + orLabelSelectors: + - matchLabels: + app: velero + - matchLabels: + app: data-protection + # NamespaceMapping is a map of source namespace names to + # target namespace names to restore into. Any source namespaces not + # included in the map will be restored into namespaces of the same name. + namespaceMapping: + namespace-backup-from: namespace-to-restore-to + # RestorePVs specifies whether to restore all included PVs + # from snapshot (via the cloudprovider). + restorePVs: true + # ScheduleName is the unique name of the Velero schedule + # to restore from. If specified, and BackupName is empty, Velero will + # restore from the most recent successful backup created from this schedule. + scheduleName: my-scheduled-backup-name + # ExistingResourcePolicy specifies the restore behaviour + # for the kubernetes resource to be restored. Optional + existingResourcePolicy: none + # Actions to perform during or post restore. The only hooks currently supported are + # adding an init container to a pod before it can be restored and executing a command in a + # restored pod's container. Optional. + hooks: + # Array of hooks that are applicable to specific resources. Optional. + resources: + # Name is the name of this hook. + - name: restore-hook-1 + # Array of namespaces to which this hook applies. If unspecified, the hook applies to all + # namespaces. Optional. + includedNamespaces: + - ns1 + # Array of namespaces to which this hook does not apply. Optional. + excludedNamespaces: + - ns3 + # Array of resources to which this hook applies. If unspecified, the hook applies to all resources in the backup. Optional. + # The only resource supported at this time is pods. + includedResources: + - pods + # Array of resources to which this hook does not apply. Optional. + excludedResources: [] + # This hook only applies to objects matching this label selector. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # An array of hooks to run during or after restores. Currently only "init" and "exec" hooks + # are supported. + postHooks: + # The type of the hook. This must be "init" or "exec". + - init: + # An array of container specs to be added as init containers to pods to which this hook applies to. + initContainers: + - name: restore-hook-init1 + image: alpine:latest + # Mounting volumes from the podSpec to which this hooks applies to. + volumeMounts: + - mountPath: /restores/pvc1-vm + # Volume name from the podSpec + name: pvc1-vm + command: + - /bin/ash + - -c + - echo -n "FOOBARBAZ" >> /restores/pvc1-vm/foobarbaz + - name: restore-hook-init2 + image: alpine:latest + # Mounting volumes from the podSpec to which this hooks applies to. + volumeMounts: + - mountPath: /restores/pvc2-vm + # Volume name from the podSpec + name: pvc2-vm + command: + - /bin/ash + - -c + - echo -n "DEADFEED" >> /restores/pvc2-vm/deadfeed + - exec: + # The container name where the hook will be executed. Defaults to the first container. + # Optional. + container: foo + # The command that will be executed in the container. Required. + command: + - /bin/bash + - -c + - "psql < /backup/backup.sql" + # How long to wait for a container to become ready. This should be long enough for the + # container to start plus any preceding hooks in the same container to complete. The wait + # timeout begins when the container is restored and may require time for the image to pull + # and volumes to mount. If not set the restore will wait indefinitely. Optional. + waitTimeout: 5m + # How long to wait once execution begins. Defaults to 30 seconds. Optional. + execTimeout: 1m + # How to handle execution failures. Valid values are `Fail` and `Continue`. Defaults to + # `Continue`. With `Continue` mode, execution failures are logged only. With `Fail` mode, + # no more restore hooks will be executed in any container in any pod and the status of the + # Restore will be `PartiallyFailed`. Optional. + onError: Continue +# RestoreStatus captures the current status of a Velero restore. Users should not set any data here. +status: + # The current phase. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. + phase: "" + # An array of any validation errors encountered. + validationErrors: null + # Number of warnings that were logged by the restore. + warnings: 2 + # Errors is a count of all error messages that were generated + # during execution of the restore. The actual errors are stored in object + # storage. + errors: 0 + # FailureReason is an error that caused the entire restore + # to fail. + failureReason: + +``` diff --git a/site/content/docs/v1.9/api-types/schedule.md b/site/content/docs/v1.9/api-types/schedule.md new file mode 100644 index 0000000000..9c5dc26cee --- /dev/null +++ b/site/content/docs/v1.9/api-types/schedule.md @@ -0,0 +1,142 @@ +--- +title: "Schedule API Type" +layout: docs +--- + +## Use + +The `Schedule` API type is used as a repeatable request for the Velero server to perform a backup for a given cron notation. Once created, the +Velero Server will start the backup process. It will then wait for the next valid point of the given cron expression and execute the backup +process on a repeating basis. + +## API GroupVersion + +Schedule belongs to the API group version `velero.io/v1`. + +## Definition + +Here is a sample `Schedule` object with each of the fields documented: + +```yaml +# Standard Kubernetes API Version declaration. Required. +apiVersion: velero.io/v1 +# Standard Kubernetes Kind declaration. Required. +kind: Schedule +# Standard Kubernetes metadata. Required. +metadata: + # Schedule name. May be any valid Kubernetes object name. Required. + name: a + # Schedule namespace. Must be the namespace of the Velero server. Required. + namespace: velero +# Parameters about the scheduled backup. Required. +spec: + # Schedule is a Cron expression defining when to run the Backup + schedule: 0 7 * * * + # Template is the spec that should be used for each backup triggered by this schedule. + template: + # Array of namespaces to include in the scheduled backup. If unspecified, all namespaces are included. + # Optional. + includedNamespaces: + - '*' + # Array of namespaces to exclude from the scheduled backup. Optional. + excludedNamespaces: + - some-namespace + # Array of resources to include in the scheduled backup. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. If unspecified, all resources are included. Optional. + includedResources: + - '*' + # Array of resources to exclude from the scheduled backup. Resources may be shortcuts (for example 'po' for 'pods') + # or fully-qualified. Optional. + excludedResources: + - storageclasses.storage.k8s.io + # Whether or not to include cluster-scoped resources. Valid values are true, false, and + # null/unset. If true, all cluster-scoped resources are included (subject to included/excluded + # resources and the label selector). If false, no cluster-scoped resources are included. If unset, + # all cluster-scoped resources are included if and only if all namespaces are included and there are + # no excluded namespaces. Otherwise, if there is at least one namespace specified in either + # includedNamespaces or excludedNamespaces, then the only cluster-scoped resources that are backed + # up are those associated with namespace-scoped resources included in the scheduled backup. For example, if a + # PersistentVolumeClaim is included in the backup, its associated PersistentVolume (which is + # cluster-scoped) would also be backed up. + includeClusterResources: null + # Individual objects must match this label selector to be included in the scheduled backup. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # Whether or not to snapshot volumes. This only applies to PersistentVolumes for Azure, GCE, and + # AWS. Valid values are true, false, and null/unset. If unset, Velero performs snapshots as long as + # a persistent volume provider is configured for Velero. + snapshotVolumes: null + # Where to store the tarball and logs. + storageLocation: aws-primary + # The list of locations in which to store volume snapshots created for backups under this schedule. + volumeSnapshotLocations: + - aws-primary + - gcp-primary + # The amount of time before backups created on this schedule are eligible for garbage collection. If not specified, + # a default value of 30 days will be used. The default can be configured on the velero server + # by passing the flag --default-backup-ttl. + ttl: 24h0m0s + # Whether restic should be used to take a backup of all pod volumes by default. + defaultVolumesToRestic: true + # The labels you want on backup objects, created from this schedule (instead of copying the labels you have on schedule object itself). + # When this field is set, the labels from the Schedule resource are not copied to the Backup resource. + metadata: + labels: + labelname: somelabelvalue + # Actions to perform at different times during a backup. The only hook supported is + # executing a command in a container in a pod using the pod exec API. Optional. + hooks: + # Array of hooks that are applicable to specific resources. Optional. + resources: + - + # Name of the hook. Will be displayed in backup log. + name: my-hook + # Array of namespaces to which this hook applies. If unspecified, the hook applies to all + # namespaces. Optional. + includedNamespaces: + - '*' + # Array of namespaces to which this hook does not apply. Optional. + excludedNamespaces: + - some-namespace + # Array of resources to which this hook applies. The only resource supported at this time is + # pods. + includedResources: + - pods + # Array of resources to which this hook does not apply. Optional. + excludedResources: [] + # This hook only applies to objects matching this label selector. Optional. + labelSelector: + matchLabels: + app: velero + component: server + # An array of hooks to run before executing custom actions. Only "exec" hooks are supported. + pre: + - + # The type of hook. This must be "exec". + exec: + # The name of the container where the command will be executed. If unspecified, the + # first container in the pod will be used. Optional. + container: my-container + # The command to execute, specified as an array. Required. + command: + - /bin/uname + - -a + # How to handle an error executing the command. Valid values are Fail and Continue. + # Defaults to Fail. Optional. + onError: Fail + # How long to wait for the command to finish executing. Defaults to 30 seconds. Optional. + timeout: 10s + # An array of hooks to run after all custom actions and additional items have been + # processed. Only "exec" hooks are supported. + post: + # Same content as pre above. +status: + # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. + phase: "" + # Date/time of the last backup for a given schedule + lastBackup: + # An array of any validation errors encountered. + validationErrors: +``` diff --git a/site/content/docs/v1.9/api-types/volumesnapshotlocation.md b/site/content/docs/v1.9/api-types/volumesnapshotlocation.md new file mode 100644 index 0000000000..28ac332222 --- /dev/null +++ b/site/content/docs/v1.9/api-types/volumesnapshotlocation.md @@ -0,0 +1,40 @@ +--- +title: "Velero Volume Snapshot Location" +layout: docs +--- + +## Volume Snapshot Location + +A volume snapshot location is the location in which to store the volume snapshots created for a backup. + +Velero can be configured to take snapshots of volumes from multiple providers. Velero also allows you to configure multiple possible `VolumeSnapshotLocation` per provider, although you can only select one location per provider at backup time. + +Each VolumeSnapshotLocation describes a provider + location. These are represented in the cluster via the `VolumeSnapshotLocation` CRD. Velero must have at least one `VolumeSnapshotLocation` per cloud provider. + +A sample YAML `VolumeSnapshotLocation` looks like the following: + +```yaml +apiVersion: velero.io/v1 +kind: VolumeSnapshotLocation +metadata: + name: aws-default + namespace: velero +spec: + provider: aws + config: + region: us-west-2 + profile: "default" +``` + +### Parameter Reference + +The configurable parameters are as follows: + +#### Main config parameters + +{{< table caption="Main config parameters" >}} +| Key | Type | Default | Meaning | +| --- | --- | --- | --- | +| `provider` | String | Required Field | The name for whichever storage provider will be used to create/store the volume snapshots. See [your volume snapshot provider's plugin documentation](../supported-providers) for the appropriate value to use. | +| `config` | map string string | None (Optional) | Provider-specific configuration keys/values to be passed to the volume snapshotter plugin. See [your volume snapshot provider's plugin documentation](../supported-providers) for details. | +{{< /table >}} diff --git a/site/content/docs/v1.9/backup-hooks.md b/site/content/docs/v1.9/backup-hooks.md new file mode 100644 index 0000000000..3a1ceffc6c --- /dev/null +++ b/site/content/docs/v1.9/backup-hooks.md @@ -0,0 +1,111 @@ +--- +title: "Backup Hooks" +layout: docs +--- + +Velero supports executing commands in containers in pods during a backup. + +## Backup Hooks + +When performing a backup, you can specify one or more commands to execute in a container in a pod +when that pod is being backed up. The commands can be configured to run *before* any custom action +processing ("pre" hooks), or after all custom actions have been completed and any additional items +specified by custom action have been backed up ("post" hooks). Note that hooks are _not_ executed within a shell +on the containers. + +There are two ways to specify hooks: annotations on the pod itself, and in the Backup spec. + +### Specifying Hooks As Pod Annotations + +You can use the following annotations on a pod to make Velero execute a hook when backing up the pod: + +#### Pre hooks + +* `pre.hook.backup.velero.io/container` + * The container where the command should be executed. Defaults to the first container in the pod. Optional. +* `pre.hook.backup.velero.io/command` + * The command to execute. This command is not executed within a shell by default. If a shell is needed to run your command, include a shell command, like `/bin/sh`, that is supported by the container at the beginning of your command. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]`. See [examples of using pre hook commands](#backup-hook-commands-examples). Optional. +* `pre.hook.backup.velero.io/on-error` + * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. +* `pre.hook.backup.velero.io/timeout` + * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults is 30s. Optional. + + +#### Post hooks + +* `post.hook.backup.velero.io/container` + * The container where the command should be executed. Default is the first container in the pod. Optional. +* `post.hook.backup.velero.io/command` + * The command to execute. This command is not executed within a shell by default. If a shell is needed to run your command, include a shell command, like `/bin/sh`, that is supported by the container at the beginning of your command. If you need multiple arguments, specify the command as a JSON array, such as `["/usr/bin/uname", "-a"]`. See [examples of using pre hook commands](#backup-hook-commands-examples). Optional. +* `post.hook.backup.velero.io/on-error` + * What to do if the command returns a non-zero exit code. Defaults is `Fail`. Valid values are Fail and Continue. Optional. +* `post.hook.backup.velero.io/timeout` + * How long to wait for the command to execute. The hook is considered in error if the command exceeds the timeout. Defaults is 30s. Optional. + +### Specifying Hooks in the Backup Spec + +Please see the documentation on the [Backup API Type][1] for how to specify hooks in the Backup +spec. + +## Hook Example with fsfreeze + +This examples walks you through using both pre and post hooks for freezing a file system. Freezing the +file system is useful to ensure that all pending disk I/O operations have completed prior to taking a snapshot. + +This example uses [examples/nginx-app/with-pv.yaml][2]. Follow the [steps for your provider][3] to +setup this example. + +### Annotations + +The Velero [example/nginx-app/with-pv.yaml][2] serves as an example of adding the pre and post hook annotations directly +to your declarative deployment. Below is an example of what updating an object in place might look like. + +```shell +kubectl annotate pod -n nginx-example -l app=nginx \ + pre.hook.backup.velero.io/command='["/sbin/fsfreeze", "--freeze", "/var/log/nginx"]' \ + pre.hook.backup.velero.io/container=fsfreeze \ + post.hook.backup.velero.io/command='["/sbin/fsfreeze", "--unfreeze", "/var/log/nginx"]' \ + post.hook.backup.velero.io/container=fsfreeze +``` + +Now test the pre and post hooks by creating a backup. You can use the Velero logs to verify that the pre and post +hooks are running and exiting without error. + +```shell +velero backup create nginx-hook-test + +velero backup get nginx-hook-test +velero backup logs nginx-hook-test | grep hookCommand +``` + +## Backup hook commands examples + +### Multiple commands + +To use multiple commands, wrap your target command in a shell and separate them with `;`, `&&`, or other shell conditional constructs. + +```shell + pre.hook.backup.velero.io/command='["/bin/bash", "-c", "echo hello > hello.txt && echo goodbye > goodbye.txt"]' +``` + +#### Using environment variables + +You are able to use environment variables from your pods in your pre and post hook commands by including a shell command before using the environment variable. For example, `MYSQL_ROOT_PASSWORD` is an environment variable defined in pod called `mysql`. To use `MYSQL_ROOT_PASSWORD` in your pre-hook, you'd include a shell, like `/bin/sh`, before calling your environment variable: + +``` +pre: +- exec: + container: mysql + command: + - /bin/sh + - -c + - mysql --password=$MYSQL_ROOT_PASSWORD -e "FLUSH TABLES WITH READ LOCK" + onError: Fail +``` + +Note that the container must support the shell command you use. + + +[1]: api-types/backup.md +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/examples/nginx-app/with-pv.yaml +[3]: cloud-common.md diff --git a/site/content/docs/v1.9/backup-reference.md b/site/content/docs/v1.9/backup-reference.md new file mode 100644 index 0000000000..b0eddab293 --- /dev/null +++ b/site/content/docs/v1.9/backup-reference.md @@ -0,0 +1,91 @@ +--- +title: "Backup Reference" +layout: docs +--- + +## Exclude Specific Items from Backup + +It is possible to exclude individual items from being backed up, even if they match the resource/namespace/label selectors defined in the backup spec. To do this, label the item as follows: + +```bash +kubectl label -n / velero.io/exclude-from-backup=true +``` + +## Specify Backup Orders of Resources of Specific Kind + +To backup resources of specific Kind in a specific order, use option --ordered-resources to specify a mapping Kinds to an ordered list of specific resources of that Kind. Resource names are separated by commas and their names are in format 'namespace/resourcename'. For cluster scope resource, simply use resource name. Key-value pairs in the mapping are separated by semi-colon. Kind name is in plural form. + +```bash +velero backup create backupName --include-cluster-resources=true --ordered-resources 'pods=ns1/pod1,ns1/pod2;persistentvolumes=pv4,pv8' --include-namespaces=ns1 +velero backup create backupName --ordered-resources 'statefulsets=ns1/sts1,ns1/sts0' --include-namespaces=ns1 +``` +## Schedule a Backup + +The **schedule** operation allows you to create a backup of your data at a specified time, defined by a [Cron expression](https://en.wikipedia.org/wiki/Cron). + +``` +velero schedule create NAME --schedule="* * * * *" [flags] +``` + +Cron schedules use the following format. + +``` +# ┌───────────── minute (0 - 59) +# │ ┌───────────── hour (0 - 23) +# │ │ ┌───────────── day of the month (1 - 31) +# │ │ │ ┌───────────── month (1 - 12) +# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; +# │ │ │ │ │ 7 is also Sunday on some systems) +# │ │ │ │ │ +# │ │ │ │ │ +# * * * * * +``` + +For example, the command below creates a backup that runs every day at 3am. + +``` +velero schedule create example-schedule --schedule="0 3 * * *" +``` + +This command will create the backup, `example-schedule`, within Velero, but the backup will not be taken until the next scheduled time, 3am. Backups created by a schedule are saved with the name `-`, where `` is formatted as *YYYYMMDDhhmmss*. For a full list of available configuration flags use the Velero CLI help command. + +``` +velero schedule create --help +``` + +Once you create the scheduled backup, you can then trigger it manually using the `velero backup` command. + +``` +velero backup create --from-schedule example-schedule +``` + +This command will immediately trigger a new backup based on your template for `example-schedule`. This will not affect the backup schedule, and another backup will trigger at the scheduled time. + + +### Limitation +Backups created from schedule can have owner reference to the schedule. This can be achieved by command: + +``` +velero schedule create --use-owner-references-in-backup +``` +By this way, schedule is the owner of it created backups. This is useful for some GitOps scenarios, or the resource tree of k8s synchronized from other places. + +Please do notice there is also side effect that may not be expected. Because schedule is the owner, when the schedule is deleted, the related backups CR (Just backup CR is deleted. Backup data still exists in object store and snapshots) will be deleted by k8s GC controller, too, but Velero controller will sync these backups from object store's metadata into k8s. Then k8s GC controller and Velero controller will fight over whether these backups should exist all through. + +If there is possibility the schedule will be disable to not create backup anymore, and the created backups are still useful. Please do not enable this option. For detail, please reference to [Backups created by a schedule with useOwnerReferenceInBackup set do not get synced properly](https://github.com/vmware-tanzu/velero/issues/4093). + + +## Kubernetes API Pagination + +By default, Velero will paginate the LIST API call for each resource type in the Kubernetes API when collecting items into a backup. The `--client-page-size` flag for the Velero server configures the size of each page. + +Depending on the cluster's scale, tuning the page size can improve backup performance. You can experiment with higher values, noting their impact on the relevant `apiserver_request_duration_seconds_*` metrics from the Kubernetes apiserver. + +Pagination can be entirely disabled by setting `--client-page-size` to `0`. This will request all items in a single unpaginated LIST call. + +## Deleting Backups + +Use the following commands to delete Velero backups and data: + +* `kubectl delete backup -n ` will delete the backup custom resource only and will not delete any associated data from object/block storage +* `velero backup delete ` will delete the backup resource including all data in object/block storage diff --git a/site/content/docs/v1.9/basic-install.md b/site/content/docs/v1.9/basic-install.md new file mode 100644 index 0000000000..080b27b2f0 --- /dev/null +++ b/site/content/docs/v1.9/basic-install.md @@ -0,0 +1,73 @@ +--- +title: "Basic Install" +layout: docs +--- + +Use this doc to get a basic installation of Velero. +Refer [this document](customize-installation.md) to customize your installation. + +## Prerequisites + +- Access to a Kubernetes cluster, v1.16 or later, with DNS and container networking enabled. For more information on supported Kubernetes versions, see the Velero [compatibility matrix](https://github.com/vmware-tanzu/velero#velero-compatabilty-matrix). +- `kubectl` installed locally + +Velero uses object storage to store backups and associated artifacts. It also optionally integrates with supported block storage systems to snapshot your persistent volumes. Before beginning the installation process, you should identify the object storage provider and optional block storage provider(s) you'll be using from the list of [compatible providers][0]. + +Velero supports storage providers for both cloud-provider environments and on-premises environments. For more details on on-premises scenarios, see the [on-premises documentation][2]. + +### Velero on Windows + +Velero does not officially support Windows. In testing, the Velero team was able to backup stateless Windows applications only. The restic integration and backups of stateful applications or PersistentVolumes were not supported. + +If you want to perform your own testing of Velero on Windows, you must deploy Velero as a Windows container. Velero does not provide official Windows images, but its possible for you to build your own Velero Windows container image to use. Note that you must build this image on a Windows node. + +## Install the CLI + +### Option 1: MacOS - Homebrew + +On macOS, you can use [Homebrew](https://brew.sh) to install the `velero` client: + +```bash +brew install velero +``` + +### Option 2: GitHub release + +1. Download the [latest release][1]'s tarball for your client platform. +1. Extract the tarball: + + ```bash + tar -xvf .tar.gz + ``` + +1. Move the extracted `velero` binary to somewhere in your `$PATH` (`/usr/local/bin` for most users). + +### Option 3: Windows - Chocolatey + +On Windows, you can use [Chocolatey](https://chocolatey.org/install) to install the [velero](https://chocolatey.org/packages/velero) client: + +```powershell +choco install velero +``` + +## Install and configure the server components + +There are two supported methods for installing the Velero server components: + +- the `velero install` CLI command +- the [Helm chart](https://vmware-tanzu.github.io/helm-charts/) + +Velero uses storage provider plugins to integrate with a variety of storage systems to support backup and snapshot operations. The steps to install and configure the Velero server components along with the appropriate plugins are specific to your chosen storage provider. To find installation instructions for your chosen storage provider, follow the documentation link for your provider at our [supported storage providers][0] page + +_Note: if your object storage provider is different than your volume snapshot provider, follow the installation instructions for your object storage provider first, then return here and follow the instructions to [add your volume snapshot provider][4]._ + +## Command line Autocompletion + +Please refer to [this part of the documentation][5]. + +[0]: supported-providers.md +[1]: https://github.com/vmware-tanzu/velero/releases/latest +[2]: on-premises.md +[3]: overview-plugins.md +[4]: customize-installation.md#install-an-additional-volume-snapshot-provider +[5]: customize-installation.md#optional-velero-cli-configurations diff --git a/site/content/docs/v1.9/build-from-source.md b/site/content/docs/v1.9/build-from-source.md new file mode 100644 index 0000000000..df9f738cf2 --- /dev/null +++ b/site/content/docs/v1.9/build-from-source.md @@ -0,0 +1,200 @@ +--- +title: "Build from source" +layout: docs +--- + +## Prerequisites + +* Access to a Kubernetes cluster, version 1.7 or later. +* A DNS server on the cluster +* `kubectl` installed +* [Go][5] installed (minimum version 1.8) + +## Get the source + +### Option 1) Get latest (recommended) + +```bash +mkdir $HOME/go +export GOPATH=$HOME/go +go get github.com/vmware-tanzu/velero +``` + +Where `go` is your [import path][4] for Go. + +For Go development, it is recommended to add the Go import path (`$HOME/go` in this example) to your path. + +### Option 2) Release archive + +Download the archive named `Source code` from the [release page][22] and extract it in your Go import path as `src/github.com/vmware-tanzu/velero`. + +Note that the Makefile targets assume building from a git repository. When building from an archive, you will be limited to the `go build` commands described below. + +## Build + +There are a number of different ways to build `velero` depending on your needs. This section outlines the main possibilities. + +When building by using `make`, it will place the binaries under `_output/bin/$GOOS/$GOARCH`. For example, you will find the binary for darwin here: `_output/bin/darwin/amd64/velero`, and the binary for linux here: `_output/bin/linux/amd64/velero`. `make` will also splice version and git commit information in so that `velero version` displays proper output. + +Note: `velero install` will also use the version information to determine which tagged image to deploy. If you would like to overwrite what image gets deployed, use the `image` flag (see below for instructions on how to build images). + +### Build the binary + +To build the `velero` binary on your local machine, compiled for your OS and architecture, run one of these two commands: + +```bash +go build ./cmd/velero +``` + +```bash +make local +``` + +### Cross compiling + +To build the velero binary targeting linux/amd64 within a build container on your local machine, run: + +```bash +make build +``` + +For any specific platform, run `make build--`. + +For example, to build for the Mac, run `make build-darwin-amd64`. + +Velero's `Makefile` has a convenience target, `all-build`, that builds the following platforms: + +* linux-amd64 +* linux-arm +* linux-arm64 +* linux-ppc64le +* darwin-amd64 +* windows-amd64 + +## Making images and updating Velero + +If after installing Velero you would like to change the image used by its deployment to one that contains your code changes, you may do so by updating the image: + +```bash +kubectl -n velero set image deploy/velero velero=myimagerepo/velero:$VERSION +``` + +To build a Velero container image, you need to configure `buildx` first. + +### Buildx + +Docker Buildx is a CLI plugin that extends the docker command with the full support of the features provided by Moby BuildKit builder toolkit. It provides the same user experience as docker build with many new features like creating scoped builder instances and building against multiple nodes concurrently. + +More information in the [docker docs][23] and in the [buildx github][24] repo. + +### Image building + +Set the `$REGISTRY` environment variable. For example, if you want to build the `gcr.io/my-registry/velero:main` image, set `$REGISTRY` to `gcr.io/my-registry`. If this variable is not set, the default is `velero`. + +Optionally, set the `$VERSION` environment variable to change the image tag or `$BIN` to change which binary to build a container image for. Then, run: + +```bash +make container +``` +_Note: To build build container images for both `velero` and `velero-restic-restore-helper`, run: `make all-containers`_ + +### Publishing container images to a registry + +To publish container images to a registry, the following one time setup is necessary: + +1. If you are building cross platform container images + ```bash + $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + ``` +1. Create and bootstrap a new docker buildx builder + ```bash + $ docker buildx create --use --name builder + builder + $ docker buildx inspect --bootstrap + [+] Building 2.6s (1/1) FINISHED + => [internal] booting buildkit 2.6s + => => pulling image moby/buildkit:buildx-stable-1 1.9s + => => creating container buildx_buildkit_builder0 0.7s + Name: builder + Driver: docker-container + + Nodes: + Name: builder0 + Endpoint: unix:///var/run/docker.sock + Status: running + Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6 + ``` + NOTE: Without the above setup, the output of `docker buildx inspect --bootstrap` will be: + ```bash + $ docker buildx inspect --bootstrap + Name: default + Driver: docker + + Nodes: + Name: default + Endpoint: default + Status: running + Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6 + ``` + And the `REGISTRY=myrepo BUILDX_OUTPUT_TYPE=registry make container` will fail with the below error: + ```bash + $ REGISTRY=ashishamarnath BUILDX_PLATFORMS=linux/arm64 BUILDX_OUTPUT_TYPE=registry make container + auto-push is currently not implemented for docker driver + make: *** [container] Error 1 + ``` + +Having completed the above one time setup, now the output of `docker buildx inspect --bootstrap` should be like + +```bash +$ docker buildx inspect --bootstrap +Name: builder +Driver: docker-container + +Nodes: +Name: builder0 +Endpoint: unix:///var/run/docker.sock +Status: running +Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v +``` + +Now build and push the container image by running the `make container` command with `$BUILDX_OUTPUT_TYPE` set to `registry` +```bash +$ REGISTRY=myrepo BUILDX_OUTPUT_TYPE=registry make container +``` + +### Cross platform building + +Docker `buildx` platforms supported: +* `linux/amd64` +* `linux/arm64` +* `linux/arm/v7` +* `linux/ppc64le` + +For any specific platform, run `BUILDX_PLATFORMS=/ make container` + +For example, to build an image for arm64, run: + +```bash +BUILDX_PLATFORMS=linux/arm64 make container +``` +_Note: By default, `$BUILDX_PLATFORMS` is set to `linux/amd64`_ + +With `buildx`, you can also build all supported platforms at the same time and push a multi-arch image to the registry. For example: + +```bash +REGISTRY=myrepo VERSION=foo BUILDX_PLATFORMS=linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le BUILDX_OUTPUT_TYPE=registry make all-containers +``` +_Note: when building for more than 1 platform at the same time, you need to set `BUILDX_OUTPUT_TYPE` to `registry` as local multi-arch images are not supported [yet][25]._ + +Note: if you want to update the image but not change its name, you will have to trigger Kubernetes to pick up the new image. One way of doing so is by deleting the Velero deployment pod: + +```bash +kubectl -n velero delete pods -l deploy=velero +``` + +[4]: https://blog.golang.org/organizing-go-code +[5]: https://golang.org/doc/install +[22]: https://github.com/vmware-tanzu/velero/releases +[23]: https://docs.docker.com/buildx/working-with-buildx/ +[24]: https://github.com/docker/buildx +[25]: https://github.com/moby/moby/pull/38738 diff --git a/site/content/docs/v1.9/code-standards.md b/site/content/docs/v1.9/code-standards.md new file mode 100644 index 0000000000..665116d06b --- /dev/null +++ b/site/content/docs/v1.9/code-standards.md @@ -0,0 +1,151 @@ +--- +title: "Code Standards" +layout: docs +toc: "true" +--- + +## Opening PRs + +When opening a pull request, please fill out the checklist supplied the template. This will help others properly categorize and review your pull request. + +## Adding a changelog + +Authors are expected to include a changelog file with their pull requests. The changelog file +should be a new file created in the `changelogs/unreleased` folder. The file should follow the +naming convention of `pr-username` and the contents of the file should be your text for the +changelog. + + velero/changelogs/unreleased <- folder + 000-username <- file + +Add that to the PR. + +If a PR does not warrant a changelog, the CI check for a changelog can be skipped by applying a `changelog-not-required` label on the PR. If you are making a PR on a release branch, you should still make a new file in the `changelogs/unreleased` folder on the release branch for your change. + +## Copyright header + +Whenever a source code file is being modified, the copyright notice should be updated to our standard copyright notice. That is, it should read “Copyright the Velero contributors.” + +For new files, the entire copyright and license header must be added. + +Please note that doc files do not need a copyright header. + +## Code + +- Log messages are capitalized. + +- Error messages are kept lower-cased. + +- Wrap/add a stack only to errors that are being directly returned from non-velero code, such as an API call to the Kubernetes server. + + ```bash + errors.WithStack(err) + ``` + +- Prefer to use the utilities in the Kubernetes package [`sets`](https://godoc.org/github.com/kubernetes/apimachinery/pkg/util/sets). + + ```bash + k8s.io/apimachinery/pkg/util/sets + ``` + +## Imports + +For imports, we use the following convention: + +`` + +Example: + + import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + corev1listers "k8s.io/client-go/listers/core/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" + ) + +## Mocks + +We use a package to generate mocks for our interfaces. + +Example: if you want to change this mock: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/restic/mocks/restorer.go + +Run: + +```bash +go get github.com/vektra/mockery/.../ +cd pkg/restic +mockery -name=Restorer +``` + +Might need to run `make update` to update the imports. + +## Kubernetes Labels + +When generating label values, be sure to pass them through the `label.GetValidName()` helper function. + +This will help ensure that the values are the proper length and format to be stored and queried. + +In general, UIDs are safe to persist as label values. + +This function is not relevant to annotation values, which do not have restrictions. + +## DCO Sign off + +All authors to the project retain copyright to their work. However, to ensure +that they are only submitting work that they have rights to, we are requiring +everyone to acknowledge this by signing their work. + +Any copyright notices in this repo should specify the authors as "the Velero contributors". + +To sign your work, just add a line like this at the end of your commit message: + +``` +Signed-off-by: Joe Beda +``` + +This can easily be done with the `--signoff` option to `git commit`. + +By doing this you state that you can certify the following (from https://developercertificate.org/): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/site/content/docs/v1.9/contributions/ibm-config.md b/site/content/docs/v1.9/contributions/ibm-config.md new file mode 100644 index 0000000000..b551bc1a73 --- /dev/null +++ b/site/content/docs/v1.9/contributions/ibm-config.md @@ -0,0 +1,101 @@ +--- +title: "Use IBM Cloud Object Storage as Velero's storage destination." +layout: docs +--- +You can deploy Velero on IBM [Public][5] or [Private][4] clouds, or even on any other Kubernetes cluster, but anyway you can use IBM Cloud Object Store as a destination for Velero's backups. + +To set up IBM Cloud Object Storage (COS) as Velero's destination, you: + +* Download an official release of Velero +* Create your COS instance +* Create an S3 bucket +* Define a service that can store data in the bucket +* Configure and start the Velero server + +## Download Velero + +1. Download the [latest official release's](https://github.com/vmware-tanzu/velero/releases) tarball for your client platform. + + _We strongly recommend that you use an [official release](https://github.com/vmware-tanzu/velero/releases) of +Velero. The tarballs for each release contain the `velero` command-line client. The code in the main branch +of the Velero repository is under active development and is not guaranteed to be stable!_ + +1. Extract the tarball: + + ```bash + tar -xvf .tar.gz -C /dir/to/extract/to + ``` + + The directory you extracted is called the "Velero directory" in subsequent steps. + +1. Move the `velero` binary from the Velero directory to somewhere in your PATH. + +## Create COS instance +If you don’t have a COS instance, you can create a new one, according to the detailed instructions in [Creating a new resource instance][1]. + +## Create an S3 bucket +Velero requires an object storage bucket to store backups in. See instructions in [Create some buckets to store your data][2]. + +## Define a service that can store data in the bucket. +The process of creating service credentials is described in [Service credentials][3]. +Several comments: + +1. The Velero service will write its backup into the bucket, so it requires the “Writer” access role. + +2. Velero uses an AWS S3 compatible API. Which means it authenticates using a signature created from a pair of access and secret keys — a set of HMAC credentials. You can create these HMAC credentials by specifying `{“HMAC”:true}` as an optional inline parameter. See [HMAC credentials][31] guide. + +3. After successfully creating a Service credential, you can view the JSON definition of the credential. Under the `cos_hmac_keys` entry there are `access_key_id` and `secret_access_key`. Use them in the next step. + +4. Create a Velero-specific credentials file (`credentials-velero`) in your local directory: + + ``` + [default] + aws_access_key_id= + aws_secret_access_key= + ``` + + Where the access key id and secret are the values that you got above. + +## Install and start Velero + +Install Velero, including all prerequisites, into the cluster and start the deployment. This will create a namespace called `velero`, and place a deployment named `velero` in it. + +```bash +velero install \ + --provider aws \ + --bucket \ + --secret-file ./credentials-velero \ + --use-volume-snapshots=false \ + --backup-location-config region=,s3ForcePathStyle="true",s3Url= +``` + +Velero does not have a volume snapshot plugin for IBM Cloud, so creating volume snapshots is disabled. + +Additionally, you can specify `--use-restic` to enable [restic support][16], and `--wait` to wait for the deployment to be ready. + +(Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/restic pods. + +Once the installation is complete, remove the default `VolumeSnapshotLocation` that was created by `velero install`, since it's specific to AWS and won't work for IBM Cloud: + +```bash +kubectl -n velero delete volumesnapshotlocation.velero.io default +``` + +For more complex installation needs, use either the Helm chart, or add `--dry-run -o yaml` options for generating the YAML representation for the installation. + +## Installing the nginx example (optional) + +If you run the nginx example, in file `examples/nginx-app/with-pv.yaml`: + +Uncomment `storageClassName: ` and replace with your `StorageClass` name. + +[0]: ../namespace.md +[1]: https://cloud.ibm.com/docs/cloud-object-storage/getting-started.html +[2]: https://cloud.ibm.com/docs/cloud-object-storage/getting-started.html#create-buckets +[3]: https://cloud.ibm.com/docs/cloud-object-storage/iam?topic=cloud-object-storage-service-credentials +[31]: https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-uhc-hmac-credentials-main +[4]: https://www.ibm.com/docs/en/cloud-private +[5]: https://cloud.ibm.com/docs/containers/container_index.html#container_index +[14]: http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html +[15]: ../customize-installation.md#customize-resource-requests-and-limits +[16]: ../restic.md diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/15ccaacf00640a04ae29ceed4c86195b.png b/site/content/docs/v1.9/contributions/img-for-tencent/15ccaacf00640a04ae29ceed4c86195b.png new file mode 100644 index 0000000000000000000000000000000000000000..61859ca50e9fbafb2948628d79b7e0bf69bd85de GIT binary patch literal 132711 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

    IZ`LFqwfbe$eQ6t@&18)&3r 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 literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/1d53b0115644d43657c2a5ece805c9b4.png b/site/content/docs/v1.9/contributions/img-for-tencent/1d53b0115644d43657c2a5ece805c9b4.png new file mode 100644 index 0000000000000000000000000000000000000000..57de0cde0607257ac55de3ef30d834db31709587 GIT binary patch literal 74478 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 aL$88LY}It)7mk;}9|c(znS7~dKK~Cm_kk?{ literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/69194157ccd5e377d1e7d914fd8c0336.png b/site/content/docs/v1.9/contributions/img-for-tencent/69194157ccd5e377d1e7d914fd8c0336.png new file mode 100644 index 0000000000000000000000000000000000000000..f363eb1a78073b9a07ed91b046293d5f7224a7c8 GIT binary patch literal 32817 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∓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 literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/ceaca9ce6bc92bdce987c63d2fe71561.png b/site/content/docs/v1.9/contributions/img-for-tencent/ceaca9ce6bc92bdce987c63d2fe71561.png new file mode 100644 index 0000000000000000000000000000000000000000..36805831bbe57b7118c9bc47bed1a7a7306c4f2c GIT binary patch literal 292269 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

  1. bHDT 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%0D84wI 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 !1-`yjuGc@{h+{c5~#C8bSt>zP{ra{|Pn| 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

    ` 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π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 CkT-R3B0ucAz=veF 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 XSX6k&spaN3z>m7J&Z8nli?{y=vTPGg literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/effe8a0a7ce3aa8e422db00bfdddc375.png b/site/content/docs/v1.9/contributions/img-for-tencent/effe8a0a7ce3aa8e422db00bfdddc375.png new file mode 100644 index 0000000000000000000000000000000000000000..28e1bbaad3ae0635df7e394f48b1a1836754d514 GIT binary patch literal 85131 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%0AmC`-4_;Qkg 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 E06hp8s{jB1 literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/contributions/img-for-tencent/f0fff5228527edc72d6e71a50d5dc966.png b/site/content/docs/v1.9/contributions/img-for-tencent/f0fff5228527edc72d6e71a50d5dc966.png new file mode 100644 index 0000000000000000000000000000000000000000..b732cf25281e349ac940bafcad1d7d5cdbb7a915 GIT binary patch literal 49558 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 PfFA`JRp}B*1Hb.tar.gz -C /dir/to/extract/to + ``` + + The directory you extracted is called the "Velero directory" in subsequent steps. + +1. Move the `velero` binary from the Velero directory to somewhere in your PATH. + +## Set up server + +These instructions start the Velero server and a Minio instance that is accessible from within the cluster only. See [Expose Minio outside your cluster](#expose-minio-outside-your-cluster-with-a-service) for information about configuring your cluster for outside access to Minio. Outside access is required to access logs and run `velero describe` commands. + +1. Create a Velero-specific credentials file (`credentials-velero`) in your Velero directory: + + ``` + [default] + aws_access_key_id = minio + aws_secret_access_key = minio123 + ``` + +1. Start the server and the local storage service. In the Velero directory, run: + + ``` + kubectl apply -f examples/minio/00-minio-deployment.yaml + ``` + _Note_: The example Minio yaml provided uses "empty dir". Your node needs to have enough space available to store the + data being backed up plus 1GB of free space. If the node does not have enough space, you can modify the example yaml to + use a Persistent Volume instead of "empty dir" + + ``` + velero install \ + --provider aws \ + --plugins velero/velero-plugin-for-aws:v1.2.1 \ + --bucket velero \ + --secret-file ./credentials-velero \ + --use-volume-snapshots=false \ + --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio.velero.svc:9000 + ``` + + This example assumes that it is running within a local cluster without a volume provider capable of snapshots, so no `VolumeSnapshotLocation` is created (`--use-volume-snapshots=false`). You may need to update AWS plugin version to one that is [compatible](https://github.com/vmware-tanzu/velero-plugin-for-aws#compatibility) with the version of Velero you are installing. + + Additionally, you can specify `--use-restic` to enable restic support, and `--wait` to wait for the deployment to be ready. + + This example also assumes you have named your Minio bucket "velero". + + +1. Deploy the example nginx application: + + ```bash + kubectl apply -f examples/nginx-app/base.yaml + ``` + +1. Check to see that both the Velero and nginx deployments are successfully created: + + ``` + kubectl get deployments -l component=velero --namespace=velero + kubectl get deployments --namespace=nginx-example + ``` + +## Back up + +1. Create a backup for any object that matches the `app=nginx` label selector: + + ``` + velero backup create nginx-backup --selector app=nginx + ``` + + Alternatively if you want to backup all objects *except* those matching the label `backup=ignore`: + + ``` + velero backup create nginx-backup --selector 'backup notin (ignore)' + ``` + +1. (Optional) Create regularly scheduled backups based on a cron expression using the `app=nginx` label selector: + + ``` + velero schedule create nginx-daily --schedule="0 1 * * *" --selector app=nginx + ``` + + Alternatively, you can use some non-standard shorthand cron expressions: + + ``` + velero schedule create nginx-daily --schedule="@daily" --selector app=nginx + ``` + + See the [cron package's documentation][30] for more usage examples. + +1. Simulate a disaster: + + ``` + kubectl delete namespace nginx-example + ``` + +1. To check that the nginx deployment and service are gone, run: + + ``` + kubectl get deployments --namespace=nginx-example + kubectl get services --namespace=nginx-example + kubectl get namespace/nginx-example + ``` + + You should get no results. + + NOTE: You might need to wait for a few minutes for the namespace to be fully cleaned up. + +## Restore + +1. Run: + + ``` + velero restore create --from-backup nginx-backup + ``` + +1. Run: + + ``` + velero restore get + ``` + + After the restore finishes, the output looks like the following: + + ``` + NAME BACKUP STATUS WARNINGS ERRORS CREATED SELECTOR + nginx-backup-20170727200524 nginx-backup Completed 0 0 2017-07-27 20:05:24 +0000 UTC + ``` + +NOTE: The restore can take a few moments to finish. During this time, the `STATUS` column reads `InProgress`. + +After a successful restore, the `STATUS` column is `Completed`, and `WARNINGS` and `ERRORS` are 0. All objects in the `nginx-example` namespace should be just as they were before you deleted them. + +If there are errors or warnings, you can look at them in detail: + +``` +velero restore describe +``` + +For more information, see [the debugging information][18]. + +## Clean up + +If you want to delete any backups you created, including data in object storage and persistent +volume snapshots, you can run: + +``` +velero backup delete BACKUP_NAME +``` + +This asks the Velero server to delete all backup data associated with `BACKUP_NAME`. You need to do +this for each backup you want to permanently delete. A future version of Velero will allow you to +delete multiple backups by name or label selector. + +Once fully removed, the backup is no longer visible when you run: + +``` +velero backup get BACKUP_NAME +``` + +To completely uninstall Velero, minio, and the nginx example app from your Kubernetes cluster: + +``` +kubectl delete namespace/velero clusterrolebinding/velero +kubectl delete crds -l component=velero +kubectl delete -f examples/nginx-app/base.yaml +``` + +## Expose Minio outside your cluster with a Service + +When you run commands to get logs or describe a backup, the Velero server generates a pre-signed URL to download the requested items. To access these URLs from outside the cluster -- that is, from your Velero client -- you need to make Minio available outside the cluster. You can: + +- Change the Minio Service type from `ClusterIP` to `NodePort`. +- Set up Ingress for your cluster, keeping Minio Service type `ClusterIP`. + +You can also specify a `publicUrl` config field for the pre-signed URL in your backup storage location config. + +### Expose Minio with Service of type NodePort + +The Minio deployment by default specifies a Service of type `ClusterIP`. You can change this to `NodePort` to easily expose a cluster service externally if you can reach the node from your Velero client. + +You must also get the Minio URL, which you can then specify as the value of the `publicUrl` field in your backup storage location config. + +1. In `examples/minio/00-minio-deployment.yaml`, change the value of Service `spec.type` from `ClusterIP` to `NodePort`. + +1. Get the Minio URL: + + - if you're running Minikube: + + ```shell + minikube service minio --namespace=velero --url + ``` + + - in any other environment: + 1. Get the value of an external IP address or DNS name of any node in your cluster. You must be able to reach this address from the Velero client. + 1. Append the value of the NodePort to get a complete URL. You can get this value by running: + + ```shell + kubectl -n velero get svc/minio -o jsonpath='{.spec.ports[0].nodePort}' + ``` + +1. Edit your `BackupStorageLocation` YAML, adding `publicUrl: ` as a field under `spec.config`. You must include the `http://` or `https://` prefix. + +## Accessing logs with an HTTPS endpoint + +If you're using Minio with HTTPS, you may see unintelligible text in the output of `velero describe`, or `velero logs` commands. + +To fix this, you can add a public URL to the `BackupStorageLocation`. + +In a terminal, run the following: + +```shell +kubectl patch -n velero backupstoragelocation default --type merge -p '{"spec":{"config":{"publicUrl":"https://:9000"}}}' +``` + +If your certificate is self-signed, see the [documentation on self-signed certificates][32]. + +## Expose Minio outside your cluster with Kubernetes in Docker (KinD): + +Kubernetes in Docker does not have support for NodePort services (see [this issue](https://github.com/kubernetes-sigs/kind/issues/99)). In this case, you can use a port forward to access the Minio bucket. + +In a terminal, run the following: + +```shell +MINIO_POD=$(kubectl get pods -n velero -l component=minio -o jsonpath='{.items[0].metadata.name}') + +kubectl port-forward $MINIO_POD -n velero 9000:9000 +``` + +Then, in another terminal: + +```shell +kubectl edit backupstoragelocation default -n velero +``` + +Add `publicUrl: http://localhost:9000` under the `spec.config` section. + + +### Work with Ingress + +Configuring Ingress for your cluster is out of scope for the Velero documentation. If you have already set up Ingress, however, it makes sense to continue with it while you run the example Velero configuration with Minio. + +In this case: + +1. Keep the Service type as `ClusterIP`. + +1. Edit your `BackupStorageLocation` YAML, adding `publicUrl: ` as a field under `spec.config`. + +[1]: #expose-minio-with-service-of-type-nodeport +[3]: ../customize-installation.md +[17]: ../restic.md +[18]: ../debugging-restores.md +[26]: https://github.com/vmware-tanzu/velero/releases +[30]: https://godoc.org/github.com/robfig/cron +[32]: ../self-signed-certificates.md diff --git a/site/content/docs/v1.9/contributions/oracle-config.md b/site/content/docs/v1.9/contributions/oracle-config.md new file mode 100644 index 0000000000..558e6232a4 --- /dev/null +++ b/site/content/docs/v1.9/contributions/oracle-config.md @@ -0,0 +1,248 @@ +--- +title: "Use Oracle Cloud as a Backup Storage Provider for Velero" +layout: docs +--- + +## Introduction + +[Velero](https://velero.io/) is a tool used to backup and migrate Kubernetes applications. Here are the steps to use [Oracle Cloud Object Storage](https://docs.cloud.oracle.com/iaas/Content/Object/Concepts/objectstorageoverview.htm) as a destination for Velero backups. + +1. [Download Velero](#download-velero) +2. [Create A Customer Secret Key](#create-a-customer-secret-key) +3. [Create An Oracle Object Storage Bucket](#create-an-oracle-object-storage-bucket) +4. [Install Velero](#install-velero) +5. [Clean Up](#clean-up) +6. [Examples](#examples) +7. [Additional Reading](#additional-reading) + +## Download Velero + +1. Download the [latest release](https://github.com/vmware-tanzu/velero/releases/) of Velero to your development environment. This includes the `velero` CLI utility and example Kubernetes manifest files. For example: + + ``` + wget https://github.com/vmware-tanzu/velero/releases/download/v1.0.0/velero-v1.0.0-linux-amd64.tar.gz + ``` + + **NOTE:** Its strongly recommend that you use an official release of Velero. The tarballs for each release contain the velero command-line client. The code in the main branch of the Velero repository is under active development and is not guaranteed to be stable! + +2. Untar the release in your `/usr/bin` directory: `tar -xzvf .tar.gz` + + You may choose to rename the directory `velero` for the sake of simplicity: `mv velero-v1.0.0-linux-amd64 velero` + +3. Add it to your PATH: `export PATH=/usr/local/bin/velero:$PATH` + +4. Run `velero` to confirm the CLI has been installed correctly. You should see an output like this: + +``` +$ velero +Velero is a tool for managing disaster recovery, specifically for Kubernetes +cluster resources. It provides a simple, configurable, and operationally robust +way to back up your application state and associated data. + +If you're familiar with kubectl, Velero supports a similar model, allowing you to +execute commands such as 'velero get backup' and 'velero create schedule'. The same +operations can also be performed as 'velero backup get' and 'velero schedule create'. + +Usage: + velero [command] +``` + + + +## Create A Customer Secret Key + +1. Oracle Object Storage provides an API to enable interoperability with Amazon S3. To use this Amazon S3 Compatibility API, you need to generate the signing key required to authenticate with Amazon S3. This special signing key is an Access Key/Secret Key pair. Follow these steps to [create a Customer Secret Key](https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/managingcredentials.htm#To4). Refer to this link for more information about [Working with Customer Secret Keys](https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/managingcredentials.htm#s3). + +2. Create a Velero credentials file with your Customer Secret Key: + + ``` + $ vi credentials-velero + + [default] + aws_access_key_id=bae031188893d1eb83719648790ac850b76c9441 + aws_secret_access_key=MmY9heKrWiNVCSZQ2Mf5XTJ6Ys93Bw2d2D6NMSTXZlk= + ``` + + + +## Create An Oracle Object Storage Bucket + +Create an Oracle Cloud Object Storage bucket called `velero` in the root compartment of your Oracle Cloud tenancy. Refer to this page for [more information about creating a bucket with Object Storage](https://docs.cloud.oracle.com/iaas/Content/Object/Tasks/managingbuckets.htm#usingconsole). + + + +## Install Velero + +You will need the following information to install Velero into your Kubernetes cluster with Oracle Object Storage as the Backup Storage provider: + +``` +velero install \ + --provider [provider name] \ + --bucket [bucket name] \ + --prefix [tenancy name] \ + --use-volume-snapshots=false \ + --secret-file [secret file location] \ + --backup-location-config region=[region],s3ForcePathStyle="true",s3Url=[storage API endpoint] +``` + +- `--provider` This example uses the S3-compatible API, so use `aws` as the provider. +- `--bucket` The name of the bucket created in Oracle Object Storage - in our case this is named `velero`. +- ` --prefix` The name of your Oracle Cloud tenancy - in our case this is named `oracle-cloudnative`. +- `--use-volume-snapshots=false` Velero does not have a volume snapshot plugin for Oracle Cloud, so creating volume snapshots is disabled. +- `--secret-file` The path to your `credentials-velero` file. +- `--backup-location-config` The path to your Oracle Object Storage bucket. This consists of your `region` which corresponds to your Oracle Cloud region name ([List of Oracle Cloud Regions](https://docs.cloud.oracle.com/iaas/Content/General/Concepts/regions.htm?Highlight=regions)) and the `s3Url`, the S3-compatible API endpoint for Oracle Object Storage based on your region: `https://oracle-cloudnative.compat.objectstorage.[region name].oraclecloud.com` + +For example: + +``` +velero install \ + --provider aws \ + --bucket velero \ + --prefix oracle-cloudnative \ + --use-volume-snapshots=false \ + --secret-file /Users/mboxell/bin/velero/credentials-velero \ + --backup-location-config region=us-phoenix-1,s3ForcePathStyle="true",s3Url=https://oracle-cloudnative.compat.objectstorage.us-phoenix-1.oraclecloud.com +``` + +This will create a `velero` namespace in your cluster along with a number of CRDs, a ClusterRoleBinding, ServiceAccount, Secret, and Deployment for Velero. If your pod fails to successfully provision, you can troubleshoot your installation by running: `kubectl logs [velero pod name]`. + + + +## Clean Up + +To remove Velero from your environment, delete the namespace, ClusterRoleBinding, ServiceAccount, Secret, and Deployment and delete the CRDs, run: + +``` +kubectl delete namespace/velero clusterrolebinding/velero +kubectl delete crds -l component=velero +``` + +This will remove all resources created by `velero install`. + + + +## Examples + +After creating the Velero server in your cluster, try this example: + +### Basic example (without PersistentVolumes) + +1. Start the sample nginx app: `kubectl apply -f examples/nginx-app/base.yaml` + + This will create an `nginx-example` namespace with a `nginx-deployment` deployment, and `my-nginx` service. + + ``` + $ kubectl apply -f examples/nginx-app/base.yaml + namespace/nginx-example created + deployment.apps/nginx-deployment created + service/my-nginx created + ``` + + You can see the created resources by running `kubectl get all` + + ``` + $ kubectl get all + NAME READY STATUS RESTARTS AGE + pod/nginx-deployment-67594d6bf6-4296p 1/1 Running 0 20s + pod/nginx-deployment-67594d6bf6-f9r5s 1/1 Running 0 20s + + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + service/my-nginx LoadBalancer 10.96.69.166 80:31859/TCP 21s + + NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE + deployment.apps/nginx-deployment 2 2 2 2 21s + + NAME DESIRED CURRENT READY AGE + replicaset.apps/nginx-deployment-67594d6bf6 2 2 2 21s + ``` + +2. Create a backup: `velero backup create nginx-backup --include-namespaces nginx-example` + + ``` + $ velero backup create nginx-backup --include-namespaces nginx-example + Backup request "nginx-backup" submitted successfully. + Run `velero backup describe nginx-backup` or `velero backup logs nginx-backup` for more details. + ``` + + At this point you can navigate to appropriate bucket, called `velero`, in the Oracle Cloud Object Storage console to see the resources backed up using Velero. + +3. Simulate a disaster by deleting the `nginx-example` namespace: `kubectl delete namespaces nginx-example` + + ``` + $ kubectl delete namespaces nginx-example + namespace "nginx-example" deleted + ``` + + Wait for the namespace to be deleted. To check that the nginx deployment, service, and namespace are gone, run: + + ``` + kubectl get deployments --namespace=nginx-example + kubectl get services --namespace=nginx-example + kubectl get namespace/nginx-example + ``` + + This should return: `No resources found.` + +4. Restore your lost resources: `velero restore create --from-backup nginx-backup` + + ``` + $ velero restore create --from-backup nginx-backup + Restore request "nginx-backup-20190604102710" submitted successfully. + Run `velero restore describe nginx-backup-20190604102710` or `velero restore logs nginx-backup-20190604102710` for more details. + ``` + + Running `kubectl get namespaces` will show that the `nginx-example` namespace has been restored along with its contents. + +5. Run: `velero restore get` to view the list of restored resources. After the restore finishes, the output looks like the following: + + ``` + $ velero restore get + NAME BACKUP STATUS WARNINGS ERRORS CREATED SELECTOR + nginx-backup-20190604104249 nginx-backup Completed 0 0 2019-06-04 10:42:39 -0700 PDT + ``` + + NOTE: The restore can take a few moments to finish. During this time, the `STATUS` column reads `InProgress`. + + After a successful restore, the `STATUS` column shows `Completed`, and `WARNINGS` and `ERRORS` will show `0`. All objects in the `nginx-example` namespace should be just as they were before you deleted them. + + If there are errors or warnings, for instance if the `STATUS` column displays `FAILED` instead of `InProgress`, you can look at them in detail with `velero restore describe ` + + +6. Clean up the environment with `kubectl delete -f examples/nginx-app/base.yaml` + + ``` + $ kubectl delete -f examples/nginx-app/base.yaml + namespace "nginx-example" deleted + deployment.apps "nginx-deployment" deleted + service "my-nginx" deleted + ``` + + If you want to delete any backups you created, including data in object storage, you can run: `velero backup delete BACKUP_NAME` + + ``` + $ velero backup delete nginx-backup + Are you sure you want to continue (Y/N)? Y + Request to delete backup "nginx-backup" submitted successfully. + The backup will be fully deleted after all associated data (disk snapshots, backup files, restores) are removed. + ``` + + This asks the Velero server to delete all backup data associated with `BACKUP_NAME`. You need to do this for each backup you want to permanently delete. A future version of Velero will allow you to delete multiple backups by name or label selector. + + Once fully removed, the backup is no longer visible when you run: `velero backup get BACKUP_NAME` or more generally `velero backup get`: + + ``` + $ velero backup get nginx-backup + An error occurred: backups.velero.io "nginx-backup" not found + ``` + + ``` + $ velero backup get + NAME STATUS CREATED EXPIRES STORAGE LOCATION SELECTOR + ``` + + + +## Additional Reading + +* [Official Velero Documentation](https://velero.io/docs/v1.9/) +* [Oracle Cloud Infrastructure Documentation](https://docs.cloud.oracle.com/) diff --git a/site/content/docs/v1.9/contributions/tencent-config.md b/site/content/docs/v1.9/contributions/tencent-config.md new file mode 100644 index 0000000000..11b0762c0a --- /dev/null +++ b/site/content/docs/v1.9/contributions/tencent-config.md @@ -0,0 +1,168 @@ +--- +title: "Use Tencent Cloud Object Storage as Velero's storage destination." +layout: docs +--- + + +You can deploy Velero on Tencent [TKE](https://cloud.tencent.com/document/product/457), or an other Kubernetes cluster, and use Tencent Cloud Object Store as a destination for Velero’s backups. + + +## Prerequisites + +- Registered [Tencent Cloud Account](https://cloud.tencent.com/register). +- [Tencent Cloud COS](https://console.cloud.tencent.com/cos) service, referred to as COS, has been launched +- A Kubernetes cluster has been created, cluster version v1.16 or later, and the cluster can use DNS and Internet services normally. If you need to create a TKE cluster, refer to the Tencent [create a cluster](https://cloud.tencent.com/document/product/457/32189) documentation. + +## Create a Tencent Cloud COS bucket + +Create an object bucket for Velero to store backups in the Tencent Cloud COS console. For how to create, please refer to Tencent Cloud COS [Create a bucket](https://cloud.tencent.com/document/product/436/13309) usage instructions. + +Set access to the bucket through the object storage console, the bucket needs to be **read** and **written**, so the account is granted data reading, data writing permissions. For how to configure, see the [permission access settings](https://cloud.tencent.com/document/product/436/13315.E5.8D.95.E4.B8.AA.E6.8E.88.E6.9D.83) Tencent user instructions. + +## Get bucket access credentials + +Velero uses an AWS S3-compatible API to access Tencent Cloud COS storage, which requires authentication using a pair of access key IDs and key-created signatures. + +In the S3 API parameter, the "access_key_id" field is the access key ID and the "secret_access_key" field is the key. + +In the [Tencent Cloud Access Management Console](https://console.cloud.tencent.com/cam/capi), Create and acquire Tencent Cloud Keys "SecretId" and "SecretKey" for COS authorized account. **Where the "SecretId" value corresponds to the value of S3 API parameter "access_key_id" field, the "SecretKey" value corresponds to the value of S3 API parameter "secret_access_key" field**. + +Create the credential profile "credentials-velero" required by Velero in the local directory based on the above correspondence: + +```bash +[default] +aws_access_key_id= +aws_secret_access_key= +``` + +## Install Velero Resources + +You need to install the Velero CLI first, see [Install the CLI](https://velero.io/docs/v1.5/basic-install/#install-the-cli) for how to install. + +Follow the Velero installation command below to create velero and restic workloads and other necessary resource objects. + +```bash +velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.1.0 --bucket \ +--secret-file ./credentials-velero \ +--use-restic \ +--default-volumes-to-restic \ +--backup-location-config \ +region=ap-guangzhou,s3ForcePathStyle="true",s3Url=https://cos.ap-guangzhou.myqcloud.com +``` + +Description of the parameters: + +- `--provider`: Declares the type of plug-in provided by "aws". + +- `--plugins`: Use the AWS S3 compatible API plug-in "velero-plugin-for-aws". + +- `--bucket`: The bucket name created at Tencent Cloud COS. + +- `--secret-file`: Access tencent cloud COS access credential file for the "credentials-velero" credential file created above. + +- `--use-restic`: Back up and restore persistent volume data using the open source free backup tool [restic](https://github.com/restic/restic). However, 'hostPath' volumes are not supported, see the [restic limit](https://velero.io/docs/v1.5/restic/#limitations) for details), an integration that complements Velero's backup capabilities and is recommended to be turned on. + +- `--default-volumes-to-restic`: Enable the use of Restic to back up all Pod volumes, provided that the `--use-restic`parameter needs to be turned on. + +- `--backup-location-config`: Back up the bucket access-related configuration: + + `region`: Tencent cloud COS bucket area, for example, if the created region is Guangzhou, the Region parameter value is "ap-guangzhou". + + `s3ForcePathStyle`: Use the S3 file path format. + + `s3Url`: Tencent Cloud COS-compatible S3 API access address,Note that instead of creating a COS bucket for public network access domain name, you must use a format of "https://cos.`region`.myqcloud.com" URL, for example, if the region is Guangzhou, the parameter value is "https://cos.ap-guangzhou.myqcloud.com.". + +There are other installation parameters that can be viewed using `velero install --help`, such as setting `--use-volume-snapshots-false` to close the storage volume data snapshot backup if you do not want to back up the storage volume data. + +After executing the installation commands above, the installation process looks like this: + +{{< figure src="/docs/main/contributions/img-for-tencent/9015313121ed7987558c88081b052574.png" width="100%">}} + +After the installation command is complete, wait for the velero and restic workloads to be ready to see if the configured storage location is available. + +Executing the 'velero backup-location get' command to view the storage location status and display "Available" indicates that access to Tencent Cloud COS is OK, as shown in the following image: + +{{< figure src="/docs/main/contributions/img-for-tencent/69194157ccd5e377d1e7d914fd8c0336.png" width="100%">}} + +At this point, The installation using Tencent Cloud COS as Velero storage location is complete, If you need more installation information about Velero, You can see the official website [Velero documentation](https://velero.io/docs/) . + +## Velero backup and restore example + +In the cluster, use the helm tool to create a minio test service with a persistent volume, and the minio installation method can be found in the [minio installation](https://github.com/minio/charts), in which case can bound a load balancer for the minio service to access the management page using a public address in the browser. + +{{< figure src="/docs/main/contributions/img-for-tencent/f0fff5228527edc72d6e71a50d5dc966.png" width="100%">}} + +Sign in to the minio web management page and upload some image data for the test, as shown below: + +{{< figure src="/docs/main/contributions/img-for-tencent/e932223585c0b19891cc085ad7f438e1.png" width="100%">}} + +With Velero Backup, you can back up all objects in the cluster directly, or filter objects by type, namespace, and/or label. This example uses the following command to back up all resources under the 'default' namespace. + +``` +velero backup create default-backup --include-namespaces +``` + +Use the `velero backup get` command to see if the backup task is complete, and when the backup task status is "Completed," the backup task is completed without any errors, as shown in the following below: + +{{< figure src="/docs/main/contributions/img-for-tencent/eb2bbabae48b188748f5278bedf177f1.png" width="100%">}} + +At this point delete all of MinIO's resources, including its PVC persistence volume, as shown below:: + +{{< figure src="/docs/main/contributions/img-for-tencent/15ccaacf00640a04ae29ceed4c86195b.png" width="100%">}} + +After deleting the MinIO resource, use your backup to restore the deleted MinIO resource, and temporarily update the backup storage location to read-only mode (this prevents the backup object from being created or deleted in the backup storage location during the restore process):: + +```bash +kubectl patch backupstoragelocation default --namespace velero \ + --type merge \ + --patch '{"spec":{"accessMode":"ReadOnly"}}' + +``` + +Modifying access to Velero's storage location is "ReadOnly," as shown in the following image: + +{{< figure src="/docs/main/contributions/img-for-tencent/e8c2ab4e5e31d1370c62fad25059a8a8.png" width="100%">}} + +Now use the backup "default-backup" that Velero just created to create the restore task: + +```bash +velero restore create --from-backup +``` + +You can also use `velero restore get` to see the status of the restore task, and if the restore status is "Completed," the restore task is complete, as shown in the following image: + +{{< figure src="/docs/main/contributions/img-for-tencent/effe8a0a7ce3aa8e422db00bfdddc375.png" width="100%">}} + +When the restore is complete, you can see that the previously deleted minio-related resources have been restored successfully, as shown in the following image: + +{{< figure src="/docs/main/contributions/img-for-tencent/1d53b0115644d43657c2a5ece805c9b4.png" width="100%">}} + +Log in to minio's management page on your browser and you can see that the previously uploaded picture data is still there, indicating that the persistent volume's data was successfully restored, as shown below: + +{{< figure src="/docs/main/contributions/img-for-tencent/ceaca9ce6bc92bdce987c63d2fe71561.png" width="100%">}} + +When the restore is complete, don't forget to restore the backup storage location to read and write mode so that the next backup task can be used successfully: + +```bash +kubectl patch backupstoragelocation default --namespace velero \ + --type merge \ + --patch '{"spec":{"accessMode":"ReadWrite"}}' +``` + + + +## Uninstall Velero Resources + +To uninstall velero resources in a cluster, you can do so using the following command: + +```bash +kubectl delete namespace/velero clusterrolebinding/velero +kubectl delete crds -l component=velero +``` + + + +## Additional Reading + +- [Official Velero Documentation](https://velero.io/docs/) +- [Tencent Cloud Documentation](https://cloud.tencent.com/document/product) diff --git a/site/content/docs/v1.9/csi.md b/site/content/docs/v1.9/csi.md new file mode 100644 index 0000000000..80198c4c97 --- /dev/null +++ b/site/content/docs/v1.9/csi.md @@ -0,0 +1,75 @@ +--- +title: "Container Storage Interface Snapshot Support in Velero" +layout: docs +--- + +Integrating Container Storage Interface (CSI) snapshot support into Velero enables Velero to backup and restore CSI-backed volumes using the [Kubernetes CSI Snapshot APIs](https://kubernetes.io/docs/concepts/storage/volume-snapshots/). + +By supporting CSI snapshot APIs, Velero can support any volume provider that has a CSI driver, without requiring a Velero-specific plugin to be available. This page gives an overview of how to add support for CSI snapshots to Velero through CSI plugins. For more information about specific components, see the [plugin repo](https://github.com/vmware-tanzu/velero-plugin-for-csi/). + +## Prerequisites + + 1. Your cluster is Kubernetes version 1.20 or greater. + 1. Your cluster is running a CSI driver capable of support volume snapshots at the [v1 API level](https://kubernetes.io/blog/2020/12/10/kubernetes-1.20-volume-snapshot-moves-to-ga/). + 1. When restoring CSI VolumeSnapshots across clusters, the name of the CSI driver in the destination cluster is the same as that on the source cluster to ensure cross cluster portability of CSI VolumeSnapshots + +**NOTE:** Not all cloud provider's CSI drivers guarantee snapshot durability, meaning that the VolumeSnapshot and VolumeSnapshotContent objects may be stored in the same object storage system location as the original PersistentVolume and may be vulnerable to data loss. You should refer to your cloud provider's documentation for more information on configuring snapshot durability. Since v0.3.0 the velero team will provide official support for CSI plugin when they are used with AWS and Azure drivers. + +## Installing Velero with CSI support + +To integrate Velero with the CSI volume snapshot APIs, you must enable the `EnableCSI` feature flag and install the Velero [CSI plugins][2] on the Velero server. + +Both of these can be added with the `velero install` command. + +```bash +velero install \ +--features=EnableCSI \ +--plugins=,velero/velero-plugin-for-csi:v0.3.0 \ +... +``` + +To include the status of CSI objects associated with a Velero backup in `velero backup describe` output, run `velero client config set features=EnableCSI`. +See [Enabling Features][1] for more information about managing client-side feature flags. You can also view the image on [Docker Hub][3]. + +## Implementation Choices + +This section documents some of the choices made during implementation of the Velero [CSI plugins][2]: + + 1. VolumeSnapshots created by the Velero CSI plugins are retained only for the lifetime of the backup even if the `DeletionPolicy` on the VolumeSnapshotClass is set to `Retain`. To accomplish this, during deletion of the backup the prior to deleting the VolumeSnapshot, VolumeSnapshotContent object is patched to set its `DeletionPolicy` to `Delete`. Deleting the VolumeSnapshot object will result in cascade delete of the VolumeSnapshotContent and the snapshot in the storage provider. + 1. VolumeSnapshotContent objects created during a `velero backup` that are dangling, unbound to a VolumeSnapshot object, will be discovered, using labels, and deleted on backup deletion. + 1. The Velero CSI plugins, to backup CSI backed PVCs, will choose the VolumeSnapshotClass in the cluster that has the same driver name and also has the `velero.io/csi-volumesnapshot-class` label set on it, like + ```yaml + velero.io/csi-volumesnapshot-class: "true" + ``` + 1. The VolumeSnapshot objects will be removed from the cluster after the backup is uploaded to the object storage, so that the namespace that is backed up can be deleted without removing the snapshot in the storage provider if the `DeletionPolicy` is `Delete. + +## How it Works - Overview + +Velero's CSI support does not rely on the Velero VolumeSnapshotter plugin interface. + +Instead, Velero uses a collection of BackupItemAction plugins that act first against PersistentVolumeClaims. + +When this BackupItemAction sees PersistentVolumeClaims pointing to a PersistentVolume backed by a CSI driver, it will choose the VolumeSnapshotClass with the same driver name that has the `velero.io/csi-volumesnapshot-class` label to create a CSI VolumeSnapshot object with the PersistentVolumeClaim as a source. +This VolumeSnapshot object resides in the same namespace as the PersistentVolumeClaim that was used as a source. + +From there, the CSI external-snapshotter controller will see the VolumeSnapshot and create a VolumeSnapshotContent object, a cluster-scoped resource that will point to the actual, disk-based snapshot in the storage system. +The external-snapshotter plugin will call the CSI driver's snapshot method, and the driver will call the storage system's APIs to generate the snapshot. +Once an ID is generated and the storage system marks the snapshot as usable for restore, the VolumeSnapshotContent object will be updated with a `status.snapshotHandle` and the `status.readyToUse` field will be set. + +Velero will include the generated VolumeSnapshot and VolumeSnapshotContent objects in the backup tarball, as well as upload all VolumeSnapshots and VolumeSnapshotContents objects in a JSON file to the object storage system. + +When Velero synchronizes backups into a new cluster, VolumeSnapshotContent objects and the VolumeSnapshotClass that is chosen to take +snapshot will be synced into the cluster as well, so that Velero can manage backup expiration appropriately. + + +The `DeletionPolicy` on the VolumeSnapshotContent will be the same as the `DeletionPolicy` on the VolumeSnapshotClass that was used to create the VolumeSnapshot. Setting a `DeletionPolicy` of `Retain` on the VolumeSnapshotClass will preserve the volume snapshot in the storage system for the lifetime of the Velero backup and will prevent the deletion of the volume snapshot, in the storage system, in the event of a disaster where the namespace with the VolumeSnapshot object may be lost. + +When the Velero backup expires, the VolumeSnapshot objects will be deleted and the VolumeSnapshotContent objects will be updated to have a `DeletionPolicy` of `Delete`, to free space on the storage system. + +For more details on how each plugin works, see the [CSI plugin repo][2]'s documentation. + +**Note:** The AWS, Microsoft Azure, and Google Cloud Platform (GCP) Velero plugins version 1.4 and later are able to snapshot and restore persistent volumes provisioned by a CSI driver via the APIs of the cloud provider, without having to install Velero CSI plugins. See the [AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws), [Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure), and [Google Cloud Platform (GCP)](https://github.com/vmware-tanzu/velero-plugin-for-gcp) Velero plugin repo for more information on supported CSI drivers. + +[1]: customize-installation.md#enable-server-side-features +[2]: https://github.com/vmware-tanzu/velero-plugin-for-csi/ +[3]: https://hub.docker.com/repository/docker/velero/velero-plugin-for-csi diff --git a/site/content/docs/v1.9/custom-plugins.md b/site/content/docs/v1.9/custom-plugins.md new file mode 100644 index 0000000000..ba0b357dc1 --- /dev/null +++ b/site/content/docs/v1.9/custom-plugins.md @@ -0,0 +1,115 @@ +--- +title: "Plugins" +layout: docs +--- + +Velero has a plugin architecture that allows users to add their own custom functionality to Velero backups & restores without having to modify/recompile the core Velero binary. To add custom functionality, users simply create their own binary containing implementations of Velero's plugin kinds (described below), plus a small amount of boilerplate code to expose the plugin implementations to Velero. This binary is added to a container image that serves as an init container for the Velero server pod and copies the binary into a shared emptyDir volume for the Velero server to access. + +Multiple plugins, of any type, can be implemented in this binary. + +A fully-functional [sample plugin repository][1] is provided to serve as a convenient starting point for plugin authors. + +## Plugin Naming + +A plugin is identified by a prefix + name. + +**Note: Please don't use `velero.io` as the prefix for a plugin not supported by the Velero team.** The prefix should help users identify the entity developing the plugin, so please use a prefix that identify yourself. + +Whenever you define a Backup Storage Location or Volume Snapshot Location, this full name will be the value for the `provider` specification. + +For example: `oracle.io/oracle`. + +``` +apiVersion: velero.io/v1 +kind: BackupStorageLocation +spec: + provider: oracle.io/oracle +``` + +``` +apiVersion: velero.io/v1 +kind: VolumeSnapshotLocation +spec: + provider: oracle.io/oracle +``` + +When naming your plugin, keep in mind that the full name needs to conform to these rules: +- have two parts, prefix + name, separated by '/' +- none of the above parts can be empty +- the prefix is a valid DNS subdomain name +- a plugin with the same prefix + name cannot not already exist + +### Some examples: + +``` +- example.io/azure +- 1.2.3.4/5678 +- example-with-dash.io/azure +``` + +You will need to give your plugin(s) the full name when registering them by calling the appropriate `RegisterX` function: + +## Plugin Kinds + +Velero supports the following kinds of plugins: + +- **Object Store** - persists and retrieves backups, backup logs and restore logs +- **Volume Snapshotter** - creates volume snapshots (during backup) and restores volumes from snapshots (during restore) +- **Backup Item Action** - executes arbitrary logic for individual items prior to storing them in a backup file +- **Restore Item Action** - executes arbitrary logic for individual items prior to restoring them into a cluster +- **Delete Item Action** - executes arbitrary logic based on individual items within a backup prior to deleting the backup + +## Plugin Logging + +Velero provides a [logger][2] that can be used by plugins to log structured information to the main Velero server log or +per-backup/restore logs. It also passes a `--log-level` flag to each plugin binary, whose value is the value of the same +flag from the main Velero process. This means that if you turn on debug logging for the Velero server via `--log-level=debug`, +plugins will also emit debug-level logs. See the [sample repository][1] for an example of how to use the logger within your plugin. + +## Plugin Configuration + +Velero uses a ConfigMap-based convention for providing configuration to plugins. If your plugin needs to be configured at runtime, +define a ConfigMap like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + # any name can be used; Velero uses the labels (below) + # to identify it rather than the name + name: my-plugin-config + + # must be in the namespace where the velero deployment + # is running + namespace: velero + + labels: + # this value-less label identifies the ConfigMap as + # config for a plugin (the built-in change storageclass + # restore item action plugin) + velero.io/plugin-config: "" + + # add a label whose key corresponds to the fully-qualified + # plugin name (for example mydomain.io/my-plugin-name), and whose + # value is the plugin type (BackupItemAction, RestoreItemAction, + # ObjectStore, or VolumeSnapshotter) + : + +data: + # add your configuration data here as key-value pairs +``` + +Then, in your plugin's implementation, you can read this ConfigMap to fetch the necessary configuration. + +## Feature Flags + +Velero will pass any known features flags as a comma-separated list of strings to the `--features` argument. + +Once parsed into a `[]string`, the features can then be registered using the `NewFeatureFlagSet` function and queried with `features.Enabled()`. + +## Environment Variables + +Velero adds the `LD_LIBRARY_PATH` into the list of environment variables to provide the convenience for plugins that requires C libraries/extensions in the runtime. + +[1]: https://github.com/vmware-tanzu/velero-plugin-example +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/plugin/logger.go diff --git a/site/content/docs/v1.9/customize-installation.md b/site/content/docs/v1.9/customize-installation.md new file mode 100644 index 0000000000..b8721662a3 --- /dev/null +++ b/site/content/docs/v1.9/customize-installation.md @@ -0,0 +1,390 @@ +--- +title: "Customize Velero Install" +layout: docs +--- + +## Plugins + +During install, Velero requires that at least one plugin is added (with the `--plugins` flag). Please see the documentation under [Plugins](overview-plugins.md) + +## Install in any namespace + +Velero is installed in the `velero` namespace by default. However, you can install Velero in any namespace. See [run in custom namespace][2] for details. + +## Use non-file-based identity mechanisms + +By default, `velero install` expects a credentials file for your `velero` IAM account to be provided via the `--secret-file` flag. + +If you are using an alternate identity mechanism, such as kube2iam/kiam on AWS, Workload Identity on GKE, etc., that does not require a credentials file, you can specify the `--no-secret` flag instead of `--secret-file`. + +## Enable restic integration + +By default, `velero install` does not install Velero's [restic integration][3]. To enable it, specify the `--use-restic` flag. + +If you've already run `velero install` without the `--use-restic` flag, you can run the same command again, including the `--use-restic` flag, to add the restic integration to your existing install. + +## Default Pod Volume backup to restic + +By default, `velero install` does not enable the use of restic to take backups of all pod volumes. You must apply an [annotation](restic.md/#using-opt-in-pod-volume-backup) to every pod which contains volumes for Velero to use restic for the backup. + +If you are planning to only use restic for volume backups, you can run the `velero install` command with the `--default-volumes-to-restic` flag. This will default all pod volumes backups to use restic without having to apply annotations to pods. Note that when this flag is set during install, Velero will always try to use restic to perform the backup, even want an individual backup to use volume snapshots, by setting the `--snapshot-volumes` flag in the `backup create` command. Alternatively, you can set the `--default-volumes-to-restic` on an individual backup to to make sure Velero uses Restic for each volume being backed up. + +## Enable features + +New features in Velero will be released as beta features behind feature flags which are not enabled by default. A full listing of Velero feature flags can be found [here][11]. + +### Enable server side features + +Features on the Velero server can be enabled using the `--features` flag to the `velero install` command. This flag takes as value a comma separated list of feature flags to enable. As an example [CSI snapshotting of PVCs][10] can be enabled using `EnableCSI` feature flag in the `velero install` command as shown below: + +```bash +velero install --features=EnableCSI +``` + +Another example is enabling the support of multiple API group versions, as documented at [- -features=EnableAPIGroupVersions](enable-api-group-versions-feature.md). + +Feature flags, passed to `velero install` will be passed to the Velero deployment and also to the `restic` daemon set, if `--use-restic` flag is used. + +Similarly, features may be disabled by removing the corresponding feature flags from the `--features` flag. + +Enabling and disabling feature flags will require modifying the Velero deployment and also the restic daemonset. This may be done from the CLI by uninstalling and re-installing Velero, or by editing the `deploy/velero` and `daemonset/restic` resources in-cluster. + +```bash +$ kubectl -n velero edit deploy/velero +$ kubectl -n velero edit daemonset/restic +``` + +### Enable client side features + +For some features it may be necessary to use the `--features` flag to the Velero client. This may be done by passing the `--features` on every command run using the Velero CLI or the by setting the features in the velero client config file using the `velero client config set` command as shown below: + +```bash +velero client config set features=EnableCSI +``` + +This stores the config in a file at `$HOME/.config/velero/config.json`. + +All client side feature flags may be disabled using the below command + +```bash +velero client config set features= +``` + +### Colored CLI output + +Velero CLI uses colored output for some commands, such as `velero describe`. If +the environment in which Velero is run doesn't support colored output, the +colored output will be automatically disabled. However, you can manually disable +colors with config file: + +```bash +velero client config set colorized=false +``` + +Note that if you specify `--colorized=true` as a CLI option it will override +the config file setting. + + +## Customize resource requests and limits + +At installation, Velero sets default resource requests and limits for the Velero pod and the restic pod, if you using the [restic integration](/docs/main/restic/). + +{{< table caption="Velero Customize resource requests and limits defaults" >}} +|Setting|Velero pod defaults|restic pod defaults| +|--- |--- |--- | +|CPU request|500m|500m| +|Memory requests|128Mi|512Mi| +|CPU limit|1000m (1 CPU)|1000m (1 CPU)| +|Memory limit|512Mi|1024Mi| +{{< /table >}} + +Depending on the cluster resources, especially if you are using Restic, you may need to increase these defaults. Through testing, the Velero maintainers have found these defaults work well when backing up and restoring 1000 or less resources and total size of files is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. + +Due to a [known Restic issue](https://github.com/restic/restic/issues/2446), the Restic pod will consume large amounts of memory, especially if you are backing up millions of tiny files and directories. If you are planning to use Restic to backup 100GB of data or more, you will need to increase the resource limits to make sure backups complete successfully. + +### Install with custom resource requests and limits + +You can customize these resource requests and limit when you first install using the [velero install][6] CLI command. + +``` +velero install \ + --velero-pod-cpu-request \ + --velero-pod-mem-request \ + --velero-pod-cpu-limit \ + --velero-pod-mem-limit \ + [--use-restic] \ + [--default-volumes-to-restic] \ + [--restic-pod-cpu-request ] \ + [--restic-pod-mem-request ] \ + [--restic-pod-cpu-limit ] \ + [--restic-pod-mem-limit ] +``` + +### Update resource requests and limits after install + +After installation you can adjust the resource requests and limits in the Velero Deployment spec or restic DeamonSet spec, if you are using the restic integration. + +**Velero pod** + +Update the `spec.template.spec.containers.resources.limits` and `spec.template.spec.containers.resources.requests` values in the Velero deployment. + +```bash +kubectl patch deployment velero -n velero --patch \ +'{"spec":{"template":{"spec":{"containers":[{"name": "velero", "resources": {"limits":{"cpu": "1", "memory": "512Mi"}, "requests": {"cpu": "1", "memory": "128Mi"}}}]}}}}' +``` + +**restic pod** + +Update the `spec.template.spec.containers.resources.limits` and `spec.template.spec.containers.resources.requests` values in the restic DeamonSet spec. + +```bash +kubectl patch daemonset restic -n velero --patch \ +'{"spec":{"template":{"spec":{"containers":[{"name": "restic", "resources": {"limits":{"cpu": "1", "memory": "1024Mi"}, "requests": {"cpu": "1", "memory": "512Mi"}}}]}}}}' +``` + +Additionally, you may want to update the the default Velero restic pod operation timeout (default 240 minutes) to allow larger backups more time to complete. You can adjust this timeout by adding the `- --restic-timeout` argument to the Velero Deployment spec. + +**NOTE:** Changes made to this timeout value will revert back to the default value if you re-run the Velero install command. + +1. Open the Velero Deployment spec. + + ``` + kubectl edit deploy velero -n velero + ``` + +1. Add `- --restic-timeout` to `spec.template.spec.containers`. + + ```yaml + spec: + template: + spec: + containers: + - args: + - --restic-timeout=240m + ``` + +## Configure more than one storage location for backups or volume snapshots + +Velero supports any number of backup storage locations and volume snapshot locations. For more details, see [about locations](locations.md). + +However, `velero install` only supports configuring at most one backup storage location and one volume snapshot location. + +To configure additional locations after running `velero install`, use the `velero backup-location create` and/or `velero snapshot-location create` commands along with provider-specific configuration. Use the `--help` flag on each of these commands for more details. + +### Set default backup storage location or volume snapshot locations + +When performing backups, Velero needs to know where to backup your data. This means that if you configure multiple locations, you must specify the location Velero should use each time you run `velero backup create`, or you can set a default backup storage location or default volume snapshot locations. If you only have one backup storage llocation or volume snapshot location set for a provider, Velero will automatically use that location as the default. + +Set a default backup storage location by passing a `--default` flag with when running `velero backup-location create`. + +``` +velero backup-location create backups-primary \ + --provider aws \ + --bucket velero-backups \ + --config region=us-east-1 \ + --default +``` + +You can set a default volume snapshot location for each of your volume snapshot providers using the `--default-volume-snapshot-locations` flag on the `velero server` command. + +``` +velero server --default-volume-snapshot-locations=":,:" +``` + +## Do not configure a backup storage location during install + +If you need to install Velero without a default backup storage location (without specifying `--bucket` or `--provider`), the `--no-default-backup-location` flag is required for confirmation. + +## Install an additional volume snapshot provider + +Velero supports using different providers for volume snapshots than for object storage -- for example, you can use AWS S3 for object storage, and Portworx for block volume snapshots. + +However, `velero install` only supports configuring a single matching provider for both object storage and volume snapshots. + +To use a different volume snapshot provider: + +1. Install the Velero server components by following the instructions for your **object storage** provider + +1. Add your volume snapshot provider's plugin to Velero (look in [your provider][0]'s documentation for the image name): + + ```bash + velero plugin add + ``` + +1. Add a volume snapshot location for your provider, following [your provider][0]'s documentation for configuration: + + ```bash + velero snapshot-location create \ + --provider \ + [--config ] + ``` + +## Generate YAML only + +By default, `velero install` generates and applies a customized set of Kubernetes configuration (YAML) to your cluster. + +To generate the YAML without applying it to your cluster, use the `--dry-run -o yaml` flags. + +This is useful for applying bespoke customizations, integrating with a GitOps workflow, etc. + +If you are installing Velero in Kubernetes 1.14.x or earlier, you need to use `kubectl apply`'s `--validate=false` option when applying the generated configuration to your cluster. See [issue 2077][7] and [issue 2311][8] for more context. + +## Use a storage provider secured by a self-signed certificate + +If you intend to use Velero with a storage provider that is secured by a self-signed certificate, +you may need to instruct Velero to trust that certificate. See [use Velero with a storage provider secured by a self-signed certificate][9] for details. + +## Additional options + +Run `velero install --help` or see the [Helm chart documentation](https://vmware-tanzu.github.io/helm-charts/) for the full set of installation options. + +## Optional Velero CLI configurations + +### Enabling shell autocompletion + +**Velero CLI** provides autocompletion support for `Bash` and `Zsh`, which can save you a lot of typing. + +Below are the procedures to set up autocompletion for `Bash` (including the difference between `Linux` and `macOS`) and `Zsh`. + +#### Bash on Linux + +The **Velero CLI** completion script for `Bash` can be generated with the command `velero completion bash`. Sourcing the completion script in your shell enables velero autocompletion. + +However, the completion script depends on [**bash-completion**](https://github.com/scop/bash-completion), which means that you have to install this software first (you can test if you have bash-completion already installed by running `type _init_completion`). + +##### Install bash-completion + +`bash-completion` is provided by many package managers (see [here](https://github.com/scop/bash-completion#installation)). You can install it with `apt-get install bash-completion` or `yum install bash-completion`, etc. + +The above commands create `/usr/share/bash-completion/bash_completion`, which is the main script of bash-completion. Depending on your package manager, you have to manually source this file in your `~/.bashrc` file. + +To find out, reload your shell and run `type _init_completion`. If the command succeeds, you're already set, otherwise add the following to your `~/.bashrc` file: + +```shell +source /usr/share/bash-completion/bash_completion +``` + +Reload your shell and verify that bash-completion is correctly installed by typing `type _init_completion`. + +##### Enable Velero CLI autocompletion for Bash on Linux + +You now need to ensure that the **Velero CLI** completion script gets sourced in all your shell sessions. There are two ways in which you can do this: + +- Source the completion script in your `~/.bashrc` file: + + ```shell + echo 'source <(velero completion bash)' >>~/.bashrc + ``` + +- Add the completion script to the `/etc/bash_completion.d` directory: + + ```shell + velero completion bash >/etc/bash_completion.d/velero + ``` + +- If you have an alias for velero, you can extend shell completion to work with that alias: + + ```shell + echo 'alias v=velero' >>~/.bashrc + echo 'complete -F __start_velero v' >>~/.bashrc + ``` + +> `bash-completion` sources all completion scripts in `/etc/bash_completion.d`. + +Both approaches are equivalent. After reloading your shell, velero autocompletion should be working. + +#### Bash on macOS + +The **Velero CLI** completion script for Bash can be generated with `velero completion bash`. Sourcing this script in your shell enables velero completion. + +However, the velero completion script depends on [**bash-completion**](https://github.com/scop/bash-completion) which you thus have to previously install. + + +> There are two versions of bash-completion, v1 and v2. V1 is for Bash 3.2 (which is the default on macOS), and v2 is for Bash 4.1+. The velero completion script **doesn't work** correctly with bash-completion v1 and Bash 3.2. It requires **bash-completion v2** and **Bash 4.1+**. Thus, to be able to correctly use velero completion on macOS, you have to install and use Bash 4.1+ ([*instructions*](https://itnext.io/upgrading-bash-on-macos-7138bd1066ba)). The following instructions assume that you use Bash 4.1+ (that is, any Bash version of 4.1 or newer). + + +##### Install bash-completion + +> As mentioned, these instructions assume you use Bash 4.1+, which means you will install bash-completion v2 (in contrast to Bash 3.2 and bash-completion v1, in which case kubectl completion won't work). + +You can test if you have bash-completion v2 already installed with `type _init_completion`. If not, you can install it with Homebrew: + + ```shell + brew install bash-completion@2 + ``` + +As stated in the output of this command, add the following to your `~/.bashrc` file: + + ```shell + export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d" + [[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh" + ``` + +Reload your shell and verify that bash-completion v2 is correctly installed with `type _init_completion`. + +##### Enable Velero CLI autocompletion for Bash on macOS + +You now have to ensure that the velero completion script gets sourced in all your shell sessions. There are multiple ways to achieve this: + +- Source the completion script in your `~/.bashrc` file: + + ```shell + echo 'source <(velero completion bash)' >>~/.bashrc + + ``` + +- Add the completion script to the `/usr/local/etc/bash_completion.d` directory: + + ```shell + velero completion bash >/usr/local/etc/bash_completion.d/velero + ``` + +- If you have an alias for velero, you can extend shell completion to work with that alias: + + ```shell + echo 'alias v=velero' >>~/.bashrc + echo 'complete -F __start_velero v' >>~/.bashrc + ``` + +- If you installed velero with Homebrew (as explained [above](#install-with-homebrew-on-macos)), then the velero completion script should already be in `/usr/local/etc/bash_completion.d/velero`. In that case, you don't need to do anything. + +> The Homebrew installation of bash-completion v2 sources all the files in the `BASH_COMPLETION_COMPAT_DIR` directory, that's why the latter two methods work. + +In any case, after reloading your shell, velero completion should be working. + +#### Autocompletion on Zsh + +The velero completion script for Zsh can be generated with the command `velero completion zsh`. Sourcing the completion script in your shell enables velero autocompletion. + +To do so in all your shell sessions, add the following to your `~/.zshrc` file: + + ```shell + source <(velero completion zsh) + ``` + +If you have an alias for kubectl, you can extend shell completion to work with that alias: + + ```shell + echo 'alias v=velero' >>~/.zshrc + echo 'complete -F __start_velero v' >>~/.zshrc + ``` + +After reloading your shell, kubectl autocompletion should be working. + +If you get an error like `complete:13: command not found: compdef`, then add the following to the beginning of your `~/.zshrc` file: + + ```shell + autoload -Uz compinit + compinit + ``` + +[1]: https://github.com/vmware-tanzu/velero/releases/latest +[2]: namespace.md +[3]: restic.md +[4]: on-premises.md +[6]: velero-install.md#usage +[7]: https://github.com/vmware-tanzu/velero/issues/2077 +[8]: https://github.com/vmware-tanzu/velero/issues/2311 +[9]: self-signed-certificates.md +[10]: csi.md +[11]: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/apis/velero/v1/constants.go diff --git a/site/content/docs/v1.9/debugging-install.md b/site/content/docs/v1.9/debugging-install.md new file mode 100644 index 0000000000..e6a75e5256 --- /dev/null +++ b/site/content/docs/v1.9/debugging-install.md @@ -0,0 +1,74 @@ +--- +title: "Debugging Installation Issues" +layout: docs +--- + +## General + +### `invalid configuration: no configuration has been provided` +This typically means that no `kubeconfig` file can be found for the Velero client to use. Velero looks for a kubeconfig in the +following locations: +* the path specified by the `--kubeconfig` flag, if any +* the path specified by the `$KUBECONFIG` environment variable, if any +* `~/.kube/config` + +### Backups or restores stuck in `New` phase +This means that the Velero controllers are not processing the backups/restores, which usually happens because the Velero server is not running. Check the pod description and logs for errors: +``` +kubectl -n velero describe pods +kubectl -n velero logs deployment/velero +``` + + +## AWS + +### `NoCredentialProviders: no valid providers in chain` + +#### Using credentials +This means that the secret containing the AWS IAM user credentials for Velero has not been created/mounted properly +into the Velero server pod. Ensure the following: + +* The `cloud-credentials` secret exists in the Velero server's namespace +* The `cloud-credentials` secret has a single key, `cloud`, whose value is the contents of the `credentials-velero` file +* The `credentials-velero` file is formatted properly and has the correct values: + + ``` + [default] + aws_access_key_id= + aws_secret_access_key= + ``` + +* The `cloud-credentials` secret is defined as a volume for the Velero deployment +* The `cloud-credentials` secret is being mounted into the Velero server pod at `/credentials` + +#### Using kube2iam +This means that Velero can't read the content of the S3 bucket. Ensure the following: + +* A Trust Policy document exists that allows the role used by kube2iam to assume Velero's role, as stated in the AWS config documentation. +* The new Velero role has all the permissions listed in the documentation regarding S3. + + +## Azure + +### `Failed to refresh the Token` or `adal: Refresh request failed` +This means that the secrets containing the Azure service principal credentials for Velero has not been created/mounted +properly into the Velero server pod. Ensure the following: + +* The `cloud-credentials` secret exists in the Velero server's namespace +* The `cloud-credentials` secret has all of the expected keys and each one has the correct value (see [setup instructions][0]) +* The `cloud-credentials` secret is defined as a volume for the Velero deployment +* The `cloud-credentials` secret is being mounted into the Velero server pod at `/credentials` + + +## GCE/GKE + +### `open credentials/cloud: no such file or directory` +This means that the secret containing the GCE service account credentials for Velero has not been created/mounted properly +into the Velero server pod. Ensure the following: + +* The `cloud-credentials` secret exists in the Velero server's namespace +* The `cloud-credentials` secret has a single key, `cloud`, whose value is the contents of the `credentials-velero` file +* The `cloud-credentials` secret is defined as a volume for the Velero deployment +* The `cloud-credentials` secret is being mounted into the Velero server pod at `/credentials` + +[0]: azure-config.md#create-service-principal diff --git a/site/content/docs/v1.9/debugging-restores.md b/site/content/docs/v1.9/debugging-restores.md new file mode 100644 index 0000000000..3320349cda --- /dev/null +++ b/site/content/docs/v1.9/debugging-restores.md @@ -0,0 +1,105 @@ +--- +title: "Debugging Restores" +layout: docs +--- + +## Example + +When Velero finishes a Restore, its status changes to "Completed" regardless of whether or not there are issues during the process. The number of warnings and errors are indicated in the output columns from `velero restore get`: + +``` +NAME BACKUP STATUS WARNINGS ERRORS CREATED SELECTOR +backup-test-20170726180512 backup-test Completed 155 76 2017-07-26 11:41:14 -0400 EDT +backup-test-20170726180513 backup-test Completed 121 14 2017-07-26 11:48:24 -0400 EDT +backup-test-2-20170726180514 backup-test-2 Completed 0 0 2017-07-26 13:31:21 -0400 EDT +backup-test-2-20170726180515 backup-test-2 Completed 0 1 2017-07-26 13:32:59 -0400 EDT +``` + +To delve into the warnings and errors into more detail, you can use `velero restore describe`: + +```bash +velero restore describe backup-test-20170726180512 +``` + +The output looks like this: + +``` +Name: backup-test-20170726180512 +Namespace: velero +Labels: +Annotations: + +Backup: backup-test + +Namespaces: + Included: * + Excluded: + +Resources: + Included: serviceaccounts + Excluded: nodes, events, events.events.k8s.io + Cluster-scoped: auto + +Namespace mappings: + +Label selector: + +Restore PVs: auto + +Preserve Service NodePorts: auto + +Phase: Completed + +Validation errors: + +Warnings: + Velero: + Cluster: + Namespaces: + velero: serviceaccounts "velero" already exists + serviceaccounts "default" already exists + kube-public: serviceaccounts "default" already exists + kube-system: serviceaccounts "attachdetach-controller" already exists + serviceaccounts "certificate-controller" already exists + serviceaccounts "cronjob-controller" already exists + serviceaccounts "daemon-set-controller" already exists + serviceaccounts "default" already exists + serviceaccounts "deployment-controller" already exists + serviceaccounts "disruption-controller" already exists + serviceaccounts "endpoint-controller" already exists + serviceaccounts "generic-garbage-collector" already exists + serviceaccounts "horizontal-pod-autoscaler" already exists + serviceaccounts "job-controller" already exists + serviceaccounts "kube-dns" already exists + serviceaccounts "namespace-controller" already exists + serviceaccounts "node-controller" already exists + serviceaccounts "persistent-volume-binder" already exists + serviceaccounts "pod-garbage-collector" already exists + serviceaccounts "replicaset-controller" already exists + serviceaccounts "replication-controller" already exists + serviceaccounts "resourcequota-controller" already exists + serviceaccounts "service-account-controller" already exists + serviceaccounts "service-controller" already exists + serviceaccounts "statefulset-controller" already exists + serviceaccounts "ttl-controller" already exists + default: serviceaccounts "default" already exists + +Errors: + Velero: + Cluster: + Namespaces: +``` + +## Structure + +Errors appear for incomplete or partial restores. Warnings appear for non-blocking issues, for example, the +restore looks "normal" and all resources referenced in the backup exist in some form, although some +of them may have been pre-existing. + +Both errors and warnings are structured in the same way: + +* `Velero`: A list of system-related issues encountered by the Velero server. For example, Velero couldn't read a directory. + +* `Cluster`: A list of issues related to the restore of cluster-scoped resources. + +* `Namespaces`: A map of namespaces to the list of issues related to the restore of their respective resources. diff --git a/site/content/docs/v1.9/development.md b/site/content/docs/v1.9/development.md new file mode 100644 index 0000000000..82ace894d3 --- /dev/null +++ b/site/content/docs/v1.9/development.md @@ -0,0 +1,56 @@ +--- +title: "Development " +layout: docs +--- + +## Update generated files + +Run `make update` to regenerate files if you make the following changes: + +* Add/edit/remove command line flags and/or their help text +* Add/edit/remove commands or subcommands +* Add new API types +* Add/edit/remove plugin protobuf message or service definitions + +The following files are automatically generated from the source code: + +* The clientset +* Listers +* Shared informers +* Documentation +* Protobuf/gRPC types + +You can run `make verify` to ensure that all generated files (clientset, listers, shared informers, docs) are up to date. + +## Linting + +You can run `make lint` which executes golangci-lint inside the build image, or `make local-lint` which executes outside of the build image. +Both `make lint` and `make local-lint` will only run the linter against changes. + +Use `lint-all` to run the linter against the entire code base. + +The default linters are defined in the `Makefile` via the `LINTERS` variable. + +You can also override the default list of linters by running the command + +`$ make lint LINTERS=gosec` + +## Test + +To run unit tests, use `make test`. + +## Vendor dependencies + +If you need to add or update the vendored dependencies, see [Vendoring dependencies][11]. + +## Using the main branch + +If you are developing or using the main branch, note that you may need to update the Velero CRDs to get new changes as other development work is completed. + +```bash +velero install --crds-only --dry-run -o yaml | kubectl apply -f - +``` + +**NOTE:** You could change the default CRD API version (v1beta1 _or_ v1) if Velero CLI can't discover the Kubernetes preferred CRD API version. The Kubernetes version < 1.16 preferred CRD API version is v1beta1; the Kubernetes version >= 1.16 preferred CRD API version is v1. + +[11]: vendoring-dependencies.md diff --git a/site/content/docs/v1.9/disaster-case.md b/site/content/docs/v1.9/disaster-case.md new file mode 100644 index 0000000000..6b73e6716d --- /dev/null +++ b/site/content/docs/v1.9/disaster-case.md @@ -0,0 +1,44 @@ +--- +title: "Disaster recovery" +layout: docs +--- + +*Using Schedules and Read-Only Backup Storage Locations* + +If you periodically back up your cluster's resources, you are able to return to a previous state in case of some unexpected mishap, such as a service outage. Doing so with Velero looks like the following: + +1. After you first run the Velero server on your cluster, set up a daily backup (replacing `` in the command as desired): + + ``` + velero schedule create --schedule "0 7 * * *" + ``` + + This creates a Backup object with the name `-`. The default backup retention period, expressed as TTL (time to live), is 30 days (720 hours); you can use the `--ttl ` flag to change this as necessary. See [how velero works][1] for more information about backup expiry. + +1. A disaster happens and you need to recreate your resources. + +1. Update your backup storage location to read-only mode (this prevents backup objects from being created or deleted in the backup storage location during the restore process): + + ```bash + kubectl patch backupstoragelocation \ + --namespace velero \ + --type merge \ + --patch '{"spec":{"accessMode":"ReadOnly"}}' + ``` + +1. Create a restore with your most recent Velero Backup: + + ``` + velero restore create --from-backup - + ``` + +1. When ready, revert your backup storage location to read-write mode: + + ```bash + kubectl patch backupstoragelocation \ + --namespace velero \ + --type merge \ + --patch '{"spec":{"accessMode":"ReadWrite"}}' + ``` + +[1]: how-velero-works.md#set-a-backup-to-expire diff --git a/site/content/docs/v1.9/enable-api-group-versions-feature.md b/site/content/docs/v1.9/enable-api-group-versions-feature.md new file mode 100644 index 0000000000..5c40b3e71a --- /dev/null +++ b/site/content/docs/v1.9/enable-api-group-versions-feature.md @@ -0,0 +1,115 @@ +--- +title: "Enable API Group Versions Feature" +layout: docs +--- + +## Background + +Velero serves to both restore and migrate Kubernetes applications. Typically, backup and restore does not involve upgrading Kubernetes API group versions. However, when migrating from a source cluster to a destination cluster, it is not unusual to see the API group versions differing between clusters. + +**NOTE:** Kubernetes applications are made up of various resources. Common resources are pods, jobs, and deployments. Custom resources are created via custom resource definitions (CRDs). Every resource, whether custom or not, is part of a group, and each group has a version called the API group version. + +Kubernetes by default allows changing API group versions between clusters as long as the upgrade is a single version, for example, v1 -> v2beta1. Jumping multiple versions, for example, v1 -> v3, is not supported out of the box. This is where the Velero Enable API Group Version feature can help you during an upgrade. + +Currently, the Enable API Group Version feature is in beta and can be enabled by installing Velero with a [feature flag](customize-installation.md/#enable-server-side-features), `--features=EnableAPIGroupVersions`. + +For the most up-to-date information on Kubernetes API version compatibility, you should always review the [Kubernetes release notes](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG) for the source and destination cluster version to before starting an upgrade, migration, or restore. If there is a difference between Kubernetes API versions, use the Enable API Group Version feature to help mitigate compatibility issues. + +## How the Enable API Group Versions Feature Works + +When the Enable API Group Versions feature is enabled on the source cluster, Velero will not only back up Kubernetes preferred API group versions, but it will also back up all supported versions on the cluster. As an example, consider the resource `horizontalpodautoscalers` which falls under the `autoscaling` group. Without the feature flag enabled, only the preferred API group version for autoscaling, `v1` will be backed up. With the feature enabled, the remaining supported versions, `v2beta1` and `v2beta2` will also be backed up. Once the versions are stored in the backup tarball file, they will be available to be restored on the destination cluster. + +When the Enable API Group Versions feature is enabled on the destination cluster, Velero restore will choose the version to restore based on an API group version priority order. + +The version priorities are listed from highest to lowest priority below: + +- Priority 1: destination cluster preferred version +- Priority 2: source cluster preferred version +- Priority 3: non-preferred common supported version with the highest [Kubernetes version priority](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#version-priority) + +The highest priority (Priority 1) will be the destination cluster's preferred API group version. If the destination preferred version is found in the backup tarball, it will be the API group version chosen for restoration for that resource. However, if the destination preferred version is not found in the backup tarball, the next version in the list will be selected: the source cluster preferred version (Priority 2). + +If the source cluster preferred version is found to be supported by the destination cluster, it will be chosen as the API group version to restore. However, if the source preferred version is not supported by the destination cluster, then the next version in the list will be considered: a non-preferred common supported version (Priority 3). + +In the case that there are more than one non-preferred common supported version, which version will be chosen? The answer requires understanding the [Kubernetes version priority order](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#version-priority). Kubernetes prioritizes group versions by making the latest, most stable version the highest priority. The highest priority version is the Kubernetes preferred version. Here is a sorted version list example from the Kubernetes.io documentation: + +- v10 +- v2 +- v1 +- v11beta2 +- v10beta3 +- v3beta1 +- v12alpha1 +- v11alpha2 +- foo1 +- foo10 + +Of the non-preferred common versions, the version that has the highest Kubernetes version priority will be chosen. See the example for Priority 3 below. + +To better understand which API group version will be chosen, the following provides some concrete examples. The examples use the term "target cluster" which is synonymous to "destination cluster". + +![Priority 1 Case A example](/docs/main/img/gv_priority1-caseA.png) + +![Priority 1 Case B example](/docs/main/img/gv_priority1-caseB.png) + +![Priority 2 Case C example](/docs/main/img/gv_priority2-caseC.png) + +![Priority 3 Case D example](/docs/main/img/gv_priority3-caseD.png) + +## Procedure for Using the Enable API Group Versions Feature + +1. [Install Velero](basic-install.md) on source cluster with the [feature flag enabled](customize-installation.md/#enable-server-side-features). The flag is `--features=EnableAPIGroupVersions`. For the enable API group versions feature to work, the feature flag needs to be used for Velero installations on both the source and destination clusters. +2. Back up and restore following the [migration case instructions](migration-case.md). Note that "Cluster 1" in the instructions refers to the source cluster, and "Cluster 2" refers to the destination cluster. + +## Advanced Procedure for Customizing the Version Prioritization + +Optionally, users can create a config map to override the default API group prioritization for some or all of the resources being migrated. For each resource that is specified by the user, Velero will search for the version in both the backup tarball and the destination cluster. If there is a match, the user-specified API group version will be restored. If the backup tarball and the destination cluster does not have or support any of the user-specified versions, then the default version prioritization will be used. + +Here are the steps for creating a config map that allows users to override the default version prioritization. These steps must happen on the destination cluster before a Velero restore is initiated. + +1. Create a file called `restoreResourcesVersionPriority`. The file name will become a key in the `data` field of the config map. + - In the file, write a line for each resource group you'd like to override. Make sure each line follows the format `.=,` + - Note that the resource group and versions are separated by a single equal (=) sign. Each version is listed in order of user's priority separated by commas. + - Here is an example of the contents of a config map file: + + ```cm + rockbands.music.example.io=v2beta1,v2beta2 + orchestras.music.example.io=v2,v3alpha1 + subscriptions.operators.coreos.com=v2,v1 + ``` + +2. Apply config map with + + ```bash + kubectl create configmap enableapigroupversions --from-file=/restoreResourcesVersionPriority -n velero + ``` + +3. See the config map with + + ```bash + kubectl describe configmap enableapigroupversions -n velero + ``` + + The config map should look something like + + ```bash + Name: enableapigroupversions + Namespace: velero + Labels: + Annotations: + + Data + ==== + restoreResourcesVersionPriority: + ---- + rockbands.music.example.io=v2beta1,v2beta2 + orchestras.music.example.io=v2,v3alpha1 + subscriptions.operators.coreos.com=v2,v1 + Events: + ``` + +## Troubleshooting + +1. Refer to the [troubleshooting section](troubleshooting.md) of the docs as the techniques generally apply here as well. +2. The [debug logs](troubleshooting.md/#getting-velero-debug-logs) will contain information on which version was chosen to restore. +3. If no API group version could be found that both exists in the backup tarball file and is supported by the destination cluster, then the following error will be recorded (no need to activate debug level logging): `"error restoring rockbands.music.example.io/rockstars/beatles: the server could not find the requested resource"`. diff --git a/site/content/docs/v1.9/examples.md b/site/content/docs/v1.9/examples.md new file mode 100644 index 0000000000..58a35fd5da --- /dev/null +++ b/site/content/docs/v1.9/examples.md @@ -0,0 +1,70 @@ +--- +title: "Examples" +layout: docs +--- + +After you set up the Velero server, you can clone the examples used in the following sections by running the following: +``` +git clone https://github.com/vmware-tanzu/velero.git +cd velero +``` + +## Basic example (without PersistentVolumes) + +1. Start the sample nginx app: + + ```bash + kubectl apply -f examples/nginx-app/base.yaml + ``` + +1. Create a backup: + + ```bash + velero backup create nginx-backup --include-namespaces nginx-example + ``` + +1. Simulate a disaster: + + ```bash + kubectl delete namespaces nginx-example + ``` + + Wait for the namespace to be deleted. + +1. Restore your lost resources: + + ```bash + velero restore create --from-backup nginx-backup + ``` + +## Snapshot example (with PersistentVolumes) + +> NOTE: For Azure, you must run Kubernetes version 1.7.2 or later to support PV snapshotting of managed disks. + +1. Start the sample nginx app: + + ```bash + kubectl apply -f examples/nginx-app/with-pv.yaml + ``` + +1. Create a backup with PV snapshotting: + + ```bash + velero backup create nginx-backup --include-namespaces nginx-example + ``` + +1. Simulate a disaster: + + ```bash + kubectl delete namespaces nginx-example + ``` + + Because the default [reclaim policy][1] for dynamically-provisioned PVs is "Delete", these commands should trigger your cloud provider to delete the disk that backs the PV. Deletion is asynchronous, so this may take some time. **Before continuing to the next step, check your cloud provider to confirm that the disk no longer exists.** + +1. Restore your lost resources: + + ```bash + velero restore create --from-backup nginx-backup + ``` + +[1]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reclaiming diff --git a/site/content/docs/v1.9/how-velero-works.md b/site/content/docs/v1.9/how-velero-works.md new file mode 100644 index 0000000000..19fc89a94f --- /dev/null +++ b/site/content/docs/v1.9/how-velero-works.md @@ -0,0 +1,114 @@ +--- +title: "How Velero Works" +layout: docs +--- + +Each Velero operation -- on-demand backup, scheduled backup, restore -- is a custom resource, defined with a Kubernetes [Custom Resource Definition (CRD)][20] and stored in [etcd][22]. Velero also includes controllers that process the custom resources to perform backups, restores, and all related operations. + +You can back up or restore all objects in your cluster, or you can filter objects by type, namespace, and/or label. + +Velero is ideal for the disaster recovery use case, as well as for snapshotting your application state, prior to performing system operations on your cluster, like upgrades. + +## On-demand backups + +The **backup** operation: + +1. Uploads a tarball of copied Kubernetes objects into cloud object storage. + +1. Calls the cloud provider API to make disk snapshots of persistent volumes, if specified. + +You can optionally specify backup hooks to be executed during the backup. For example, you might +need to tell a database to flush its in-memory buffers to disk before taking a snapshot. [More about backup hooks][10]. + +Note that cluster backups are not strictly atomic. If Kubernetes objects are being created or edited at the time of backup, they might not be included in the backup. The odds of capturing inconsistent information are low, but it is possible. + +## Scheduled backups + +The **schedule** operation allows you to back up your data at recurring intervals. You can create a scheduled backup at any time, and the first backup is then performed at the schedule's specified interval. These intervals are specified by a Cron expression. + +Velero saves backups created from a schedule with the name `-`, where `` is formatted as *YYYYMMDDhhmmss*. For more information see the [Backup Reference documentation](backup-reference.md). + + +## Backup workflow + +When you run `velero backup create test-backup`: + +1. The Velero client makes a call to the Kubernetes API server to create a `Backup` object. + +1. The `BackupController` notices the new `Backup` object and performs validation. + +1. The `BackupController` begins the backup process. It collects the data to back up by querying the API server for resources. + +1. The `BackupController` makes a call to the object storage service -- for example, AWS S3 -- to upload the backup file. + +By default, `velero backup create` makes disk snapshots of any persistent volumes. You can adjust the snapshots by specifying additional flags. Run `velero backup create --help` to see available flags. Snapshots can be disabled with the option `--snapshot-volumes=false`. + +![19] + +## Restores + +The **restore** operation allows you to restore all of the objects and persistent volumes from a previously created backup. You can also restore only a [filtered](resource-filtering.md) subset of objects and persistent volumes. Velero supports multiple namespace remapping--for example, in a single restore, objects in namespace "abc" can be recreated under namespace "def", and the objects in namespace "123" under "456". + +The default name of a restore is `-`, where `` is formatted as *YYYYMMDDhhmmss*. You can also specify a custom name. A restored object also includes a label with key `velero.io/restore-name` and value ``. + +By default, backup storage locations are created in read-write mode. However, during a restore, you can configure a backup storage location to be in read-only mode, which disables backup creation and deletion for the storage location. This is useful to ensure that no backups are inadvertently created or deleted during a restore scenario. + +You can optionally specify [restore hooks][11] to be executed during a restore or after resources are restored. For example, you might need to perform a custom database restore operation before the database application containers start. + +### Restore workflow + +When you run `velero restore create`: + +1. The Velero client makes a call to the Kubernetes API server to create a [`Restore`](api-types/restore.md) object. + +1. The `RestoreController` notices the new Restore object and performs validation. + +1. The `RestoreController` fetches the backup information from the object storage service. It then runs some preprocessing on the backed up resources to make sure the resources will work on the new cluster. For example, using the [backed-up API versions](#backed-up-api-versions) to verify that the restore resource will work on the target cluster. + +1. The `RestoreController` starts the restore process, restoring each eligible resource one at a time. + +By default, Velero performs a non-destructive restore, meaning that it won't delete any data on the target cluster. If a resource in the backup already exists in the target cluster, Velero will skip that resource. You can configure Velero to use an update policy instead using the [`--existing-resource-policy`](restore-reference.md#restore-existing-resource-policy) restore flag. When this flag is set to `update`, Velero will attempt to update an existing resource in the target cluster to match the resource from the backup. + +For more details about the Velero restore process, see the [Restore Reference](restore-reference.md) page. + +## Backed-up API versions + +Velero backs up resources using the Kubernetes API server's *preferred version* for each group/resource. When restoring a resource, this same API group/version must exist in the target cluster in order for the restore to be successful. + +For example, if the cluster being backed up has a `gizmos` resource in the `things` API group, with group/versions `things/v1alpha1`, `things/v1beta1`, and `things/v1`, and the server's preferred group/version is `things/v1`, then all `gizmos` will be backed up from the `things/v1` API endpoint. When backups from this cluster are restored, the target cluster **must** have the `things/v1` endpoint in order for `gizmos` to be restored. Note that `things/v1` **does not** need to be the preferred version in the target cluster; it just needs to exist. + +## Set a backup to expire + +When you create a backup, you can specify a TTL (time to live) by adding the flag `--ttl `. If Velero sees that an existing backup resource is expired, it removes: + +* The backup resource +* The backup file from cloud object storage +* All PersistentVolume snapshots +* All associated Restores + +The TTL flag allows the user to specify the backup retention period with the value specified in hours, minutes and seconds in the form `--ttl 24h0m0s`. If not specified, a default TTL value of 30 days will be applied. + +If backup fails to delete, a label `velero.io/gc-failure=` will be added to the backup custom resource. + +You can use this label to filter and select backups that failed to delete. + +Implemented reasons are: +- BSLNotFound: Backup storage location not found +- BSLCannotGet: Backup storage location cannot be retrieved from the API server for reasons other than not found +- BSLReadOnly: Backup storage location is read-only + +## Object storage sync + +Velero treats object storage as the source of truth. It continuously checks to see that the correct backup resources are always present. If there is a properly formatted backup file in the storage bucket, but no corresponding backup resource in the Kubernetes API, Velero synchronizes the information from object storage to Kubernetes. + +This allows restore functionality to work in a cluster migration scenario, where the original backup objects do not exist in the new cluster. + +Likewise, if a `Completed` backup object exists in Kubernetes but not in object storage, it will be deleted from Kubernetes since the backup tarball no longer exists. +`Failed` or `PartiallyFailed` backup will not be removed by object storage sync. + +[10]: backup-hooks.md +[11]: restore-hooks.md +[19]: /docs/main/img/backup-process.png +[20]: https://kubernetes.io/docs/concepts/api-extension/custom-resources/#customresourcedefinitions +[21]: https://kubernetes.io/docs/concepts/api-extension/custom-resources/#custom-controllers +[22]: https://github.com/coreos/etcd diff --git a/site/content/docs/v1.9/image-tagging.md b/site/content/docs/v1.9/image-tagging.md new file mode 100644 index 0000000000..64e9310b50 --- /dev/null +++ b/site/content/docs/v1.9/image-tagging.md @@ -0,0 +1,24 @@ +--- +title: "Image tagging policy" +layout: docs +--- + +This document describes Velero's image tagging policy. + +## Released versions + +`velero/velero:` + +Velero follows the [Semantic Versioning](http://semver.org/) standard for releases. Each tag in the `github.com/vmware-tanzu/velero` repository has a matching image, `velero/velero:v1.0.0`. + +### Latest + +`velero/velero:latest` + +The `latest` tag follows the most recently released version of Velero. + +## Development + +`velero/velero:main` + +The `main` tag follows the latest commit to land on the `main` branch. diff --git a/site/content/docs/v1.9/img/README.md b/site/content/docs/v1.9/img/README.md new file mode 100644 index 0000000000..85c071c63b --- /dev/null +++ b/site/content/docs/v1.9/img/README.md @@ -0,0 +1 @@ +Some of these diagrams (for instance backup-process.png), have been created on [draw.io](https://www.draw.io), using the "Include a copy of my diagram" option. If you want to make changes to these diagrams, try importing them into draw.io, and you should have access to the original shapes/text that went into the originals. diff --git a/site/content/docs/v1.9/img/backup-process.png b/site/content/docs/v1.9/img/backup-process.png new file mode 100644 index 0000000000000000000000000000000000000000..7d4f10d5602a3a9798f449e4f9a7be0b74923a9e GIT binary patch literal 33630 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

    Oa*AXK>A{SCkTGAeMuLb#W122eJe?g{aj^h2pED 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 literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/img/gv_priority1-caseA.png b/site/content/docs/v1.9/img/gv_priority1-caseA.png new file mode 100644 index 0000000000000000000000000000000000000000..a1940453fa10f8805d45fa04e2aab30afdb62000 GIT binary patch literal 57254 zcmc$F1y>!xx-9M*0>L48aCditYjAgW*We!9JrF#&y9W1-ySoLqH=J{itb2dJW6fTB zn3?XL{`%{g>Z;n|3UU(22)GDfU|`5nlA=mrV31K@VBmXjFu)N`Zm>o$Fobmr5fKF` z5fNeqM>|ssYZEXq$?&8USe1k!EdTwt84+QBD5#%7YjJX5Uq07O76ujvk^N8*mVrZX zHFkhjgGNVJ_-3yv+C5kJM-vTE?IYeTw8qE8eZ%gM;imwJg;lpTul?1`A@BWHH`_E8 zr*Q(XKi&A%oRDe|#L9_u;TLyBgZ-3~4uN10OJJyVVBx(<5ow#7-C%KP55KQo=?$a* z(3MH)y}iD4Mj*s8>_UK%lVpx2II^Ih2@X(wqaA_)bAg(|qaDGLzp@Fk_&_EU6iUJ6 zlHMufc=fR$smMB?L>&$f!k^NCGhPKul07UtyH{51`9}|W1NvnUNzgpWNjIjwI~=J) zfQhLoh%A=aI$|LboOlM!l`3pKa^%H{v&1pG((3C4ziV8WB1?buuLDAJ+~ zl9BNbsCa)kV>OA}b6rohADLo9c?K-BJF<)N)q+@Pe7)LVXbeI;@N6XLL1 z^~N8Q*T8waddGF%S!)Y;oTg0+%+VN+%$p+SL+sQ$nO^Tlrq=QoI$W)|dlRi|WSBR% zb1rKUN@t?eH}0F=kK9aWNqTABcnm)BYu?A0V>zt9;vgw;`51N%5JF2-Qk$B*9 zH2Z4iGPDa;heeV{G4JvbJ$}SvxaBXmoE`kfR zzX3uQAELhB>EnMV#up7K{sHq75l0B?2SJ}dFR>mKc7*>{9vKzp2O(y2@;XB5kl|cv zb5s@77bJqvalw>a!UO(URAdpm+zCbmif&eQJob?NZlqLr*lxuO5>8BAg#B*l3yP)> zmBFapbQd`H2;Q(~fqFegRQ^hWC3$}p)Tm%0f)PZ>bCnc6%v!3TmqMsUT8h>ev?w$x z_!OhhQnF&nip@-^nR7UUapR7OF^K&ZPsuHtgq@^2a5~UB@cm2mh!+wy)uXPj&15Zu zPk~vBq#Rb#b<)dfG;VaJ55sg`tzM1`f;x^u?iw(fsiLVOU&vaZu!e1b~XgV+>&=vc0tK>+(m-O# zaKo^{aN02WFl-XT7u1psl_*tjRd;G=Y8PrVRhVL`Qt%|PU8Q;DJEaL_SPS)Z_-I+_ z0?ZkdnHGzJQtDEaQl`=o)iPE2(%X`}*{Hb%OB~CLxtP*b)hAvFFBB&fzwrUCXeZn8 zhg|+|tI}D&b$_n?^p$w2h0*E#MygYwQlPqBO`$oX?G&)bJf@Sr&nB@qz1K3fJ5HDG z<$MN7r#_-hBWx9{mIYF+V6q{w@w2JEMLDfqBIUN^*5$U~-sS$~9O6>aLf9g_>bN@7 z!f;UPOu-kZD#*M-~!VST$ zWVL4W))!?o#DVJcY2~?$WHrDj z&9Q9Z+KbQb#`-DsTZUevKV2qm5Ec)eCoNMleKN_2=7=s$Jmw7bU}<;hy5<=70{42$ z+~XW?uLu0~*L|00y^$HNnq*sY?usK>F>)T8$Y^!RD6yVjusZn zt1uvrB4}VcacGjdpT5xhr2i?1p;-5Vwujx&MD!aOo1@fD_n$k(HN}hqLh%gok$fH_ zDyDFz8zv${AHxyDr>(vIiip+^zi}9%?4-NM`^ZkDv9%6b?U#GHMF;zO`dTApu;}!h zEvomnqWURQ6q4WLSueM|17Oe?wy78qk-pC#8dt8^>298LR68Ax2e*eLtF%vjx)V9 zrIm+T?1Hd1)tS~&i}UidqSO!LoR9JJ@eL+3Kg179M^$dK$^`~Ve{t_`*QA;ZCetu{ zqU%hz%5G)4^T^g)lc$-aX`^sL4Ski?Hm^Y6OlZ)9dCvG)+Ann>v8KeUc4kjUt4d2$>nzzbnUG4EeuM^Z0k@{UFjH4o<<)jM z-X?__pW4-q(l*8BybP(*dfA}KXe5R~^?NDL+}fNhn%QeC`KDzpWdQ6vvmdqA2FQZ%6m%jTF-<-XUEg!wR$lE)DFi!i*wdRi3Tu}EI zvVCB=!M(y(9$u&qN+VF~nT3)SH9thmpP06LuEYISiNF|FLL+>u=l+9~bW=+wMa0?o zW#%Pn^HZxxyJIdw_qN*AiuQ+Z$9i`;lRSz(N;f-u%6-bvzbNGPvj~sT+@dn2_*f>J ze_n4cHq|yx&~eWD@E<()Bl>n!d5JcXYqW-@5W+hCKPfdAf;2z(tNHBu z@pEe)Mz3!#hc2x*)4kHOqxa7XqT#n<50l5E6oIzaCT?5~?t9HE&n?*@t?0(5b*{E! zd_*FzEB@=3`_TMYTq0C&L+_(U{HKS9`x=HOh9teQjs`!($6Ip}Cqv=UIQt?^@Q=(J zi66n%P9z?;-NA}Ez=-KH&wl@cO83Q(LBE&hT@92E#{hF_2NSITBfiH4<0<`-jCCn5 zvKwJ~uvAa=L0 zwsqom=Og*Y72Lr7`(XwW;(uJ?Y{f^SE~`K+V&`Z=%tp^j&q%_LKuk=`>u7Astt2Y` zKgEIn_(;s1o$a|97~I_4=-pW8?HtV*n7FvO7#Nuun3?H-E9jg&Y@H3<>1>@y|5?eu z>Jc?@GIF%Aceb#zC4R5hz|hXcnU93zy`g{q{qsIe+%5jolC9JKbPMPp!}}QqCVEDO zf7cBZ<$XWOtzhA9Vy!M}VFQE*XoH`Nk(2iy*Z-e0|7r1`B~_hF97XJGfP&8a|B3Ja z6#nm%|GVHnn$-ADldPQV|J~$&o%x@VybSL>|F1~=GtB=u3dEToftTUmW5$obt&J!I z1||q5B`T!i4t|ma-GtSDHwX(2sa1`sh~5f`*@{TZSS41?qll#N#_kH6x&TcTI*ywo@uck&f-ia2=Tu+G-5`n4GzC} zF!&$;Yx5@;42VO8_&>)5slXAU^3>hqpd|hO*9JkF1O9>de{BH@5FfEcsxL_XPfcLZ z;{1^R`-TJ|@}Q^~Lku^?{%=jUqW{$a4|sj8eN=(x$+pV@yuGsD9n4wWF3hVfE-cq$ z9C~ZTIQ*0W>%LE8-cP5ErlloC%ro)=KI046nNX6s73k)a$Zhi=gt4oC_xz*8##ST) zdnl1_)5GDf98J-dCCjq3sRD_N+fy#_RR}p``sd3*A~ngNNw~W|q_Nnu@C5H&A|n?} zn*I6n;CCKe+ufSiUH~j1BL;a92h(+d%#+|S4R8o z5{-(w=s1zr3DfnI1QDC-XHAE**XL{bf*z8Hhh9{H9csEgoJsaieCrnz0&mzm^%dN= z;7gWc91_i3EA#j_D%aX5MEomAxU7GkACG_MSs4AqE~U%jabu69Ye+p@to@~7lB6p9 zg5`4=gdxW`Na$4_?HVsd!F1mF)-fhAbvVo10a7obeV#1oc5(x0m@T46g`yCQA~Lk; zW9c|g(N1Av75Tlr?sYt!(a`con^!jPF~&Qc?|O2tvCfEV)+{KmT(0y84aY~fV3u_Z|3z(hrj${y2(KgJ%EN1F_*vn|%wl0EOfHf~#vMlfj{z^>%4+Sp?OHevo@|vt^`Ngg;486M; z;f=1874xCx>8uq|z5s;YFu+`BLPc$*J71|obA&aK3#VdHK)vFZLa(E_Lv#9szNg2n zosBg7F|8g37qddhUe_!_4VmI;`mvS2SVWO zxmHiYJoK#PxN$q|*20(IiG1lEjZ0>~yg8@3i!B1-p|mQSO+E%M4ax@6xpA zbUoP*F}j`pPkO8wwk;)GPbaln;_4fiQTnYYmb7DM(}@<(_m`#{T>*Pxq%n+*D~{5& zpH{u^R^7z82~1Dx=Nni)M62iGm}OQcT5m4e0A2dx(6}pU^$^4AsHJhs;(rr?%J=t2 z&L}V@R%)6S;_$$jbJ%^(>-y4`Vd$kk%EX5;TGVM4FztA$@?WI0xi<+u;Yw2X_-^j{Qexf4y^UhX*b zyf4K>J_(tQmsO?&tNs~dUmj(EMe^4NBOf2*y&g4<_)-*h-Q$G=H+xKAQP;o>*`gxE z|FDyQ@TO&5E!J=MYVHHY*frN^r{@Hu%0iD1Q0ZQ_*BMiWU5CeB&zgNNvafpddgr9L z-1J}YCAbe*wNr4crz=z}y42w4qTV-Xhm(UvW>xcv6zx)|M%2laZIWqX+R--s(n$@XBI zOgnDCvGKM%q80D*Odr^oq_KgqIo_pxvI$^LFj~QZ|5y?qLEH9tREBv&ADE2ewC7>y zKbWw{CeXnKQYUrN9wTJ5Aj(}F8G7cR1TXzLJRd_(6u`BY+8e`Y*dV_Ow0#gOcvMdk zt1Qn~pJot(6A@K&=1@PtEGD$-`*ezIdODkT@+-%;h0VV9sI=_JgitD16}?zOOM^nt zEd-tV7cdq&X+unBFrUFljSXwNCXiLWUXD#{U>Ynb5#_3?sN>N< zK@YI2ZJJ=Z6{VS4R%fFu>rTcAVuKg6lm$|wfHDa@5As%i$>v=wSi;v8HB8s%BbO~| z$1YnEEn`7DLAS2^y{)deF5A#OlQnOY76|hZtwVBuAPyKH6%44pJo;(jRCm#Xn6YS@ zF*fnGY}c7Jl*uiQ_gSGUS9lQxhKB#``8oo9Jb$L##3N$dOXJdjCfOueV_?PWyxlby zoZ|lQRRYY)znY?E)QsiW>B}&y>sU!~5MH(SL#NCq=Me9_lZr*QuUZ;p$0R}hB-a?X z&&ED`Y?UNEDL2+uYs77HJ-~Xi0dpB#$M5B)Bq5j%%6cRpW!b4PtC0RWO|i)0iG*>7 zLfWyM4wg=F`0*BRT~@t84c4mJVBM>o z$hEKh=Q4k8d+}a`v8IZT-@|SS=+F95*>Brry1#S^l;%WvEj;>NVAq?WL<3l3W71Wq zXrHR0_>rfbuD5*!ay_H&2z@wtt=v(7M8|!ySpYKZ^y+8R^OY_JhWU75%BagC)HJrE z{q=Xgv>lf!znY@{gpk>Gn+dUv%NN?ZN#WactDnNX$LL;O&Ev@H9X?)nYA4OC{@JPS zcQc^--k1bA?h>~Zhd~b#+Wvv?3|rrtL`rOlDw%m5H=TT5*pbf}#LI00SAR$`H&MBE z(7gB#1*-kOp}*!w(tpKeO%Onjpq?Hv{436%pPhPtCpvlC=U75XP$uIcg+UcLg?G|T zl?87_My>8=(0M5*B{7K`QJmaJk}Zu6!MoVee>|BdB9;}jOLu~MMY9AEr_uiQ`qZvr zmc^#H0wjjhPIi*VdfXo*il(VQHia24Bmvx(N;?0P}#~@?A>A0wh5gJR=-MP*=xuMGA_+P!= zZ^te$im}k;cQAC^8>0GrOtD*NE?)Mm0kPD0+vok4Uu|A(2>?yPyg zQd7-)rs>>LrGo9;r5I^CP7o17gGWfqraoys^^Z&dWvL6@77DwlS*Ak>5k+~P800QD zhxc@d%by>g)41E25jT7;x?ydv=*Cx{a}8v*2t^NoIg}8!GH?ZN6k_%0AiOVZ8uZL* z{Fh7Bh`yl1R~>HJ0YVUw6NmoeXU1W;;vBOoe+8w65fa8>&#VaV7MCyLS-iZ6XKZ{D zSt?FvEJ-)bi8yzYf4gVut&1NJ2*qTXW;JU@snvx(-US*E8;kcNZ*I5WtywAlZ9A8Y zgj3cw-)Z>JPZjSn$!%J7*5f-u>i|7Qa;=PB{ru-?bRRkPIZ=bZFSff1nPa7@-wX0q z@mzM&PZ5H&+|%`BVQK=gypYo6K4!x!e_~YE>l72Gj<(3-7%#bs?afm(Z7jYgxoP(j8Ur;@l`H59>Vhpu|Xg|lVG_z%drjS zV6G@sU88VP|M*ttq+AY>NR@ML=hnIy{S>EvB5OWOIrm%r6 zpI%#$!*i8SxXVFj>cL@q{2$}k_CAgb)Ww*iQ?{9&M{(1w=eHdmroJ1{9?Z$ zR17{!67_ok9>>};ncVCoO%LB?A4XjFuwnYIj%ot91*^r+oI+3HpdXLBXqY;!vLB%& z?*l>7iB8uaK(O|`NvpqvG;MwBqC7MGULW{0YL6!1{Qgc!sFBi8DdZF6TJZtH+K}Bs zTSsPVMqxn-haARE#Tl{YWuBdnOQLbDG_?={lN@P#Z;4E!VyF`UOQ5{i@tNAvi-5o+ zDzn)n01a<<<+;fqJ{k71#X*H8p{yg)^Xrzb$8J)@)k)oqoYZ%Z-cZ!vaIoy}gEbHU z39Gi#iaSFxoG7NR?Yd}EA?OSsW{FZQ%QgDelbQ%-o5odFPny36{K^{H(r#*+pi+0V zKRx(CJfkGd7J|s!!v=n88nA~?DfRr-kk||RE=j6tmoKCQaLEqzBRn5I>8-Sj+tQ4f zBD?TbxSh0XjU{E}v`u)gU6`btXSuDFq$WW+X%rAf5J2+W!&tmD4U#|)w4Qggl0*9fy3e}#Qn1R-Oeucfe>`m#1fs#2Cw{E%orH2XNzHsr~=Kj8rP7|ndYy?ah|H+ z&<`KVTzs*MC0h!9HR*-W2(D%re zW}4ji1?ihw9qn}9@njo3dft^a<5;(*vu@cn|LHvf%XYbz!0c~e>^wiwQC@K~k&yY*gI>?` zurPwEa7N>j=U&7Vt{hXhy%NjwrjgJ0^Rg8BCmxdz=~wFJX$7roO}t2^?&VNxni5wHWEFZD6bvo zZooKBk4kpY5J&1H8^>>`R=4kl#(0uHQZ$99x&78RaAA>Y^%Zwhw1P$PqGmXO>JnRi zfkV1{?}6XAVNL**ryKOiuztLgT#fqW+LSY3@f;u>mMd_o#H6k47aT0~Vv9-vAGMN$6@Jw|Qc+597zSBUs9<-___2>Jo4I98eRBpGl|KZ818O6&^ zjF0k2US;&}>>1?#o4uSda`GEu;HQNG+7hQ=lc4S=?2}Bg=rUTr=vg&(++)ps%2O61 zZ;I)EOg3@wHMVL=!coT^yAxRTB8(e%n!n)z^chG&ku!2Ubk6Ju>n2ReyJnPB){h)J zKdp;wH7$QvFizl~tiVnL6f1h@y|)v-kFT{uZU~g!CdQYVr{oUFCGx-gY};yojeYnT*d{kWs4?q5w}%<(_#2Bgr)V zSV3AfKoOLUT?}JTyPBJD<1UUAd7qLHWBLT{9v=xdn{|&dU+fZn#GiD!E6G4jLx%KSjZr#m~eaxd`X;|Dy=lJ=j z&t4#^!V>VTp1{O58G#`UDfq5Yd29#gTZ1*V2Y#AdE{md?=3@3K@Tj~SB5`b1mchnn z#8qz(SdrF#XWHsMVSp(#Vfac43XE3&D9=i|zn8bvybN2Snp32i{r!%qDy$=++@-Fu z@2le^q94Z>fOwSEbf_P1NR7Hp5G9RelU5+@C*n>q4C)ylc*w@x=p)EN8kZv*%RbRK z@RDWjJ@B%f#PIIx5tEG5mVviSszJGo&aLo3KCgde#BeNq8^q)O~#ngc6ZfMby>Zn6L9 zS6_&lijXXmUKzaCj{7Y{7S|K?RxrOVe{gYjB0Ek4^g^tW2;a>>%MY*7)RfpRD!(8S zUu_3SO*NiwPmj$AZm=ofT91l>H(0~V{X>Ae5*7Gj*)U3%U}|WtuAKpTu!z|po9O3c ztFiH#5KODT+LXzG$lS7L>AS#+Vy=|G_4V({>EEOJatWaOAkGMsdC7luU+2yw1rgu^AIhJ8#UKcnu*`WI^KE%~F55KXEHZ$g zp0FpU1HhuYEfL-(5tzb+Loexg8e_Nv1k$kebyt+=SG2PX9!u&e6C*Bc%+ z#>FZ~^X+FkJ=5yCt-9=`8pT}t^)|&>Bb>j5p@ zZ!Pi5#T1oVN`bU8Sj&Agm?pmau?-**C&pP_52yPzJA2JrDz%g*Qgl5ncEG0p&dC2> zJDDO#``x6t3p*`e`&#|gx#G(8pTn(l2&DWAR&(uxSPH4UHyT)u_mvbLG)xMx!Xc~$ zh>r)wmfFawh%-%}TRtvI6Bd{FVV9N>`ql+-c5i8b&V5FpsYd&lC*`o8<(3Z6qoS4P za`E7|7V!%CIW=(4zd5wk5Ta6`jzqzZQC)(Vk9(|;V@b%WKWhBSFZzlVBAm-yOxi9K zFqNefu=~H`$kx&S&k2=At%*GWsD4|JODLF{M)^J z0{auIh53}-6jX7%CadDWJR{UawIb}z5881y)=5n$aeMna<9EvO6jtt`3Bo9c-{-^= zs(sMN0cz5ri{I#|O70_ztzu99V_84?lg%M5Ocam4bb3>TRBl65J6tYk;Nxh%@Cvwvx)^I{}>{Lq+rYTmN-(0$5S{YvrVmn z%5-k(=cF-}Yo)*FSSr!W@(YS>I%0!F5q@tt15y${iOE5L6v>L7{ zx~#HQCPAcpxhGUZhZG@7g9lkXXD`O_C_9R()+#*us95uqZ(F5@E>$zu+ z;G7D<=(b29LhWD1&{%NjQGP=a`{Ep-o0D!>BGz|86(*dwg+MT>P2>;lvWbtx@c6gI z%lSYT?KE6E0SAvU$KNMc20GQ;#aulI7Np$Y(1Hx8M{By9)8QGHAW1dnv=?Q{3UluG zVcv2F&bdeVO!D}fjv?srBN6=KJQn*|AuAU0j z;7SG<#3QpWbWM<|1W#7GntkfF`v*k7_^A%T$oUhcM~$>1F8DK{i>1ENyNH6#HRx+3 zf$tEs=rXT!YHe3nywj1nx(V7+68b&XViGSQ=~1GISE=N5V%dOhIbDBW7@WzMpnW=1H`!h6&wL|9QM zzumhjC4!OLwJ?iu2&iDG9;UQDRw&Vmr=)nbu@ENNF^TB4W-Pq7v8$Rc6XjDzFl?GE2GXJi=CV9 zlN()>Xx{ruOp-tOjrq$k zB)ZCgcpnh5Dy5h%tc!d3jR1Z!8o^W=uhs&o_y#4voKF=2OR(Bi@efRSE%OIY40>s#tS96`SvU&g){qt}q zErgI*cAtXak*G-+qT_$((rA zq@+<2R3rG^e6I^+j`>w8brd-D#8o0obfsFIH;3 zn)y0mKMl65ZF6KV+EusQ;c@g5C88)S)Gp=tq9g2cDT3eG6E%x({6n=-2xzGXPXHbPUfBJ|+F+bFUJH_U=>m+x<$sL>=T>sdG%i|jkug0^xT{J_DCNj|m za06dj_uoEq(Sz16EHsLWPxFzU63=j?a)7WYnml5hC8kfyB4DvRrJuBitjBKG7aM?? z=3#Yt_D&0DtJmL(M%Qi;!hC{7B|I7GD1E)DffnCU25~(dT08}`{ZUhG${J3(88xmr zmmu<~6I(RCozOB(GqG+BwsWYefPrjdP^6dD9X^{OjH72O~x9n25A~b5`p{(9BQ;?k9j9&;XcdX zlmjHsHA&=*!z~37awX+iSEBX97%Z2B+%%-(Q5VK9d8aCw4g)xA05Q4S9lNI*>p-A9 zYG`G}mr@_Yj&0gAE5G7ZVzI;50caV%p6RxPV@b@ZrWJOh6vMg$bi==PkqAFijl3pS zOU~>Sj`AZjr?U&#Zc;I}uxd_l0qTr$N^mk%E&kHM5N??8_5iHR_Q0sTW(r$ddErIh zRSBh4Roh`v#`r|w9<+qBI8zyw79rWV8tAzR-Zz(2}5^O&6x)mu!F^WFY9l@vs^mG5xqleRx!Vl^IhlCMD z0TV>)b&A~ogmR9Qx~Y-m(i|sc>`{ca_%6AIS5tN3OX`6X#ES|za=$}wPF2V+YAM-C z5Hcl%#~A;T%cAaSxy*Hny2H90xh0HqfVTU169cvF;>Y2rb-0JIFIF33*$KihRhe$d zzfQPGoA=&F{(X5+eQ;&b#<_)852%2T&-H}%qC8|)CvD&KeCAN6+r8Y@8DRV@H&>nw zauIp2T1ej6ox1iHKH7!+q3|5Ty>^?;j=n=JNKWIR@h^} zGu!%3Ge}i($E3O+izWXxm7S+3?AbV41PW6NMV&hsnmu(b#310OOUTRHWLSS#TYXkI&jg|_X_v{P6+N-oRtpPHw%37ph^b1!t12OGP{>#u&#ISs_+1i-X z+J&w5nTO)8({^8Tv3q*sn&-$_LWCjDBYbRE1{#@T?!fkqGtCz~w+Y z*lzLzWH_q;;vNnDq-)+TY#)LqEAcN^&tw}^Q-%07h`>n>q8VNv4+;|tJ*^l`Nqtnd zzL~VExP}|f+ajxVS4`>p!D1%Y?_Um^fF<gDdbSOuRE^f$b_QOHaAejgmqB1?6IG9lQ_sOBY4Ze`YwjDZGj=xd}#_m3r`z-hp@P# z{&7^;Q)n}1paU2r@^|9^=4=(p2l1Z=|GCu+u;?{bw-=^6vL9(+v(SUlcxJu5j)b7> zAy?A0ZKE!b2-q>U*WZJ<;hvIY!inkXK|(9zGPL$S(tp{P1Tipjzgl8^BdnE!(13q_ zzMkl)3gcv7wq`+y)G%o1v1bg9Ii;&?yZTxSyqYMeKf~a~@LoyskV+Xap)-4u{L>y4 zP(<8?Gf-4aVHyt=LGJHjDiVTJ{X{FoxnSFRda|b2U(~api3omz3=jT6!a5Hz zN6XUtUf;+Xht|00QZ4B?4U~Tm;2WHR{!YPvxugEaR)zM}zqkAUi&+ccYP@0o9ru6l z2*WGrBlY)0{$JbIN9+frpbx2>4*4FBhrh<88QMR3=6sUMWkcuOO;jEQw0Da&z-k0i zDOVclVb51-i)uqgzX#OdQGl+oe5d@bXO-pC1YYk8kdydz6g!{Jc&^V{&*K%jt!20I z*0%s~x7-H!jARq`8{(&KZ3=T~&A1`%?cd!3?2{>az80q09(&H{lN|7@@B#bTUgJpE zj9q)KfS79&uRSl2VNH6Z2Rka{gmDiTg~ovAi-|3eoDtuh&sG-StzV|oQj`@<<3t0r z06((_AT1_HSQJ)Z#Q_ml#|#13d~!ShSbXB&wVR^1HvDydr9bbT;`_#SmkRBE+5c$_ zSVI}T@jsogupF1yrUSS74=&hrH|ZP73zbLq{@@G19o+Aaxig!NVzank=+9K@@Hnly z%%?4y<+NA!|Iws$8FSA1&LaoyJnz$sXPW<_Qb*+pJ=XLI=*FY$2i?uyrLtfxzc7tcC+GtRG z#Bufd8t~sN@;(F5HvF2|^RKzn%gua7Nm2T+J`4@ryg_fsBF;whe)ck8XQOgxiQcH5 zdK8#TJppE)n*O`LZw%<5O;NVzk)@D(ryA5wf&Bbecx~^?{%r3c;s>$U@Qnjx0`59! za0~4h#vH#_kI1JnX9K|6bTvX!OX4!-nay2dHE{f+$r*_k{p_V>*&vhvVWJ>0a0{g~g#;DrU)NV?df z>f=YPQpbOHzFIOmhO=pOzP+}};uNr2l~;!VRFln#d6T#CsY`MDyH#WFhX_iE0g6>d zVwqgoZlw)MltI={%GZrRRNT?9G9b~HM}ZV77eM6F1?UMFQV`1+Yr^mEI+A@ZF*^W} zR$Z&7u$(_42WHg-486^o-`57R0L?xE>3uly(L_G_b1hnz z4E{=@vYbVy!~M9B6U-7^>GZ<@o?U81PuDrFB06Q5QAc^mN|2F6e@dGNDl0EPn9z(*Tqw0z1>`=Z7Z>HNYx# z*k}#G_CY31CK^s(%@lB?T}#aDY;W`ZofILgY&`?!@FjiQg|HpqRjlz9s~>JZtuI;( z1VXoj;Zmc3f*DMID52=0q;!kRuBjn%EHF5YvUFWm*-|?B43z6(Ujs~jn7DTBq|5q& z$kxpuK#QKPnFex#N^&XLkb=tER5&WhJ@pXHpud4h@$$mc14`7qapDosnO8~B6;J;H zHbPD>y=62-J)6zngrAI8>&E6YoyIj{cQV+=Xj!7DP5?)tm4`^d4xk=OYQJ{)JT2Lq zL-Idg^!9Dy_8YZ7321P(y*?hl@pMSDnJ)KT5TQ1*yjIA z%(L=LZVnngz2iT8_l*G4f@t<>t%|d-#RA|py%yw9EM}lVdHCfGaBA!UcUA>%u%JJH zV^+6M!dMhbO9GYsa=AUY+*^3Wq7N!7|R-JMjmYpT{}#*HFhFH%(IwLOe|i#(qZggW{@q9z;i> zE@p%QyGiB*CeTb8AcRGN2DXTIn+CauV9t}ZY?#ATKZL!Q?@h<@^6l%RCwbplK#Gfog`U zo4`yDz>Dz5R$xPJop zwyeH9oUd2(bX3!F1t7s_o=e^~n4}2+-zx|S@ED7eTk_t9-60&GYKNvVn-f9lpil5a zJt`K672zsp6(RQNV3Wi435euCUcU=wUEhr_8;@d$ z3ufvvS{N_9;lnMSJ{|w~p)2J9k3OAK>WK0lZTU=tL%MkVt9fjxDY<*`u{`$U=yD%RLeYS+C@A&E#(< z!)ZYPW3&IOv`iCpKqCwTHA##A^SMz8i0NI#r;Mju6rQjpq8HqS1Q|cj&mzUQ^LRsd)3~s z{0~E!v+%0cYx)bTc1@ym)bhQD!kWS0Mr|@V9i9FXw>&UhLkSXn0qle>*aMLBRUl>4 z(DtT%t?LgiZ2&xbzV8Q9O62g;NfukEACkm7H%%SU+G8!R1%?S`7=4ambeTUOx+u^M z?mT4_?Xf)2C4I+>81jp+d!3*24e+~PagTVd$QecZ6L^YfON;*R(BeAFff`A&?2CQ+ zrk8;2lR1{}HZ`D>s9fU=0FUC_3*s`nJAPn&t!I>KIB3c*if~wBKz{%R3*+7B$kX}}OJbp({TB?`*OnN!`)z&gKD2wE&%y!9?W=E$(l2Y7|os-ZAh zh4_eFM{ydS=4)yLnx$Al&6eIL@R{)zVw;om5FD{6ZzB*i3%~j&C&0Lp`fgIi2P_Y+ zuA1C6ASI?X^fm@;x}@<&9eJ9)#naKBIbDDa@Ce)G`yvMCEUYO#qgEc){;PD4e)5cq z81Iw9q0hTgqiHd!Z`jVbMzB~js2_s)(0%PTM4r+l^|bGi^P6{~At$^G-gJUVB-BR` z_~C`kGKD@!U8V!pd*CeyT81ggD954?p55K`l5q2(Tc#d+v+9(>RCRJZ4Cn1$(rK;# zI%*&66eC-C7ROYdkq5tyENlPakJfLlNDXxS2~vEJh{Tu*`EGo@HlAHu^{O-BLsvX{ z8;gR8YdsjF$abaGXgb!kz0P!vMJnrSJ?zPYPjajO2cP=@Zz?gXS!jsIe{6(-Oswa`EN zgmYNzl@!pJxyYDMwLJdQV?PQ1`{-N%h* zhQ!h-?@<37Kd5&dFzhe)#9H5MN(~OJrNp>JK21w{)vN|k65U65Y4r9dLBogrINtv@ zYrv<@@0`Wz&n}Sfdoa}F1{h8~rd;Afz0_Q(h{YeB1t!F>H2MR5MeAo|z&@T$FeztFp(a{MUlar<1PE;uX()Lgh^zm7wP)R=LAO zPZBb<`ve;yZ$EwJ`UY&8=VZ=upRxJ}H`# zB`3OMvyaP?0-~|X(?_-B@^#3@50gY9I)FEI>&zs3J{(M(?@$vQ+}W@mgVFtBV}(4z z%D7YvW$ptU)}IOPpk6>z1r4%;p`kL~p9t3s)h23EIBPv+%+xmIk$d=VD z&ncH_l7OyY&NGe0&+utJRo=N2Q8C{V6Vli^bqioH@_UY4G&;8iKU-nm)cDQXpiDlX z`1bk8!^_F>-KYootB#S=9J4LL}O%$w0#18x`WcLcWS6j&}xu? z5ap5WlY-0*t9bOM2IY@g^r_8v`t$3g{Y8{L@)M4&P~5tP-<-p=!J%ew%NYG!i>ULc zlm$C+x}W`z1j9!WylURegV&j%#MA2UjAc#4xVwowvSZtBa7U$Q&n+QHXSeIN!&?Gz z4JMl>9NFl_l4!!a)ZfDoLvQi&DNbUFu=+fFw87JmhghBOwsDkEtu0goT0E`)ZhZ*w zPSCQ_A#EKvVX`-U6HSY=R|0t~adJch91;+FTIuZb0Ikn0^;okHhez`IA1nml zs+f!QO&<0}sQ9>hNK!kpyop+;My|U5r5I78SFAtuc%cK_Qr}*|CrI7IDiEq>Azh zU1-2>zV%%v-7JE5Rd|LE8dSsM(*&$`;9m2lVc3+5mnG7CVDA8oNLB$9>+1sOnsw$xas|h>^NiQXok#^)oQqMEb4LbqJ3|7^%+|wi#oYk#}`)Y zSG>1UzX-Nzv1^+p9F);sx0bydSD|tFMUXE@wMn-mmFw5`k?KLC7!F{G94N;_= z1t0-?k-*D#O4bF&s-nJ=r|{F}RUf-+J}-bYv}j-Hq63B*9H}Av(cFLueLU?1Z%kcw z4=eXXl&Rmf*X^m-S|9hI{ChqD=rjK9MS%(R@An9mj`U)UL^dCVg5d`L?AWDMRK*ro z4V`YIrB-L9wdQna$&Xm_S%H2_VqUsmr9wfN_uD0|4ThiAvr^1b?<3CrO@PYw}#XQk>3_YDtz zJeSmBmcgnux_$!7=-f;#2~?PRw-%I{sMUZ3f?RSOAgI3h0fy2*BfLD_d59rxHI?3! z=?QO^Q*pPN`|2S&TjH|G%VaHEWW-19OJDIE0b4>j9?RDJcTWvhK=f4QY81%}-F9a^ z)(2>KN7_%Al86Tb3+nk0;Ea%8 z%S(=e{cHz{AgvZT%0~HHm(6Dijk9(AZ}iPZLN~E%UAhn5ja{lne~E37`Vqw!`Z949 zL_9we1K5v+8yd0NGpAT3lUTofR4=0^geWVg3tluoR1X*ayR{TD3?Gv{7kyqfad^J&~Qz_3})MtAd+MUJB(TPLSwMxG&B1{=C(4HDqvKi^5KEX0$x<_fAVFhXJkAMAnO+g>?{s_I_ZRdh){uN`#Y}(8N1x2N?#8R4U-Wn-V)rl@6eq{vsktl_lvFY9pgH!*!);CV`H@!)cXxeaYrJ`w=R+IYC2Ci zhfG?lbJUCV_ykpD93}iHm1@?5vV_9>DS53ig$DsdCj>#>FiTB>TP;}E%Ng?VNc`6i#4k6zoU~R(z+g^R<5FQRm z{B%yi- zm43FBkvqUjsZx=@eHt>rHDg^1FW0XD_feC?1*3lcx7^p70#V0H^f0v9k=x%6thpj?lo zx4w+27FJ$THE2iD8qxfjTjMMu=5y;2aM0C3wkeO4m%PcbsT|izE6dLK)KX&|0Buwi?}_CJGO3)V z=doNib=(+|Xr%;>FUUsj)XB^Z3CY%QUB@VQncF{RSuhs6G&Dmi{&McF>Q?3F^78_w zi=NLt8Qi=5F@l^s+x)0#AFK~H@-{=vuBK`@Mm7C=58Y!li=|>%p%1`3$U2(Gfam1F z(s(|uqm!h@THr>r;Eg~V^;$2^0iZi=z{<7URltB<5Kt?fNBZlrTpH#oQ<(?Wib^~0 zHDCP#{Nb|kgK3Zk>FX?{lLP{ig+v#bwe*?TF&-fn(z;D=+W#avnPUibW>?kakl^Lip4y~pnN zx40?}t<2RvJO6YrR^#Rk>{z@~`7wnnt`K1a_pLZy9ysaF9##hLi|Z$c0%)~;CsP6K z-z{_P-XQ{O`SJc5=k4w0?6yj?I_n-nd2xeXcY#9<@N#=M%yPKWp;Jmd+MAR2c98P$ z`MstO13kWP)zGJHSB49sxv4+Vh77Cei-}bMnz3%^ z1Y|9+10UonA|*d03PDu!x;dvGlBZAYdisRYt+oe+^Wt zG5k@cy@pq~xM-Jw*yQ&Ui7lWY=dw8Drpf*er>rPK@@ghWS%Qmc{)cg7DF3D_xuwph z?YAn8hdNy^0&$(|7U06RTo$iU9%>9#K13Oz+ke4NBPe^5X74V{Nw*zBN4|@YFTIb} zQRK@wX%Da8m65h#Vhn!W}RT4`6oQf5!)r?5&6ML{>ux-y?4 zhOD#H0?WO2|0;&d7p@O$W-iOrUi&>3B~eBIOvXXaef=!5w+;!~_)~!t?Luc!)Hx)y z3|*9K`S+w5?@I$e6V$=b+)0%~JzIS=k}KhmA$?o+pLo;ubAer#1gm)>LKLr660UwM z3PgV3^4-29lq(^g2Qi~ez##S%bWSyn5?53y5{{99aCsEFq+Wlu_!z&{YkWx0z2iCK z`uX&{=gT;B^1}c7fA!2!rd$^pQX>wqcm*dmO-! z!toH}3SR%2J&|f3C|v5l`E%`nI#t)Q*!W{#n;rhz#W8&SGTPEaIKixR{)OrBS3mf| zXwCboQYjv@O>Cb-VHy{BrPLZd+r}xzLSz9OPF!YBfZz+=pNZSu7Nnol63gi5|Jn`_ z5M5hyG}1W7+2Fk95f`3j;2S=)yz}*Hy-__E?+99!`8^TdZ*LQC>nhUx}8;0 zXaei96n(L6Zz46UrFECXM>%}$%yW*zwP?wgYPVo%xhFG#1e%l@UQ^i}m&9QIpd&FK zP1d7No57{V#5gCw|6}RYyRgbdzdDk85aP*UpEjiD_fLU<=*$ou_d)!lmjN`_dyme$ z)lp+;xFpR+I}Lms8~r>#3kzYq^=}S#J{aR{%_nSQOfAScoL-(Dmmo+8cw>DMe>|<& zgBD^c5`YkB z>U&G4wnA`R6MBlb`;mpXC}m_!-B?`&;Gdx|4vo&Z5ogLbljeS+Y*K|J8x3d^g9(xnoB>lQGm8?Q_|{Xs4=N zR_@7nEkU+1^MyNS#1WX<>>K(9A0vxyfL?B*3HMbmGQ{Kk=zAgG?>ME76r7(~!|Fsd zx2O^ux1`?dt6}(NzSCGjr=qui=$k@{f%mSVmylRYv8uadX;>LcaJm^r2+d6`(9Si@ z9o7h&`BDwkS%G}`2-v2hpHdVFxG0$J52Fb6Ilhx-F7AE%S1i_JNy}BrkgK+@&;nkJ zrQ{AzuPddXo~=(g<~b(xSe2^biO~m_h5^`*slWNi;;x)1E$OdKU8fmr~1O6 zoO1g9&`L3!O~nRP&fZ|yg*ZP{x#b#8_H)7Gbgp3_MM%BfZ~bmc4iIxRZ5E(CY%36j zJgpR&bni}-!J|L0DT^G*w(aBWnp)Ut;&E6D>^*QdJu5iQTgs>E@Xcf`&Sh3Rq;L_t z7x!bemG;gi-a@iDo1&IkZ|p?VnusDbFD~cRub-ZN#Dn~hea<#UG}{9HBhF^lUZkw- z73$%&&`(b(8Rlg)=NIQAMFzFkz7`x-*vgJDW_rB(9N8*=ATor!`9)1j)^A_K1ZDeU z;}3WH!ek`uZUYabiJ|64ZuiT#Mlvel5S1>mV2mi-KF+Y2Y@ct<-{SOUv`F82X(UI& zZM8{$T%W-Ba4nwFwtlPBVtQUEP2eI=S)`>o>)(t4k1Y|E9dq2ipo_^ejr;ccV-QHM zl-*KF`H3t6b{87)gb(Na5b!IXSV%z5_Rjd$W)3LItj-9+8{y z$pY^nT6Sb*!U|;rX+?#+^@!*t#P9z4lo8bY`YdN5t}K>a5|3l}G=q^N*7wUNVAaEO z2YIXduB!R7Qr~H8O$haWU6QVPznMhyo&RvG0ybr34;LR4T*3$K=Uf0hlK}jos1aan z0-anTx&M@&?r(phmr@ABpQ@WUZ7TtKSjbl_xxv-@0K;O_?|hB&XlN1D6=c9&k2j5R zJ_Zx_pbfH?|4p=x;Bj7+G)baN`l< z*+2O%L;t1F=7j(GjnWgaH~!yC{

    cX7&Ax)%Cv}<}XKs|EJy;pA%#0ff_JIQN)SY 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%0D_H+& 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%0D>T0#tJA_}MUckj_CIV>w)v22-j7PDXZ=Rb2> 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 aZm<^2Sb~hRU-?i0zbhI#>TlI79{mf9tP|b< literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/img/gv_priority3-caseD.png b/site/content/docs/v1.9/img/gv_priority3-caseD.png new file mode 100644 index 0000000000000000000000000000000000000000..11c9a11ce8bfbf020cbf7ed3e1df4227fb28a04e GIT binary patch literal 69760 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�fu>MSxrxgi#n;Lqh=@0@!ay&F0yspK)28*_577-8-b z3r6UEwH0zS3Xzjm6e;P;>2Ie|SYWS8>Fo)f{91$2DxzRS)3~7HtG!~!V^TFhzAxdo zVe4~(oKLW?@9I<47Jel^E~=-XH&iL#vUkV2I>FNuA-soAyK5bE8LA%p$!*tpRn^k{ zJ&m&4t8UhP!-W1yDzf&&DuwCcl(}gz@tL&CEdD?lepJE(|MN5A92exXHDxTZd|B_# zgPb&+3<`?bfE>v4QXj>Pgg$aKDlK%Ga6c8s&~y}m7_>Go1ZVr+<6V2$($n(^AV zN+nh2qJ&=39O~ORx0K#o?E3w=;LiNFQxKx2T;9kC%i#UpZ>LCoIP)$G!JB5|`6vlvNh)KDhi%?D~SMf6{I-kqfCM_)0c)&!Z zbpgD=V1_aaQCp>)j#l8rv+%-nSN>9m1+nEi!X&PyTjYW=i*gQoEHbB+9pyze)2I5%Q;g_+Vy*ZrPoH$uaiwLyf&KP^J8nTN{iGMX)23X-V|O<+ zZB3i?&-~-b;yfJC8y?eaPkJ6`_L~8r*=WA&?H*`R>lRYuxuQ%>fc$W1`V_qA{Kb-O zV}grty%2zd8#c4h)CJK7NwAgG{s`oA7`Ui~Cuf`-=NDlP*6QjNstN@H{ap0K~T-O|sLc+B@K%tx1bU5?dOj6$lj)9i268bBQCv z-Xpu8HJa>YC(+S**yL5$y>=Bd?Db~8J36Q{%ojf9C5(b24!P!aG@$l0CV|xBKdT^y zB%KFqW7{=z{6d~eAJ3LFyR>djbEd8PeWR&bn4K6!FoNuED6^kQ^hqlj6jnJ)f$|jI_i+#f0j;5vVYn zN7q@@05|;Qd3P&+bnjJT!hqbI`2nFd=PtOWz2xti_fFvusVsu&m=gkm_h!NCD^E@` zUfP&bZ%`a7kf~0CR5oN;_!F6J)y3nzI9hLmMzACYB>KekV6HYdigYq$i&eOEp|po@ z1VXIvJ>)`~zfchm>h3dEbru@o1@_Fj$HxuJJFD2jOPGGY{Lny)A#8D;fQacCo6djW z8U7YoN8`sY!zryDC2sezC0JgQ=@tg*)z(O$*VE4|>gD!c=b3$geFzAyuUmMOX~BB( z0gYu;zYk~h{ggl1?riP2y;$c-VM#^rK0T4#jH(uus7MYftZl@$HX2r|zFwa%bv;gm zyMAmBw#N^qATpcH=GBOhspoG~z=Dt1%5zf>d8-f=?b^QAOj8U?p8RZ9PW;|iPK@H> zQX(KU-l;FV|#j1-TgjxpS5{7TONKOG+uJ3#iNM;M ze;^k)r%Lf(*4eQ~jXak$UIq zdg-7jFW%uVm3e!RAt^J10$Gj5J!{}ZMpMCw!_guM(8t{HvVaqxSycI495VYZUB)l( z&45+j3GO>KC}EDtZq;1^cai!tR?LYX^U=-PhKp*6o7wi138x zFJ+Si*ql&=%y=_IhpJQ%emS*L@_kBOtHwI^+{LxZ%~SGQ3F0B}sbYt^{P+4_xv;z2Mse3N8jK1u5h{38qnSh6MQz*A7 z8^7qQvzS%Z7(Z>>s3Um;TaDP_S75S|z1GxD9cu44Y2Nl>}`S*T^xFMJ}I@RMt!jHJoo~9GEVy{=#u9N>sH-Qp~Ut z5%*egC_3TB!Ag~v;abSyG<8qh^F64C*OMnmGmXa@ke4xzU1N1IDSy}x{>%=OGSn0F z=|}m;|I`5+YFAi5ChbhL{mc_{3_#MM=Y*zcqV!E<9YM?nGk<mcYCN2BI*Kl!e(i~@32)I7S=Z| zP2XpFoulAl0AHN2dBfl5l{N>HqWe{!#Snj&5*{-$=kE=g`F7(*<3-1D_(cnIz(~!4C z;C&xKWN(kp`D2_z@ljGCm+X>D(*t4*b-IoQT>(VUEX8uLVAo6|Xlv_L62(}G(i{Yw z0hraQ(I6CR*eD9(hH$Ycx0Fbt_(MFWJ_S15vnTdj<~>szr+8GXDRnPcH;n$lA1IWe z9F7GRrR&qJ9@>$7rq3&ba1?!|GEVcvROh33aIkNql*F$9&M9u^4Aiyv%e@qTFpjT! z3f_ppwK}`m2Uo0r=3s$4R#d?Zn~#U>uo&#!D3C#ywXKX( z=L2RiA7@r4u*g4U>0!#~s(m{k)4~0ksY~jBNfnftbDKsUNS(4kZCqhf2wU^s!P|L3 zI%qfggW(DaaZ}B<5QfSSI1gi1$zUZf=a1Rv9A^iq5q=-Z)+Qlic5@B;){d3slak^U z!7Ghl16dUsO(uL=3mPatG|ylRI?zK>su(&+6`9Sjlim9k zVwDWfrMPIP+QnI&FBDyo87$qr@Ui7MYDOzz00k^U}7;}c~M<-`zHKDMo<2Pq38;IZ|Yz!G+Vc@9h)v{6spE?ta;^6(T z^!A2Gfo26EX^|rGxDItlz#E4cd04^+GVE5o zx9bzGidN|yoNsjLb^ zO>A|uI(L+1}L3YqAA4(BrJ=v>!Rd8RsvsAt;{pS|;#zk?*6PH*LkDh#4Ln6)c4^(89R zz#7=<5;QqG8#*{4(uq$STPtGdNzm8!;^U9iqDlueoKLJS-J|t;atrbuTp8cOLXgs> z&1VUfmM{R{UCr`0L-5%J*bVYvGoYZh$oIjeiq14=@|%{g>rLZ~m)0#tPL}1l@+>10 zK%b2=GW=z1@0I7nxRCGj(^5NMq)D}|4Ja`lt(Jv9G;ES`qcv8MTdcsL&x40UlSI=w zRvF^bKqzm?3WEK4B!X;>k}StDEOkR>u66ai$S5(!kgq6}8N_NQHKJAu)GGRh`;Vw? zsUcedqt|{{wu{tG$ML&*HNP@MJ$^{Bp(lynO_qRq^65Pa5$ZCA;xPNYOokkeTc?fA zpqSogtU+>`UoD?Ht^|0L= zH_iQ+Z?yi-Qd#H*RFG6W4hWG(uDPX&>0M{bYzwN|6O1F#$Z9`4#6pGm)8*CarJ6zl z-^^20Hm||!TJ)u*NRHU+Fi$*wKh$+Cor8A9;pT8;L0sf?Fa#MdgUfs@Z$g&lZ;zXY z@qkYy*4=oXXyM;WNUHUOfg<#S21~V_;BZrkFb0GEQ+$(rq2hy$2uD-vPGDguEICe^ zsoQT)zyrY*s7mW?eJaQ2Aqe;M(vHKLd-Fu@n{R3n_}1^VGEwaL4;9iX_7GkVP>t&y z5(;pA59qzQfoF+u*014HX%!$W$6n@gLXTa)18jLgT9v>6?`H%Na5ZAl2kq0ooW-(U zg>Xh9VWi-ir`h@X_Wijo|8jiv%g(4Rf#}TSi6&~OYW5gt>u4Ody zMIGVzte;9$?+s8<$n~^9`+&@Kv_}ReAA*%L2R`PL=Q__NE9;6w4c?4_;*7(dI}RtPL%%lZ0c=sVPr5!=Zo@zkn-hpzA&o=xN{9 zovz-bTQwh#|Lt$BdygINkXs$Le9lSzrS>LB+%7@ zS}heu{%p~Ku|F64F-jY@B;aaub}lc?r765lf_aAQTj&(zxI|j3l_Y&otOYe;P7pO| zCQKcTp-pTZ7C!yh5^_{a=yO;p;VF>fn_J?Eog^J@`6$nT>ca)Ohh zVBi>;aeJleLU|Z$O7^m}mmF|;b@FlCjvkeAfcPN7nsi7%H_O;ampyR4ZZ~J%yuDt^2f+=lZ`s2Fv}YqbdjvcR~oG z*zEgu$Z;2g`io~UN?0UQhPwoJABz1>-|Kqrw8C&K*_Rc(KLz-jjXzf{@VRQvzJ88U zthL<$0tYl98cw2^7?3j|Vp{{jxS{5vp1KjbS60-@Z1gLqb5^mg z`%{4LVYhv~_f)-U?_?NoLKyQ`6;*{2{~0R$p3a-ITDlhgz#lX}YQ*(~C5<^+!JI#N z4A1Awl2{9hLuFxre}{s+yq}@E(F8_n;qmd6t{AL%Vbub;%a!RDC(%oc4*a zzgon`%i3?Moo`az(d7Nh7h6AE>NB^Mp2AiNVMk#;;ix=u4`o|+E2BU7W>|^$bNXr4 zcGXW&PpB;=ef2)z3@<&d1(^TPHCg{bL5PnxN8Qyi^eeuZ38UID59_#9;=MpBhMtLK zguZ2V%tQtLU&lud1z4IT-{c_6fjQ_;g6NFvt;y-P5EOBOg^5-xJT@7!n=7+wQ4{qn z{?cLZ8|Kgb>H2na1I3Rg{u3;P32K3vUl%1tuB%#GDI;>cmik3}4kk6V33rAb>L9@p zW^2ZDI(=n)+coJj!$tCf-@U&2Q6C?XBD3fr6$Of2;VMS}wVo6~G_oVtt4J63YkCk8 zr#v9Bq0n?L>-AV0*$piz5Jo#&DKQyESjaR5m__r8X#6C>KxINgSF zRwe1r?Ls(*;(1%*4C$p9R0<J_w55X!?!kTZpbhp?2?XNcbC#LI_D^o(`RYmFxRO6^^^{Y z#3Q*#u3xiS_ItOG{8Dq;-aDDRwlCD)Ip|?7-KP=pEtWTVI*0^W$eaALOfMx5cHp3d zhv9FpIW-wLH`l)s6@OuZYJ{5*3fC?=x($j?hOm@fv2SraRqm5Im>v6Nc2&9o4rpk< z{}5Ut!If7nV&^-h2df3_EE@zE{nj$MURKVpvccw!AJ2U*cWl9|CFJXruBu4Dd7*yac;^|9FmNAMe+R{O@3-3WV^S_mf|O3T?ID zYmx8Yhc=oG^JQ(ti#T{}wI3ydgdqonSGlkhbNBC|U(9hKjUTeB$Hk)lhU+uMk2>zj zG19-3lrL9t(Ttu=&>!i&LK0T9Y$VudDRN86LC*ji9@OQq)}b^5Gcbj0eJDCLDb10y zbQ+aY;BP-G%DU^UI?mIcbo%q@fRy6!Mt~-m+ew*CXV#4j6+NHBJwUY)Y1BFxM7KoS z_2p8(Nt7buv6uzPf(2Fo-6~Sd#hN}?hQI(IVcx)0R$$nC)_S>>a=2KjE_K}-e*uF_ z`Ia}eC?6gL5yC)0A|Ps7I_bojNk~xDKf^N##I??=CgNwh3SSPt3onYAqC$Z<@=V2} ziw=^FEXvHEeM4ZMFGSWSMidxwhSl3)u#Q zeZ^_O%P?+^>|BCny8ZlbqQeVFKF;C&=%~WR=Ay_U(J&x!jMiuSsEWyug$6gA&N(8+ z90F9ROjw50%xTBr9qDNc5qy;-q6|d)#h#4AD;}BQE^&E)?YX6=y zmXS3%oO+th3-+VsT~%m9nSsJQl5_O@Rbw`rEIlH9=9)Ask$z{KT#{uxu1Y!GkXmw-5BQ6sSa7C&yTZ;`AG2>bdU-H@XDnsc=LUWYyP< zP|TOp|C2na0~%U#T66N1EM3rg+MZgDRi5K^Po;*$pz_%bZKZHM9wR57)8~DnANh*-^9v{ir1@0E|SiF`R ziVlM?)9=saiD3{SNWtpE8rWxDQ=fYLC9%G?^O8^b~~~=T!gI9H-zO5QVNs z%r|kk6PD8HO{VJIrXp3p09%rL5QrdK%7 ziTPz!xj)!CIm$|D`b)@v*h>ipO@a2Lt+JlvosC88I#dhC*67K;!Z)I*4~oei)h&Mz zbz7%FctBOCeFE63W7LQ*mpsF2_(3WTA_vQ}`kUH-cBO8W|6LyrRGY+R4-V_1pEbu< zIHKDV7{5oO90a%tNarrdp~x_^Kp;gfv=#JOiWQK`eFU{C(ROnSi428@0EUTz@B15* zanKlMs^ar^YS+7yX!r?RGYyW$oa};sQiMLc82k<)Q76&~RoidBLi1`*xyTiCL-i;co!-W18ihhSPO9VE*5*pA=B)mAu+#&)-sgxomuzS~zp8>VpS^*qAs zJbz!Z`UioEh;J5B=wc90jL|923&x0eo@yK`2dZ4su&+ATnR0#{8Vx9naH!o$<#rj^ zryxvGLW>^B&6#El(kbS_!J#Lnr>q<>9#QZQth@|Ul2B~Ug&dveXDwmG$DLM@{Z}?{ zh@iMwhXhpo9UgX1d0OR4mSNnzVbJJAaNeb?5&j?sFaDP-H$+|9yf6I$`ay!A{9r^F zBrAY8p)Y45t(#~_Xy8Umfv|so3x~nORcd4uqo4z-U1SRSJkh0ykDaK21+Uj zD5Ovl5efU<%+O!rOoL2l(sy!&`A?{{erWhLQt)tX27VXCl4p} z(e-Q>SHS@azBrC5bRopnv+Laqt%q0BZP%3^rWh0Z;yU;-gs{k&EXKSP#c)$g51K1xFe0;exclaZ>--0EEuD6y=lf-zsDkjy@7Hv$ z1^+X}mk_Z~inE%ddx@?Y@GgymW6=^9J#*2l?E;P&IEa#12&Zp|0Rt))rX?2!O_Z1b ziJTdS(mWVVQChp7!rdlhvv*e-;fVaLc> ztK5Vaz$BTT0M;=URqqW`J{dkVK|Tr-4V43mSUG+iajSvEgZy*1eBw`}&8YF7;AR09 zP-_(dMFGLyX+U%SJG`PTWoV8nm|}E%wfrs7F3<5yndW&*AaCMt|1~VxH6-vq>Bc{c?<>RWy@E|3E=KJUpDJ=Qgsj12khZ{rko5G8B zB7?Ex{!BN7(G?Rm_p6Rq7ccE)c zf*1W7Sj@+lCH;dEBe7OzRZ%7N?rB7ROv{s!0D&xBotj{z*Uo_gnw2;s3w>2POWI=)bY+|8ufx cr?_~7RG)fuILZd?K)gS)Qc9B5;)a3$2S#KsC;$Ke literal 0 HcmV?d00001 diff --git a/site/content/docs/v1.9/locations.md b/site/content/docs/v1.9/locations.md new file mode 100644 index 0000000000..d5dbc710a6 --- /dev/null +++ b/site/content/docs/v1.9/locations.md @@ -0,0 +1,262 @@ +--- +title: "Backup Storage Locations and Volume Snapshot Locations" +layout: docs +--- + +## Overview + +Velero has two custom resources, `BackupStorageLocation` and `VolumeSnapshotLocation`, that are used to configure where Velero backups and their associated persistent volume snapshots are stored. + +A `BackupStorageLocation` is defined as a bucket or a prefix within a bucket under which all Velero data is stored and a set of additional provider-specific fields (AWS region, Azure storage account, etc.). Velero assumes it has control over the location you provide so you should use a dedicated bucket or prefix. If you provide a prefix, then the rest of the bucket is safe to use for multiple purposes. The [API documentation][1] captures the configurable parameters for each in-tree provider. + +A `VolumeSnapshotLocation` is defined entirely by provider-specific fields (AWS region, Azure resource group, Portworx snapshot type, etc.) The [API documentation][2] captures the configurable parameters for each in-tree provider. + +The user can pre-configure one or more possible `BackupStorageLocations` and one or more `VolumeSnapshotLocations`, and can select *at backup creation time* the location in which the backup and associated snapshots should be stored. + +This configuration design enables a number of different use cases, including: + +- Take snapshots of more than one kind of persistent volume in a single Velero backup. For example, in a cluster with both EBS volumes and Portworx volumes +- Have some Velero backups go to a bucket in an eastern USA region, and others go to a bucket in a western USA region, or to a different storage provider +- For volume providers that support it, like Portworx, you can have some snapshots stored locally on the cluster and have others stored in the cloud + +## Limitations / Caveats + +- Velero supports multiple credentials for `BackupStorageLocations`, allowing you to specify the credentials to use with any `BackupStorageLocation`. + However, use of this feature requires support within the plugin for the object storage provider you wish to use. + All [plugins maintained by the Velero team][5] support this feature. + If you are using a plugin from another provider, please check their documentation to determine if this feature is supported. + +- Velero only supports a single set of credentials for `VolumeSnapshotLocations`. + Velero will always use the credentials provided at install time (stored in the `cloud-credentials` secret) for volume snapshots. + +- Volume snapshots are still limited by where your provider allows you to create snapshots. For example, AWS and Azure do not allow you to create a volume snapshot in a different region than where the volume is. If you try to take a Velero backup using a volume snapshot location with a different region than where your cluster's volumes are, the backup will fail. + +- Each Velero backup has one `BackupStorageLocation`, and one `VolumeSnapshotLocation` per volume provider. It is not possible (yet) to send a single Velero backup to multiple backup storage locations simultaneously, or a single volume snapshot to multiple locations simultaneously. However, you can always set up multiple scheduled backups that differ only in the storage locations used if redundancy of backups across locations is important. + +- Cross-provider snapshots are not supported. If you have a cluster with more than one type of volume, like EBS and Portworx, but you only have a `VolumeSnapshotLocation` configured for EBS, then Velero will **only** snapshot the EBS volumes. + +- Restic data is stored under a prefix/subdirectory of the main Velero bucket, and will go into the bucket corresponding to the `BackupStorageLocation` selected by the user at backup creation time. + +- Velero's backups are split into 2 pieces - the metadata stored in object storage, and snapshots/backups of the persistent volume data. Right now, Velero *itself* does not encrypt either of them, instead it relies on the native mechanisms in the object and snapshot systems. A special case is restic, which backs up the persistent volume data at the filesystem level and send it to Velero's object storage. + +- Velero's compression for object metadata is limited, using Golang's tar implementation. In most instances, Kubernetes objects are limited to 1.5MB in size, but many don't approach that, meaning that compression may not be necessary. Note that restic has not yet implemented compression, but does have de-deduplication capabilities. + +- If you have [multiple](customize-installation.md/#configure-more-than-one-storage-location-for-backups-or-volume-snapshots) `VolumeSnapshotLocations` configured for a provider, you must always specify a valid `VolumeSnapshotLocation` when creating a backup, even if you are using [Restic](restic.md) for volume backups. You can optionally decide to set the [`--default-volume-snapshot-locations`](customize-locations.md#set-default-backup-storage-location-or-volume-snapshot-locations) flag using the `velero server`, which lists the default `VolumeSnapshotLocation` Velero should use if a `VolumeSnapshotLocation` is not specified when creating a backup. If you only have one `VolumeSnapshotLocation` for a provider, Velero will automatically use that location as the default. + +## Examples + +Let's look at some examples of how you can use this configuration mechanism to address some common use cases: + +### Take snapshots of more than one kind of persistent volume in a single Velero backup + +During server configuration: + +```shell +velero snapshot-location create ebs-us-east-1 \ + --provider aws \ + --config region=us-east-1 + +velero snapshot-location create portworx-cloud \ + --provider portworx \ + --config type=cloud +``` + +During backup creation: + +```shell +velero backup create full-cluster-backup \ + --volume-snapshot-locations ebs-us-east-1,portworx-cloud +``` + +Alternately, since in this example there's only one possible volume snapshot location configured for each of our two providers (`ebs-us-east-1` for `aws`, and `portworx-cloud` for `portworx`), Velero doesn't require them to be explicitly specified when creating the backup: + +```shell +velero backup create full-cluster-backup +``` + +### Have some Velero backups go to a bucket in an eastern USA region (default), and others go to a bucket in a western USA region + +In this example, two `BackupStorageLocations` will be created within the same account but in different regions. +They will both use the credentials provided at install time and stored in the `cloud-credentials` secret. +If you need to configure unique credentials for each `BackupStorageLocation`, please refer to the [later example][8]. + +During server configuration: + +```shell +velero backup-location create backups-primary \ + --provider aws \ + --bucket velero-backups \ + --config region=us-east-1 \ + --default + +velero backup-location create backups-secondary \ + --provider aws \ + --bucket velero-backups \ + --config region=us-west-1 +``` + +A "default" backup storage location (BSL) is where backups get saved to when no BSL is specified at backup creation time. + +You can change the default backup storage location at any time by setting the `--default` flag using the +`velero backup-location set` command and configure a different location to be the default. + +Examples: + +```shell +velero backup-location set backups-secondary --default +``` + + + +During backup creation: + +```shell +velero backup create full-cluster-backup +``` + +Or: + +```shell +velero backup create full-cluster-alternate-location-backup \ + --storage-location backups-secondary +``` + +### For volume providers that support it (like Portworx), have some snapshots be stored locally on the cluster and have others be stored in the cloud + +During server configuration: + +```shell +velero snapshot-location create portworx-local \ + --provider portworx \ + --config type=local + +velero snapshot-location create portworx-cloud \ + --provider portworx \ + --config type=cloud +``` + +During backup creation: + +```shell +# Note that since in this example you have two possible volume snapshot locations for the Portworx +# provider, you need to explicitly specify which one to use when creating a backup. Alternately, +# you can set the --default-volume-snapshot-locations flag on the `velero server` command (run by +# the Velero deployment) to specify which location should be used for each provider by default, in +# which case you don't need to specify it when creating a backup. +velero backup create local-snapshot-backup \ + --volume-snapshot-locations portworx-local +``` + +Or: + +```shell +velero backup create cloud-snapshot-backup \ + --volume-snapshot-locations portworx-cloud +``` + +### Use a single location + +If you don't have a use case for more than one location, it's still easy to use Velero. Let's assume you're running on AWS, in the `us-west-1` region: + +During server configuration: + +```shell +velero backup-location create backups-primary \ + --provider aws \ + --bucket velero-backups \ + --config region=us-west-1 \ + --default + +velero snapshot-location create ebs-us-west-1 \ + --provider aws \ + --config region=us-west-1 +``` + +During backup creation: + +```shell +# Velero will automatically use your configured backup storage location and volume snapshot location. +# Nothing needs to be specified when creating a backup. +velero backup create full-cluster-backup +``` + +### Create a storage location that uses unique credentials + +It is possible to create additional `BackupStorageLocations` that use their own credentials. +This enables you to save backups to another storage provider or to another account with the storage provider you are already using. + +If you create additional `BackupStorageLocations` without specifying the credentials to use, Velero will use the credentials provided at install time and stored in the `cloud-credentials` secret. +Please see the [earlier example][9] for details on how to create multiple `BackupStorageLocations` that use the same credentials. + +#### Prerequisites +- This feature requires support from the [object storage provider plugin][5] you wish to use. + All plugins maintained by the Velero team support this feature. + If you are using a plugin from another provider, please check their documentation to determine if this is supported. +- The [plugin for the object storage provider][5] you wish to use must be [installed][6]. +- You must create a file with the object storage credentials. Follow the instructions provided by your object storage provider plugin to create this file. + +Once you have installed the necessary plugin and created the credentials file, create a [Kubernetes Secret][7] in the Velero namespace that contains these credentials: + +```shell +kubectl create secret generic -n velero credentials --from-file=bsl= +``` + +This will create a secret named `credentials` with a single key (`bsl`) which contains the contents of your credentials file. +Next, create a `BackupStorageLocation` that uses this Secret by passing the Secret name and key in the `--credential` flag. +When interacting with this `BackupStroageLocation` in the future, Velero will fetch the data from the key within the Secret you provide. + +For example, a new `BackupStorageLocation` with a Secret would be configured as follows: + +```bash +velero backup-location create \ + --provider \ + --bucket \ + --config region= \ + --credential== +``` + +The `BackupStorageLocation` is ready to use when it has the phase `Available`. +You can check the status with the following command: + +```bash +velero backup-location get +``` + +To use this new `BackupStorageLocation` when performing a backup, use the flag `--storage-location ` when running `velero backup create`. +You may also set this new `BackupStorageLocation` as the default with the command `velero backup-location set --default `. + +### Modify the credentials used by an existing storage location + +By default, `BackupStorageLocations` will use the credentials provided at install time and stored in the `cloud-credentials` secret in the Velero namespace. +You can modify these existing credentials by [editing the `cloud-credentials` secret][10], however, these changes will apply to all locations using this secret. +This may be the desired outcome, for example, in the case where you wish to rotate the credentials used for a particular account. + +You can also opt to modify an existing `BackupStorageLocation` such that it uses its own credentials by using the `backup-location set` command. + +If you have a credentials file that you wish to use for a `BackupStorageLocation`, follow the instructions above to create the Secret with that file in the Velero namespace. + +Once you have created the Secret, or have an existing Secret which contains the credentials you wish to use for your `BackupStorageLocation`, set the credential to use as follows: + +```bash +velero backup-location set \ + --credential== +``` + +## Additional Use Cases + +1. If you're using Azure's AKS, you may want to store your volume snapshots outside of the "infrastructure" resource group that is automatically created when you create your AKS cluster. This is possible using a `VolumeSnapshotLocation`, by specifying a `resourceGroup` under the `config` section of the snapshot location. See the [Azure volume snapshot location documentation][3] for details. + +1. If you're using Azure, you may want to store your Velero backups across multiple storage accounts and/or resource groups/subscriptions. This is possible using a `BackupStorageLocation`, by specifying a `storageAccount`, `resourceGroup` and/or `subscriptionId`, respectively, under the `config` section of the backup location. See the [Azure backup storage location documentation][4] for details. + + + +[1]: api-types/backupstoragelocation.md +[2]: api-types/volumesnapshotlocation.md +[3]: https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/volumesnapshotlocation.md +[4]: https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/backupstoragelocation.md +[5]: /plugins +[6]: overview-plugins.md +[7]: https://kubernetes.io/docs/concepts/configuration/secret/ +[8]: #create-a-storage-location-that-uses-unique-credentials +[9]: #have-some-velero-backups-go-to-a-bucket-in-an-eastern-usa-region-default-and-others-go-to-a-bucket-in-a-western-usa-region +[10]: https://kubernetes.io/docs/concepts/configuration/secret/#editing-a-secret diff --git a/site/content/docs/v1.9/maintainers.md b/site/content/docs/v1.9/maintainers.md new file mode 100644 index 0000000000..6de609ccef --- /dev/null +++ b/site/content/docs/v1.9/maintainers.md @@ -0,0 +1,37 @@ +--- +title: "Instructions for Maintainers" +layout: docs +toc: "true" +--- + +There are some guidelines maintainers need to follow. We list them here for quick reference, especially for new maintainers. These guidelines apply to all projects in the Velero org, including the main project, the Velero Helm chart, and all other [related repositories](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#code-repositories). + +Please be sure to also go through the guidance under the entire [Contribute](start-contributing/) section. + +## Reviewing PRs +- PRs require 2 approvals before it is mergeable. +- The second reviewer usually merges the PR (if you notice a PR open for a while and with 2 approvals, go ahead and merge it!) +- As you review a PR that is not yet ready to merge, please check if the "request review" needs to be refreshed for any reviewer (this is better than @mention at them) +- Refrain from @mention other maintainers to review the PR unless it is an immediate need. All maintainers already get notified through the automated add to the "request review". If it is an urgent need, please add a helpful message as to why it is so people can properly prioritize work. +- There is no need to manually request reviewers: after the PR is created, all maintainers will be automatically added to the list (note: feel free to remove people if they are on PTO, etc). +- Be familiar with the [lazy consensus](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#lazy-consensus) policy for the project. + +Some tips for doing reviews: +- There are some [code standards and general guidelines](https://velero.io/docs/v1.9/code-standards) we aim for +- We have [guidelines for writing and reviewing documentation](https://velero.io/docs/v1.9/style-guide/) +- When reviewing a design document, ensure it follows [our format and guidelines]( https://github.com/vmware-tanzu/velero/blob/v1.9/design/_template.md). Also, when reviewing a PR that implements a previously accepted design, ensure the associated design doc is moved to the [design/implemented](https://github.com/vmware-tanzu/velero/tree/main/design/implemented) folder. + + +## Creating a release +Maintainers are expected to create releases for the project. We have parts of the process automated, and full [instructions](release-instructions). +We are working towards automating more the Velero testing, but there is still a need for manual testing as part of the release process. +The manual test cases for release testing are documented [here](./manual-testing). + +## Community support +Maintainers are expected to participate in the community support rotation. We have guidelines for how we handle the [support](support-process). + +## Community engagement +Maintainers for the Velero project are highly involved with the open source community. All the online community meetings for the project are listed in our [community](community) page. + +## How do I become a maintainer? +The Velero project welcomes contributors of all kinds. We are also always on the look out for a high level of engagement from contributors and opportunities to bring in new maintainers. If this is of interest, take a look at how [adding a maintainer](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#maintainers) is decided. diff --git a/site/content/docs/v1.9/manual-testing.md b/site/content/docs/v1.9/manual-testing.md new file mode 100644 index 0000000000..d7d8968831 --- /dev/null +++ b/site/content/docs/v1.9/manual-testing.md @@ -0,0 +1,92 @@ +--- +title: "Manual Testing Requirements for Velero" +layout: docs +--- + +Although we have automated unit and end-to-end tests, there is still a need for Velero to undergo manual tests during a release. +This document outlines the manual test operations that Velero needs to correctly perform in order to be considered ready for release. + +## Current test cases + +The following are test cases that are currently performed as part of a Velero release. + +### Install + +- Verify that Velero CRDs are compatible with the earliest and latest versions of Kubernetes that we support: + - Kubernetes v1.12 + - Kubernetes v1.20 + +### Upgrade + +- Verify that Velero upgrade instructions work + +### Basic functionality + +The "Backup and Restore" test cases below describe general backup and restore functionality that needs to run successfully on all the following providers that we maintain plugins for: +- AWS +- GCP +- Microsoft Azure +- VMware vSphere + +#### Backup and Restore + +- Verify that a backup and restore using Volume Snapshots can be performed +- Verify that a backup and restore using Restic can be performed +- Verify that a backup of a cluster workload can be restored in a new cluster +- Verify that an installation using the latest version can be used to restore from backups created with the last 3 versions. + - e.g. Install Velero 1.6 and use it to restore backups from Velero v1.3, v1.4, v1.5. + +### Working with Multiple Providers + +The following are test cases that exercise Velero behaviour when interacting with multiple providers: + +- Verify that a backup and restore to multiple BackupStorageLocations using the same provider with unique credentials can be performed +- Verify that a backup and restore to multiple BackupStorageLocations using different providers with unique credentials can be performed +- Verify that a backup and restore that includes volume snapshots using different providers for the snapshots and object storage can be performed + - e.g. perform a backup and restore using AWS for the VolumeSnapshotLocation and Azure Blob Storage as the BackupStorageLocation + +## Future test cases + +The following are test cases that are not currently performed as part of a Velero release but cases that we will want to cover with future releases. + +### Schedules + +- Verify that schedules create a backup upon creation and create Backup resources at the correct frequency + +### Resource management + +- Verify that deleted backups are successfully removed from object storage +- Verify that backups that have been removed from object storage can still be deleted with `velero delete backup` +- Verify that Volume Snapshots associated with a deleted backup are removed +- Verify that backups that exceed their TTL are deleted +- Verify that existing backups in object storage are synced to Velero + +### Restic repository test cases + +- Verify that restic repository maintenance is performed as the specified interval + +### Backup Hooks + +- Verify that a pre backup hook provided via pod annotation is performed during backup +- Verify that a pre backup hook provided via Backup spec is performed during backup +- Verify that a post backup hook provided via pod annotation is performed during backup +- Verify that a post backup hook provided via Backup spec is performed during backup + +### Restore Hooks + +- Verify that an InitContainer restore hook provided via pod annotation is performed during restore +- Verify that an InitContainer restore hook provided via Restore spec is performed during restore +- Verify that an InitContainer restore hook provided via Restore spec is performed during restore that includes restoring restic volumes +- Verify that an Exec restore hook provided via pod annotation is performed during restore +- Verify that an Exec restore hook provided via Restore spec is performed during restore + + +#### Resource filtering + +- Verify that backups and restores correctly apply the following resource filters: + - `--include-namespaces` + - `--include-resources` + - `--include-cluster-resources` + - `--exclude-namespaces` + - `--exclude-resources` + - `velero.io/exclude-from-backup=true` label diff --git a/site/content/docs/v1.9/migration-case.md b/site/content/docs/v1.9/migration-case.md new file mode 100644 index 0000000000..163b3fba92 --- /dev/null +++ b/site/content/docs/v1.9/migration-case.md @@ -0,0 +1,104 @@ +--- +title: "Cluster migration" +layout: docs +--- + +Velero's backup and restore capabilities make it a valuable tool for migrating your data between clusters. Cluster migration with Velero is based on Velero's [object storage sync](how-velero-works.md#object-storage-sync) functionality, which is responsible for syncing Velero resources from your designated object storage to your cluster. This means that to perform cluster migration with Velero you must point each Velero instance running on clusters involved with the migration to the same cloud object storage location. + +This page outlines a cluster migration scenario and some common configurations you will need to start using Velero to begin migrating data. + +## Before migrating your cluster + +Before migrating you should consider the following, + +* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [restic](restic.md), which will backup volume contents at the filesystem level. +* Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. +* Migrating workloads across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered before migration, including the compatibility of API groups between clusters for each custom resource. If a Kubernetes version upgrade breaks the compatibility of core/native API groups, migrating with Velero will not be possible without first updating the impacted custom resources. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). +* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [restic](restic.md). + + +## Migration Scenario + +This scenario steps through the migration of resources from Cluster 1 to Cluster 2. In this scenario, both clusters are using the same cloud provider, AWS, and Velero's [AWS plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws). + +1. On Cluster 1, make sure Velero is installed and points to an object storage location using the `--bucket` flag. + + ``` + velero install --provider aws --image velero/velero:v1.8.0 --plugins velero/velero-plugin-for-aws:v1.4.0 --bucket velero-migration-demo --secret-file xxxx/aws-credentials-cluster1 --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 + ``` + + During installation, Velero creates a Backup Storage Location called `default` inside the `--bucket` your provided in the install command, in this case `velero-migration-demo`. This is the location that Velero will use to store backups. Running `velero backup-location get` will show the backup location of Cluster 1. + + + ``` + velero backup-location get + NAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULT + default aws velero-migration-demo Available 2022-05-13 13:41:30 +0800 CST ReadWrite true + ``` + +1. Still on Cluster 1, make sure you have a backup of your cluster. Replace `` with a name for your backup. + + ``` + velero backup create + ``` + + Alternatively, you can create a [scheduled backup](https://velero.io/docs/v1.9/backup-reference/#schedule-a-backup) of your data with the Velero `schedule` operation. This is the recommended way to make sure your data is automatically backed up according to the schedule you define. + + The default backup retention period, expressed as TTL (time to live), is 30 days (720 hours); you can use the `--ttl ` flag to change this as necessary. See [how velero works](how-velero-works.md#set-a-backup-to-expire) for more information about backup expiry. + +1. On Cluster 2, make sure that Velero is installed. Note that the install command below has the same `region` and `--bucket` location as the install command for Cluster 1. The Velero plugin for AWS does not support migrating data between regions. + + ``` + velero install --provider aws --image velero/velero:v1.8.0 --plugins velero/velero-plugin-for-aws:v1.4.0 --bucket velero-migration-demo --secret-file xxxx/aws-credentials-cluster2 --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 + ``` + + Alternatively you could configure `BackupStorageLocations` and `VolumeSnapshotLocations` after installing Velero on Cluster 2, pointing to the `--bucket` location and `region` used by Cluster 1. To do this you can use to `velero backup-location create` and `velero snapshot-location create` commands. + + ``` + velero backup-location create bsl --provider aws --bucket velero-migration-demo --config region=us-east-2 --access-mode=ReadOnly + ``` + + Its recommended that you configure the `BackupStorageLocations` as read-only + by using the `--access-mode=ReadOnly` flag for `velero backup-location create`. This will make sure that the backup is not deleted from the object store by mistake during the restore. See `velero backup-location –help` for more information about the available flags for this command. + + ``` + velero snapshot-location create vsl --provider aws --config region=us-east-2 + ``` + See `velero snapshot-location –help` for more information about the available flags for this command. + + +1. Continuing on Cluster 2, make sure that the Velero Backup object created on Cluster 1 is available. `` should be the same name used to create your backup of Cluster 1. + + ``` + velero backup describe + ``` + + Velero resources are [synchronized](how-velero-works.md#object-storage-sync) with the backup files in object storage. This means that the Velero resources created by Cluster 1's backup will be synced to Cluster 2 through the shared Backup Storage Location. Once the sync occurs, you will be able to access the backup from Cluster 1 on Cluster 2 using Velero commands. The default sync interval is 1 minute, so you may need to wait before checking for the backup's availability on Cluster 2. You can configure this interval with the `--backup-sync-period` flag to the Velero server on Cluster 2. + +1. On Cluster 2, once you have confirmed that the right backup is available, you can restore everything to Cluster 2. + + ``` + velero restore create --from-backup + ``` + + Make sure `` is the same backup name from Cluster 1. + +## Verify Both Clusters + +Check that the Cluster 2 is behaving as expected: + +1. On Cluster 2, run: + + ``` + velero restore get + ``` + +1. Then run: + + ``` + velero restore describe + ``` + + Your data that was backed up from Cluster 1 should now be available on Cluster 2. + +If you encounter issues, make sure that Velero is running in the same namespace in both clusters. diff --git a/site/content/docs/v1.9/namespace.md b/site/content/docs/v1.9/namespace.md new file mode 100644 index 0000000000..68561e7207 --- /dev/null +++ b/site/content/docs/v1.9/namespace.md @@ -0,0 +1,22 @@ +--- +title: "Run in a non-default namespace" +layout: docs +--- + +The Velero installation and backups by default are run in the `velero` namespace. However, it is possible to use a different namespace. + +## Customize the namespace during install + +Use the `--namespace` flag, in conjunction with the other flags in the `velero install` command (as shown in the [the Velero install instructions][0]). This will inform Velero where to install. + +## Customize the namespace for operational commands + +To have namespace consistency, specify the namespace for all Velero operational commands to be the same as the namespace used to install Velero: + +```bash +velero client config set namespace= +``` + +Alternatively, you may use the global `--namespace` flag with any operational command to tell Velero where to run. + +[0]: basic-install.md#install-the-cli diff --git a/site/content/docs/v1.9/on-premises.md b/site/content/docs/v1.9/on-premises.md new file mode 100644 index 0000000000..30ddf98b31 --- /dev/null +++ b/site/content/docs/v1.9/on-premises.md @@ -0,0 +1,95 @@ +--- +title: "On-Premises Environments" +layout: docs +--- + +You can run Velero in an on-premises cluster in different ways depending on your requirements. + +### Selecting an object storage provider + +You must select an object storage backend that Velero can use to store backup data. [Supported providers][0] contains information on various +options that are supported or have been reported to work by users. + +If you do not already have an object storage system, [MinIO][2] is an open-source S3-compatible object storage system that can be installed on-premises and is compatible with Velero. The details of configuring it for production usage are out of scope for Velero's documentation, but an [evaluation install guide][3] using MinIO is provided for convenience. + +### (Optional) Selecting volume snapshot providers + +If you need to back up persistent volume data, you must select a volume backup solution. [Supported providers][0] contains information on the supported options. + +For example, if you use [Portworx][4] for persistent storage, you can install their Velero plugin to get native Portworx snapshots as part of your Velero backups. + +If there is no native snapshot plugin available for your storage platform, you can use Velero's [restic integration][1], which provides a platform-agnostic file-level backup solution for volume data. + +### Air-gapped deployments + +In an air-gapped deployment, there is no access to the public internet, and therefore no access to public container registries. + +In these scenarios, you will need to make sure that you have an internal registry, such as [Harbor][5], installed and the Velero core and plugin images loaded into your internal registry. + +Below you will find instructions to downloading the Velero images to your local machine, tagging them, then uploading them to your custom registry. + +#### Preparing the Velero image + +First, download the Velero image, tag it for the your private registry, then upload it into the registry so that it can be pulled by your cluster. + +```bash +PRIVATE_REG= +VELERO_VERSION= + +docker pull velero/velero:$VELERO_VERSION +docker tag velero/velero:$VELERO_VERSION $PRIVATE_REG/velero:$VELERO_VERSION +docker push $PRIVATE_REG/velero:$VELERO_VERSION +``` + +#### Preparing plugin images + +Next, repeat these steps for any plugins you may need. This example will use the AWS plugin, but the plugin name should be replaced with the plugins you will need. + +```bash +PRIVATE_REG= +PLUGIN_VERSION= + +docker pull velero/velero-plugin-for-aws:$PLUGIN_VERSION +docker tag velero/velero-plugin-for-aws:$PLUGIN_VERSION $PRIVATE_REG/velero-plugin-for-aws:$PLUGIN_VERSION +docker push $PRIVATE_REG/velero-plugin-for-aws:$PLUGIN_VERSION +``` + +#### Preparing the restic helper image (optional) + +If you are using restic, you will also need to upload the restic helper image. + +```bash +PRIVATE_REG= +VELERO_VERSION= + +docker pull velero/velero-restic-restore-helper:$VELERO_VERSION +docker tag velero/velero-restic-restore-helper:$VELERO_VERSION $PRIVATE_REG/velero-restic-restore-helper:$VELERO_VERSION +docker push $PRIVATE_REG/velero-restic-restore-helper:$VELERO_VERSION +``` + +#### Pulling specific architecture images (optional) + +Velero uses Docker manifests for its images, allowing Docker to pull the image needed based on your client machine's architecture. + +If you need to pull a specific image, you should replace the `velero/velero` image with the specific architecture image, such as `velero/velero-arm`. + +To see an up-to-date list of architectures, be sure to enable Docker experimental features and use `docker manifest inspect velero/velero` (or whichever image you're interested in), and join the architecture string to the end of the image name with `-`. + +#### Installing Velero + +By default, `velero install` will use the public `velero/velero` image. When using an air-gapped deployment, use your private registry's image for Velero and your private registry's images for any plugins. + +```bash +velero install \ + --image=$PRIVATE_REG/velero:$VELERO_VERSION \ + --plugins=$PRIVATE_REG/velero-plugin-for-aws:$PLUGIN_VERSION \ +<....> +``` + + +[0]: supported-providers.md +[1]: restic.md +[2]: https://min.io +[3]: contributions/minio.md +[4]: https://portworx.com +[5]: https://goharbor.io/ diff --git a/site/content/docs/v1.9/output-file-format.md b/site/content/docs/v1.9/output-file-format.md new file mode 100644 index 0000000000..8d4823505f --- /dev/null +++ b/site/content/docs/v1.9/output-file-format.md @@ -0,0 +1,224 @@ +--- +title: "Output file format" +layout: docs +--- + +A backup is a gzip-compressed tar file whose name matches the Backup API resource's `metadata.name` (what is specified during `velero backup create `). + +In cloud object storage, each backup file is stored in its own subdirectory in the bucket specified in the Velero server configuration. This subdirectory includes an additional file called `velero-backup.json`. The JSON file lists all information about your associated Backup resource, including any default values. This gives you a complete historical record of the backup configuration. The JSON file also specifies `status.version`, which corresponds to the output file format. + +The directory structure in your cloud storage looks something like: + +``` +rootBucket/ + backup1234/ + velero-backup.json + backup1234.tar.gz +``` + +## Example backup JSON file + +```json +{ + "kind": "Backup", + "apiVersion": "velero.io/v1", + "metadata": { + "name": "test-backup", + "namespace": "velero", + "selfLink": "/apis/velero.io/v1/namespaces/velero/backups/test-backup", + "uid": "a12345cb-75f5-11e7-b4c2-abcdef123456", + "resourceVersion": "337075", + "creationTimestamp": "2017-07-31T13:39:15Z" + }, + "spec": { + "includedNamespaces": [ + "*" + ], + "excludedNamespaces": null, + "includedResources": [ + "*" + ], + "excludedResources": null, + "labelSelector": null, + "snapshotVolumes": true, + "ttl": "24h0m0s" + }, + "status": { + "version": 1, + "formatVersion": "1.1.0", + "expiration": "2017-08-01T13:39:15Z", + "phase": "Completed", + "volumeBackups": { + "pvc-e1e2d345-7583-11e7-b4c2-abcdef123456": { + "snapshotID": "snap-04b1a8e11dfb33ab0", + "type": "gp2", + "iops": 100 + } + }, + "validationErrors": null + } +} +``` +Note that this file includes detailed info about your volume snapshots in the `status.volumeBackups` field, which can be helpful if you want to manually check them in your cloud provider GUI. + +## Output File Format Versioning + +The Velero output file format is intended to be relatively stable, but may change over time to support new features. + +To accommodate this, Velero follows [Semantic Versioning](http://semver.org/) for the file format version. + +Minor and patch versions will indicate backwards-compatible changes that previous versions of Velero can restore, including new directories or files. + +A major version would indicate that a version of Velero older than the version that created the backup could not restore it, usually because of moved or renamed directories or files. + +Major versions of the file format will be incremented with major version releases of Velero. +However, a major version release of Velero does not necessarily mean that the backup format version changed - Velero 3.0 could still use backup file format 2.0, as an example. + +## Versions + +### File Format Version: 1.1 (Current) + +Version 1.1 added support of API groups versions as part of the backup. Previously, only the preferred version of each API groups was backed up. Each resource has one or more sub-directories: one sub-directory for each supported version of the API group. The preferred version API Group of each resource has the suffix "-preferredversion" as part of the sub-directory name. For backward compatibility, we kept the classic directory structure without the API group version, which sits on the same level as the API group sub-directory versions. + +By default, only the preferred API group of each resource is backed up. To take a backup of all API group versions, you need to run the Velero server with the `--features=EnableAPIGroupVersions` feature flag. This is an experimental flag and the restore logic to handle multiple API group versions is documented at [EnableAPIGroupVersions](enable-api-group-versions-feature.md). + +When unzipped, a typical backup directory (`backup1234.tar.gz`) taken with this file format version looks like the following (with the feature flag): + +``` +resources/ + persistentvolumes/ + cluster/ + pv01.json + ... + v1-preferredversion/ + cluster/ + pv01.json + ... + configmaps/ + namespaces/ + namespace1/ + myconfigmap.json + ... + namespace2/ + ... + v1-preferredversion/ + namespaces/ + namespace1/ + myconfigmap.json + ... + namespace2/ + ... + pods/ + namespaces/ + namespace1/ + mypod.json + ... + namespace2/ + ... + v1-preferredversion/ + namespaces/ + namespace1/ + mypod.json + ... + namespace2/ + ... + jobs.batch/ + namespaces/ + namespace1/ + awesome-job.json + ... + namespace2/ + ... + v1-preferredversion/ + namespaces/ + namespace1/ + awesome-job.json + ... + namespace2/ + ... + deployments/ + namespaces/ + namespace1/ + cool-deployment.json + ... + namespace2/ + ... + v1-preferredversion/ + namespaces/ + namespace1/ + cool-deployment.json + ... + namespace2/ + ... + horizontalpodautoscalers.autoscaling/ + namespaces/ + namespace1/ + hpa-to-the-rescue.json + ... + namespace2/ + ... + v1-preferredversion/ + namespaces/ + namespace1/ + hpa-to-the-rescue.json + ... + namespace2/ + ... + v2beta1/ + namespaces/ + namespace1/ + hpa-to-the-rescue.json + ... + namespace2/ + ... + v2beta2/ + namespaces/ + namespace1/ + hpa-to-the-rescue.json + ... + namespace2/ + ... + + ... +``` + +### File Format Version: 1 + +When unzipped, a typical backup directory (`backup1234.tar.gz`) looks like the following: + +``` +resources/ + persistentvolumes/ + cluster/ + pv01.json + ... + configmaps/ + namespaces/ + namespace1/ + myconfigmap.json + ... + namespace2/ + ... + pods/ + namespaces/ + namespace1/ + mypod.json + ... + namespace2/ + ... + jobs/ + namespaces/ + namespace1/ + awesome-job.json + ... + namespace2/ + ... + deployments/ + namespaces/ + namespace1/ + cool-deployment.json + ... + namespace2/ + ... + ... +``` diff --git a/site/content/docs/v1.9/overview-plugins.md b/site/content/docs/v1.9/overview-plugins.md new file mode 100644 index 0000000000..037601d518 --- /dev/null +++ b/site/content/docs/v1.9/overview-plugins.md @@ -0,0 +1,29 @@ +--- +title: "Velero plugin system" +layout: docs +--- + +Velero uses storage provider plugins to integrate with a variety of storage systems to support backup and snapshot operations. + +For server installation, Velero requires that at least one plugin is added (with the `--plugins` flag). The plugin will be either of the type object store or volume snapshotter, or a plugin that contains both. An exception to this is that when the user is not configuring a backup storage location or a snapshot storage location at the time of install, this flag is optional. + +Any plugin can be added after Velero has been installed by using the command `velero plugin add `. + +Example with a dockerhub image: `velero plugin add velero/velero-plugin-for-aws:v1.0.0`. + +In the same way, any plugin can be removed by using the command `velero plugin remove `. + +## Creating a new plugin + +Anyone can add integrations for any platform to provide additional backup and volume storage without modifying the Velero codebase. To write a plugin for a new backup or volume storage platform, take a look at our [example repo][1] and at our documentation for [Custom plugins][2]. + +## Adding a new plugin + +After you publish your plugin on your own repository, open a PR that adds a link to it under the appropriate list of [supported providers][3] page in our documentation. + +You can also add the [`velero-plugin` GitHub Topic][4] to your repo, and it will be shown under the aggregated list of repositories automatically. + +[1]: https://github.com/vmware-tanzu/velero-plugin-example/ +[2]: custom-plugins.md +[3]: supported-providers.md +[4]: https://github.com/topics/velero-plugin diff --git a/site/content/docs/v1.9/plugin-release-instructions.md b/site/content/docs/v1.9/plugin-release-instructions.md new file mode 100644 index 0000000000..4323d8c9ac --- /dev/null +++ b/site/content/docs/v1.9/plugin-release-instructions.md @@ -0,0 +1,30 @@ +--- +title: Releasing Velero plugins +layout: docs +toc: "true" +--- + +Velero plugins maintained by the core maintainers do not have any shipped binaries, only container images, so there is no need to invoke a GoReleaser script. +Container images are built via a CI job on git push. + +Plugins the Velero core team is responsible include all those listed in [the Velero-supported providers list](supported-providers.md) _except_ the vSphere plugin. + + +## Steps +### Open a PR to prepare the repo +1. Update the README.md file to update the compatibility matrix and `velero install` instructions with the expected version number and open a PR. +1. Determining the version number is based on semantic versioning and whether the plugin uses any newly introduced, changed, or removed methods or variables from Velero. +2. Roll all unreleased changelogs into a new `CHANGELOG-v.md` file and delete the content of the `unreleased` folder. Edit the new changelog file as needed. +### Tag +1. Once the PR is merged, checkout the upstream `main` branch. Your local upstream might be named `upstream` or `origin`, so use this command: `git checkout /main`. +1. Tag the git version - `git tag v`. +1. Push the git tag - `git push --tags ` to trigger the image build. +2. Wait for the container images to build. You may check the progress of the GH action that triggers the image build at `https://github.com/vmware-tanzu//actions` +3. Verify that an image with the new tag is available at `https://hub.docker.com/repository/docker/velero//`. +4. Run the Velero [e2e tests][2] using the new image. Until it is made configurable, you will have to edit the [plugin version][1] in the test. +### Release +1. If all e2e tests pass, go to the GitHub release page of the plugin (`https://github.com/vmware-tanzu//releases`) and manually create a release for the new tag. +1. Copy and paste the content of the new changelog file into the release description field. + +[1]: https://github.com/vmware-tanzu/velero/blob/c8dfd648bbe85db0184ea53296de4220895497e6/test/e2e/velero_utils.go#L27 +[2]: https://github.com/vmware-tanzu/velero/tree/main/test/e2e diff --git a/site/content/docs/v1.9/rbac.md b/site/content/docs/v1.9/rbac.md new file mode 100644 index 0000000000..a9c22b9f1b --- /dev/null +++ b/site/content/docs/v1.9/rbac.md @@ -0,0 +1,50 @@ +--- +title: "Run Velero more securely with restrictive RBAC settings" +layout: docs +--- + +By default Velero runs with an RBAC policy of ClusterRole `cluster-admin`. This is to make sure that Velero can back up or restore anything in your cluster. But `cluster-admin` access is wide open -- it gives Velero components access to everything in your cluster. Depending on your environment and your security needs, you should consider whether to configure additional RBAC policies with more restrictive access. + +**Note:** Roles and RoleBindings are associated with a single namespaces, not with an entire cluster. PersistentVolume backups are associated only with an entire cluster. This means that any backups or restores that use a restrictive Role and RoleBinding pair can manage only the resources that belong to the namespace. You do not need a wide open RBAC policy to manage PersistentVolumes, however. You can configure a ClusterRole and ClusterRoleBinding that allow backups and restores only of PersistentVolumes, not of all objects in the cluster. + +For more information about RBAC and access control generally in Kubernetes, see the Kubernetes documentation about [access control][1], [managing service accounts][2], and [RBAC authorization][3]. + +## Set up Roles and RoleBindings + +Here's a sample Role and RoleBinding pair. + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: YOUR_NAMESPACE_HERE + name: ROLE_NAME_HERE + labels: + component: velero +rules: + - apiGroups: + - velero.io + verbs: + - "*" + resources: + - "*" +``` + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ROLEBINDING_NAME_HERE +subjects: + - kind: ServiceAccount + name: YOUR_SERVICEACCOUNT_HERE +roleRef: + kind: Role + name: ROLE_NAME_HERE + apiGroup: rbac.authorization.k8s.io +``` + +[1]: https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/ +[2]: https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/ +[3]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/ +[4]: namespace.md diff --git a/site/content/docs/v1.9/release-instructions.md b/site/content/docs/v1.9/release-instructions.md new file mode 100644 index 0000000000..f62c355011 --- /dev/null +++ b/site/content/docs/v1.9/release-instructions.md @@ -0,0 +1,177 @@ +--- +title: "Release Instructions" +layout: docs +toc: "true" +--- +This page covers the steps to perform when releasing a new version of Velero. + +## General notes +- Please read the documented variables in each script to understand what they are for and how to properly format their values. +- You will need to have an upstream remote configured to use to the [vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) repository. + You can check this using `git remote -v`. + The release script ([`tag-release.sh`](https://github.com/vmware-tanzu/velero/blob/v1.9/hack/release-tools/tag-release.sh)) will use `upstream` as the default remote name if it is not specified using the environment variable `REMOTE`. +- GA release: major and minor releases only. Example: 1.0 (major), 1.5 (minor). +- Pre-releases: Any release leading up to a GA. Example: 1.4.0-beta.1, 1.5.0-rc.1 +- RC releases: Release Candidate, contains everything that is supposed to ship with the GA release. This is still a pre-release. + +## Velero Release Requirements + +Velero is on a "train leaves the station" model for releases. We will generate a release candidate (RC) +at the scheduled time. Multiple release candidates may be generated, depending on if bugs are found during testing. +When testing has passed a release build will be generated. + +### Release Candidate criteria +The release candidate commit must meet the following criteria: + +* No major bugs outstanding +* Unit tests pass +* E2E tests against latest Kubernetes on AWS, vSphere and kind pass + +Once the release has moved to RC, a code freeze is in effect. Only changes needed to release are allowable. + +### Release criteria +In order for a release candidate to be released, it must meet the following criteria: + +* Unit tests pass +* E2E tests against latest K8S and earliest supported K8S on Azure, vSphere, Kind, AWS, GCP +* Manual tests pass (manual tests will be converted to automated testing) + +When bugs are identified by any of these methods, we will determine whether the bug is a release blocker or not and +a fix generated if it is. When release blocker bugs identifies in an release candidate are fixed, another RC will +be generated and the test cycle will restart. + +## Preparing + +### Create release blog post (GA only) +For each major or minor release, create and publish a blog post to let folks know what's new. Please follow these [instructions](how-to-write-and-release-a-blog-post). + +### Changelog and Docs PR +#### Troubleshooting +- If you encounter the error `You don't have enough free space in /var/cache/apt/archives/` when running `make serve-docs`: run `docker system prune`. + +#### Steps +1. If it doesn't already exist: in a branch, create the file `changelogs/CHANGELOG-..md` by copying the most recent one. +1. Update the file `changelogs/CHANGELOG-..md` + - Run `make changelog` to generate a list of all unreleased changes. + - Copy/paste the output into `CHANGELOG-..md`, under the "All Changes" section for the release. + - You *may* choose to tweak formatting on the list of changes by adding code blocks, etc. + - Update links at the top of the file to point to the new release version +1. Update the main `CHANGELOG.md` file to properly reference the release-specific changelog file + - Under "Current release": + - Should contain only the current GA release. + - Under "Development release": + - Should contain only the latest pre-release + - Move any prior pre-release into "Older releases" +1. GA Only: Remove all changelog files from `changelogs/unreleased`. +1. Generate new docs + - Run `make gen-docs`, passing the appropriate variables. Examples: + a) `VELERO_VERSION=v1.5.0-rc.1 NEW_DOCS_VERSION=v1.5.0-rc.1 make gen-docs`. + b) `VELERO_VERSION=v1.5.0 NEW_DOCS_VERSION=v1.5 make gen-docs`). + - Note: `PREVIOUS_DOCS_VERSION=` is optional; when not set, it will default to the latest doc version. +1. Clean up when there is an existing set of pre-release versioned docs for the version you are releasing + - Example: `site/content/docs/v1.5.0-beta.1` exists, and you're releasing `v1.5.0-rc.1` or `v1.5` + - Remove the directory containing the pre-release docs, i.e. `site/content/docs/`. + - Delete the pre-release docs table of contents file, i.e. `site/data/docs/-toc.yml`. + - Remove the pre-release docs table of contents mapping entry from `site/data/toc-mapping.yml`. + - Remove all references to the pre-release docs from `site/config.yml`. +1. Create the "Upgrade to $major.minor" page if it does not already exist ([example](https://velero.io/docs/v1.5/upgrade-to-1.5/)). + If it already exists, update any usage of the previous version string within this file to use the new version string instead ([example](https://github.com/vmware-tanzu/velero/pull/2941/files#diff-d594f8fd0901fed79c39aab4b348193d)). + This needs to be done in both the versioned and the `main` folders. +1. Review and submit PR + - Follow the additional instructions at `site/README-HUGO.md` to complete the docs generation process. + - Do a review of the diffs, and/or run `make serve-docs` and review the site. + - Submit a PR containing the changelog and the version-tagged docs. + +### Pin the base image +The image of velero is built based on [Distroless docker image](https://github.com/GoogleContainerTools/distroless). +For the reproducibility of the release, before the release candidate is tagged, we need to make sure the in the Dockerfile +on the release branch, the base image is referenced by digest, such as +https://github.com/vmware-tanzu/velero/blob/release-1.7/Dockerfile#L53-L54 + +## Velero release +### Notes +- Pre-requisite: PR with the changelog and docs is merged, so that it's included in the release tag. +- This process is the same for both pre-release and GA. +- Refer to the [General notes](general-notes) above for instructions. + +#### Troubleshooting +- If the dry-run fails with random errors, try running it again. + +#### Steps +1. Manually create the release branch on Github, in the form like `release-$major.$minor` +1. Create a tagged release in dry-run mode + - This won't push anything to GitHub. + - Run `VELERO_VERSION=v1.9.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ON_RELEASE_BRANCH=TRUE ./hack/release-tools/tag-release.sh`. + - Fix any issue. +1. Create a tagged release and push it to GitHub + - Run `VELERO_VERSION=v1.9.0-rc.1 REMOTE= GITHUB_TOKEN=REDACTED ON_RELEASE_BRANCH=TRUE ./hack/release-tools/tag-release.sh publish`. +1. Publish the release + - Navigate to the draft GitHub release at https://github.com/vmware-tanzu/velero/releases and edit the release. + - If this is a patch release (e.g. `v1.9.1`), note that the full `CHANGELOG-1.9.md` contents will be included in the body of the GitHub release. You need to delete the previous releases' content (e.g. `v1.9.0`'s changelog) so that only the latest patch release's changelog shows. + - Do a quick review for formatting. + - **Note:** the `goreleaser` process should have detected if it's a pre-release version and, if so, checked the box at the bottom of the GitHub release page appropriately, but it's always worth double-checking. + - Verify that GitHub has built and pushed all the images (it takes a while): https://github.com/vmware-tanzu/velero/actions + - Verify that the images are on Docker Hub: https://hub.docker.com/r/velero/velero/tags + - Verify that the assets were published to the GitHub release + - Publish the release. +1. Test the release + - By now, the Docker images should have been published. + - Perform a smoke-test - for example: + - Download the CLI from the GitHub release + - Use it to install Velero into a cluster (or manually update an existing deployment to use the new images) + - Verify that `velero version` shows the expected output + - Run a backup/restore and ensure it works + +## Homebrew release (GA only) +These are the steps to update the Velero Homebrew version. + +### Steps +- If you don't already have one, create a [GitHub access token for Homebrew](https://github.com/settings/tokens/new?scopes=gist,public_repo&description=Homebrew) +- Run `export HOMEBREW_GITHUB_API_TOKEN=your_token_here` on your command line to make sure that `brew` can work on GitHub on your behalf. +- Run `hack/release-tools/brew-update.sh`. This script will download the necessary files, do the checks, and invoke the brew helper to submit the PR, which will open in your browser. +- Update Windows Chocolatey version. From a Windows computer, follow the step-by-step instructions to [create the Windows Chocolatey package for Velero CLI](https://github.com/adamrushuk/velero-choco/blob/main/README.md) + +## Plugins + +To release plugins maintained by the Velero team, follow the [plugin release instructions](plugin-release-instructions.md). + +After the plugin images are built, be sure to update any [e2e tests][3] that use these plugins. + +## Helm Chart (GA only) + +### Steps +- Update the CRDs under helm chart folder `crds` according to the current Velero GA version, and add the labels for the helm chart CRDs. For example: https://github.com/vmware-tanzu/helm-charts/pull/248. +- Bump the Chart version `version` on the `Chart.yaml`. +- Bump the Velero version `appVersion` on the `Chart.yaml` file and `tag` on the `values.yaml` file. +- Bump the plugin version on the `values.yaml` if needed. +- Update the _upgrade_ instruction and related tag on the `README.md` file. + +## How to write and release a blog post +What to include in a release blog: +* Thank all contributors for their involvement in the release. + * Where possible shoutout folks by name or consider spotlighting new maintainers. +* Highlight the themes, or areas of focus, for the release. Some examples of themes are security, bug fixes, feature improvements. See past Velero [release blog posts][1] for more examples. +* Include summaries of new features or workflows introduced in a release. + * This can also include new project initiatives, like a code-of-conduct update. + * Consider creating additional blog posts that go through new features in more detail. Plan to publish additional blogs after the release blog (all blogs don’t have to be publish all at once). + +Release blog post PR: +* Prepare a PR containing the release blog post. Read the [website guidelines][2] for more information on creating a blog post. It's usually easiest to make a copy of the most recent existing post, then replace the content as appropriate. +* You also need to update `site/index.html` to have "Latest Release Information" contain a link to the new post. +* Plan to publish the blog post the same day as the release. + +## Announce a release +Once you are finished doing the release, let the rest of the world know it's available by posting messages in the following places. +1. GA Only: Merge the blog post PR. +1. Velero's Twitter account. Maintainers are encouraged to help spread the word by posting or reposting on social media. +1. Community Slack channel. +1. Google group message. + +What to include: +* Thank all contributors +* A brief list of highlights in the release +* Link to the release blog post, release notes, and/or github release page + +[1]: https://velero.io/blog +[2]: website-guidelines.md +[3]: https://github.com/vmware-tanzu/velero/tree/main/test/e2e diff --git a/site/content/docs/v1.9/release-schedule.md b/site/content/docs/v1.9/release-schedule.md new file mode 100644 index 0000000000..ca88383d25 --- /dev/null +++ b/site/content/docs/v1.9/release-schedule.md @@ -0,0 +1,15 @@ +--- +title: "Release Schedule" +layout: docs +toc: "true" +--- + +Definitions borrowed from [the Kubernetes release process document](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-release/release.md#definitions) + +General phases for a Velero release +- Enhancement/Design freeze +- Implementation phase +- Feature freeze & pruning +- Code freeze & prerelease +- Release + diff --git a/site/content/docs/v1.9/resource-filtering.md b/site/content/docs/v1.9/resource-filtering.md new file mode 100644 index 0000000000..02ae3d68d0 --- /dev/null +++ b/site/content/docs/v1.9/resource-filtering.md @@ -0,0 +1,143 @@ +--- +title: "Resource filtering" +layout: docs +--- + +*Filter objects by namespace, type, or labels.* + +This page describes how to use the include and exclude flags with the `velero backup` and `velero restore` commands. By default Velero includes all objects in a backup or restore when no filtering options are used. + +## Includes + +Only specific resources are included, all others are excluded. + +Wildcard takes precedence when both a wildcard and specific resource are included. + +### --include-namespaces + +Namespaces to include. Default is `*`, all namespaces. + +* Backup a namespace and it's objects. + + ```bash + velero backup create --include-namespaces + ``` + +* Restore two namespaces and their objects. + + ```bash + velero restore create --include-namespaces , + ``` + +### --include-resources + +Kubernetes resources to include in the backup, formatted as resource.group, such as storageclasses.storage.k8s.io (use `*` for all resources). + +* Backup all deployments in the cluster. + + ```bash + velero backup create --include-resources deployments + ``` + +* Restore all deployments and configmaps in the cluster. + + ```bash + velero restore create --include-resources deployments,configmaps + ``` + +* Backup the deployments in a namespace. + + ```bash + velero backup create --include-resources deployments --include-namespaces + ``` + +### --include-cluster-resources + +Includes cluster-scoped resources. This option can have three possible values: + +* `true`: all cluster-scoped resources are included. + +* `false`: no cluster-scoped resources are included. + +* `nil` ("auto" or not supplied): + + - Cluster-scoped resources are included when backing up or restoring all namespaces. Default: `true`. + + - Cluster-scoped resources are not included when namespace filtering is used. Default: `false`. + + * Some related cluster-scoped resources may still be backed/restored up if triggered by a custom action (for example, PVC->PV) unless `--include-cluster-resources=false`. + +* Backup entire cluster including cluster-scoped resources. + + ```bash + velero backup create + ``` + +* Restore only namespaced resources in the cluster. + + ```bash + velero restore create --include-cluster-resources=false + ``` + +* Backup a namespace and include cluster-scoped resources. + + ```bash + velero backup create --include-namespaces --include-cluster-resources=true + ``` + +### --selector + +* Include resources matching the label selector. + + ```bash + velero backup create --selector = + ``` +* Include resources that are not matching the selector + ```bash + velero backup create --selector != + ``` + +For more information read the [Kubernetes label selector documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) + + +## Excludes + +Exclude specific resources from the backup. + +Wildcard excludes are ignored. + +### --exclude-namespaces + +Namespaces to exclude. + +* Exclude kube-system from the cluster backup. + + ```bash + velero backup create --exclude-namespaces kube-system + ``` + +* Exclude two namespaces during a restore. + + ```bash + velero restore create --exclude-namespaces , + ``` + +### --exclude-resources + +Kubernetes resources to exclude, formatted as resource.group, such as storageclasses.storage.k8s.io. + +* Exclude secrets from the backup. + + ```bash + velero backup create --exclude-resources secrets + ``` + +* Exclude secrets and rolebindings. + + ```bash + velero backup create --exclude-resources secrets,rolebindings + ``` + +### velero.io/exclude-from-backup=true + +* Resources with the label `velero.io/exclude-from-backup=true` are not included in backup, even if it contains a matching selector label. diff --git a/site/content/docs/v1.9/restic.md b/site/content/docs/v1.9/restic.md new file mode 100644 index 0000000000..492738af15 --- /dev/null +++ b/site/content/docs/v1.9/restic.md @@ -0,0 +1,549 @@ +--- +title: "Restic Integration" +layout: docs +--- + +Velero supports backing up and restoring Kubernetes volumes using a free open-source backup tool called [restic][1]. This support is considered beta quality. Please see the list of [limitations](#limitations) to understand if it fits your use case. + +Velero allows you to take snapshots of persistent volumes as part of your backups if you’re using one of +the supported cloud providers’ block storage offerings (Amazon EBS Volumes, Azure Managed Disks, Google Persistent Disks). +It also provides a plugin model that enables anyone to implement additional object and block storage backends, outside the +main Velero repository. + +Velero's Restic integration was added to give you an out-of-the-box solution for backing up and restoring almost any type of Kubernetes volume. This integration is an addition to Velero's capabilities, not a replacement for existing functionality. If you're running on AWS, and taking EBS snapshots as part of your regular Velero backups, there's no need to switch to using Restic. However, if you need a volume snapshot plugin for your storage platform, or if you're using EFS, AzureFile, NFS, emptyDir, +local, or any other volume type that doesn't have a native snapshot concept, Restic might be for you. + +Restic is not tied to a specific storage platform, which means that this integration also paves the way for future work to enable +cross-volume-type data migrations. + +**NOTE:** hostPath volumes are not supported, but the [local volume type][4] is supported. + +## Setup Restic + +### Prerequisites + +- Understand how Velero performs [backups with the Restic integration](#how-backup-and-restore-work-with-restic). +- [Download][3] the latest Velero release. +- Kubernetes v1.16.0 and later. Velero's Restic integration requires the Kubernetes [MountPropagation feature][6]. + +### Install Restic + +To install Restic, use the `--use-restic` flag in the `velero install` command. See the [install overview][2] for more details on other flags for the install command. + +``` +velero install --use-restic +``` + +When using Restic on a storage provider that doesn't have Velero support for snapshots, the `--use-volume-snapshots=false` flag prevents an unused `VolumeSnapshotLocation` from being created on installation. + +Velero handles the creation of the restic repo prefix for Amazon, Azure, and GCP plugins, if you are using a different [provider plugin](supported-providers.md), then you will need to make sure the `resticRepoPrefix` is set in the [BackupStorageLocation `config`](api-types/backupstoragelocation.md). The value for `resticRepoPrefix` should be the cloud storage URL where all namespace restic repos will be created. Velero creates one restic repo per namespace. For example, if backing up 2 namespaces, namespace1 and namespace2, using restic on AWS, the `resticRepoPrefix` would be something like `s3:s3-us-west-2.amazonaws.com/bucket/restic` and the full restic repo path for namespace1 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns1` and for namespace2 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns2`. + +There may be additional installation steps depending on the cloud provider plugin you are using. You should refer to the [plugin specific documentation](supported-providers.md) for the must up to date information. + +### Configure Restic DaemonSet spec + +After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the Restic DaemonSet spec. The steps in this section are only needed if you are installing on RancherOS, OpenShift, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. + + +**RancherOS** + + +Update the host path for volumes in the Restic DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to `/opt/rke/var/lib/kubelet/pods`. + +```yaml +hostPath: + path: /var/lib/kubelet/pods +``` + +to + +```yaml +hostPath: + path: /opt/rke/var/lib/kubelet/pods +``` + + +**OpenShift** + + +To mount the correct hostpath to pods volumes, run the Restic pod in `privileged` mode. + +1. Add the `velero` ServiceAccount to the `privileged` SCC: + + ``` + $ oc adm policy add-scc-to-user privileged -z velero -n velero + ``` + +2. For OpenShift version >= `4.1`, modify the DaemonSet yaml to request a privileged mode: + + ```diff + @@ -67,3 +67,5 @@ spec: + value: /credentials/cloud + - name: VELERO_SCRATCH_DIR + value: /scratch + + securityContext: + + privileged: true + ``` + + or + + ```shell + oc patch ds/restic \ + --namespace velero \ + --type json \ + -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value": { "privileged": true}}]' + ``` + +3. For OpenShift version < `4.1`, modify the DaemonSet yaml to request a privileged mode and mount the correct hostpath to pods volumes. + + ```diff + @@ -35,7 +35,7 @@ spec: + secretName: cloud-credentials + - name: host-pods + hostPath: + - path: /var/lib/kubelet/pods + + path: /var/lib/origin/openshift.local.volumes/pods + - name: scratch + emptyDir: {} + containers: + @@ -67,3 +67,5 @@ spec: + value: /credentials/cloud + - name: VELERO_SCRATCH_DIR + value: /scratch + + securityContext: + + privileged: true + ``` + + or + + ```shell + oc patch ds/restic \ + --namespace velero \ + --type json \ + -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value": { "privileged": true}}]' + + oc patch ds/restic \ + --namespace velero \ + --type json \ + -p '[{"op":"replace","path":"/spec/template/spec/volumes/0/hostPath","value": { "path": "/var/lib/origin/openshift.local.volumes/pods"}}]' + ``` + + +If Restic is not running in a privileged mode, it will not be able to access pods volumes within the mounted hostpath directory because of the default enforced SELinux mode configured in the host system level. You can [create a custom SCC](https://docs.openshift.com/container-platform/3.11/admin_guide/manage_scc.html) to relax the security in your cluster so that Restic pods are allowed to use the hostPath volume plug-in without granting them access to the `privileged` SCC. + +By default a userland openshift namespace will not schedule pods on all nodes in the cluster. + +To schedule on all nodes the namespace needs an annotation: + +``` +oc annotate namespace openshift.io/node-selector="" +``` + +This should be done before velero installation. + +Or the ds needs to be deleted and recreated: + +``` +oc get ds restic -o yaml -n > ds.yaml +oc annotate namespace openshift.io/node-selector="" +oc create -n -f ds.yaml +``` + +**VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** + +You need to enable the `Allow Privileged` option in your plan configuration so that Restic is able to mount the hostpath. + +The hostPath should be changed from `/var/lib/kubelet/pods` to `/var/vcap/data/kubelet/pods` + +```yaml +hostPath: + path: /var/vcap/data/kubelet/pods +``` + + +**Microsoft Azure** + +If you are using [Azure Files][8], you need to add `nouser_xattr` to your storage class's `mountOptions`. See [this restic issue][9] for more details. + +You can use the following command to patch the storage class: + +```bash +kubectl patch storageclass/ \ + --type json \ + --patch '[{"op":"add","path":"/mountOptions/-","value":"nouser_xattr"}]' +``` + +## To back up + +Velero supports two approaches of discovering pod volumes that need to be backed up using Restic: + +- Opt-in approach: Where every pod containing a volume to be backed up using Restic must be annotated with the volume's name. +- Opt-out approach: Where all pod volumes are backed up using Restic, with the ability to opt-out any volumes that should not be backed up. + +The following sections provide more details on the two approaches. + +### Using the opt-out approach + +In this approach, Velero will back up all pod volumes using Restic with the exception of: + +- Volumes mounting the default service account token, Kubernetes secrets, and config maps +- Hostpath volumes + +It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` annotation on the pod. + +Instructions to back up using this approach are as follows: + +1. Run the following command on each pod that contains volumes that should **not** be backed up using Restic + + ```bash + kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes-excludes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... + ``` + where the volume names are the names of the volumes in the pod spec. + + For example, in the following pod: + + ```yaml + apiVersion: v1 + kind: Pod + metadata: + name: app1 + namespace: sample + spec: + containers: + - image: k8s.gcr.io/test-webserver + name: test-webserver + volumeMounts: + - name: pvc1-vm + mountPath: /volume-1 + - name: pvc2-vm + mountPath: /volume-2 + volumes: + - name: pvc1-vm + persistentVolumeClaim: + claimName: pvc1 + - name: pvc2-vm + claimName: pvc2 + ``` + to exclude Restic backup of volume `pvc1-vm`, you would run: + + ```bash + kubectl -n sample annotate pod/app1 backup.velero.io/backup-volumes-excludes=pvc1-vm + ``` + +2. Take a Velero backup: + + ```bash + velero backup create BACKUP_NAME --default-volumes-to-restic OTHER_OPTIONS + ``` + + The above steps uses the opt-out approach on a per backup basis. + + Alternatively, this behavior may be enabled on all velero backups running the `velero install` command with the `--default-volumes-to-restic` flag. Refer [install overview][11] for details. + +3. When the backup completes, view information about the backups: + + ```bash + velero backup describe YOUR_BACKUP_NAME + ``` + ```bash + kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml + ``` + +### Using opt-in pod volume backup + +Velero, by default, uses this approach to discover pod volumes that need to be backed up using Restic. Every pod containing a volume to be backed up using Restic must be annotated with the volume's name using the `backup.velero.io/backup-volumes` annotation. + +Instructions to back up using this approach are as follows: + +1. Run the following for each pod that contains a volume to back up: + + ```bash + kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... + ``` + + where the volume names are the names of the volumes in the pod spec. + + For example, for the following pod: + + ```yaml + apiVersion: v1 + kind: Pod + metadata: + name: sample + namespace: foo + spec: + containers: + - image: k8s.gcr.io/test-webserver + name: test-webserver + volumeMounts: + - name: pvc-volume + mountPath: /volume-1 + - name: emptydir-volume + mountPath: /volume-2 + volumes: + - name: pvc-volume + persistentVolumeClaim: + claimName: test-volume-claim + - name: emptydir-volume + emptyDir: {} + ``` + + You'd run: + + ```bash + kubectl -n foo annotate pod/sample backup.velero.io/backup-volumes=pvc-volume,emptydir-volume + ``` + + This annotation can also be provided in a pod template spec if you use a controller to manage your pods. + +1. Take a Velero backup: + + ```bash + velero backup create NAME OPTIONS... + ``` + +1. When the backup completes, view information about the backups: + + ```bash + velero backup describe YOUR_BACKUP_NAME + ``` + ```bash + kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml + ``` + +## To restore + +Regardless of how volumes are discovered for backup using Restic, the process of restoring remains the same. + +1. Restore from your Velero backup: + + ```bash + velero restore create --from-backup BACKUP_NAME OPTIONS... + ``` + +1. When the restore completes, view information about your pod volume restores: + + ```bash + velero restore describe YOUR_RESTORE_NAME + ``` + ```bash + kubectl -n velero get podvolumerestores -l velero.io/restore-name=YOUR_RESTORE_NAME -o yaml + ``` + +## Limitations + +- `hostPath` volumes are not supported. [Local persistent volumes][4] are supported. +- Those of you familiar with [restic][1] may know that it encrypts all of its data. Velero uses a static, +common encryption key for all Restic repositories it creates. **This means that anyone who has access to your +bucket can decrypt your Restic backup data**. Make sure that you limit access to the Restic bucket +appropriately. +- An incremental backup chain will be maintained across pod reschedules for PVCs. However, for pod volumes that are *not* +PVCs, such as `emptyDir` volumes, when a pod is deleted/recreated (for example, by a ReplicaSet/Deployment), the next backup of those +volumes will be full rather than incremental, because the pod volume's lifecycle is assumed to be defined by its pod. +- Restic scans each file in a single thread. This means that large files (such as ones storing a database) will take a long time to scan for data deduplication, even if the actual +difference is small. +- If you plan to use Velero's Restic integration to backup 100GB of data or more, you may need to [customize the resource limits](/docs/main/customize-installation/#customize-resource-requests-and-limits) to make sure backups complete successfully. +- Velero's Restic integration backs up data from volumes by accessing the node's filesystem, on which the pod is running. For this reason, Velero's Restic integration can only backup volumes that are mounted by a pod and not directly from the PVC. For orphan PVC/PV pairs (without running pods), some Velero users overcame this limitation running a staging pod (i.e. a busybox or alpine container with an infinite sleep) to mount these PVC/PV pairs prior taking a Velero backup. + +## Customize Restore Helper Container + +Velero uses a helper init container when performing a Restic restore. By default, the image for this container is `velero/velero-restic-restore-helper:`, +where `VERSION` matches the version/tag of the main Velero image. You can customize the image that is used for this helper by creating a ConfigMap in the Velero namespace with +the alternate image. + +In addition, you can customize the resource requirements for the init container, should you need. + +The ConfigMap must look like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + # any name can be used; Velero uses the labels (below) + # to identify it rather than the name + name: restic-restore-action-config + # must be in the velero namespace + namespace: velero + # the below labels should be used verbatim in your + # ConfigMap. + labels: + # this value-less label identifies the ConfigMap as + # config for a plugin (i.e. the built-in restic restore + # item action plugin) + velero.io/plugin-config: "" + # this label identifies the name and kind of plugin + # that this ConfigMap is for. + velero.io/restic: RestoreItemAction +data: + # The value for "image" can either include a tag or not; + # if the tag is *not* included, the tag from the main Velero + # image will automatically be used. + image: myregistry.io/my-custom-helper-image[:OPTIONAL_TAG] + + # "cpuRequest" sets the request.cpu value on the restic init containers during restore. + # If not set, it will default to "100m". A value of "0" is treated as unbounded. + cpuRequest: 200m + + # "memRequest" sets the request.memory value on the restic init containers during restore. + # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. + memRequest: 128Mi + + # "cpuLimit" sets the request.cpu value on the restic init containers during restore. + # If not set, it will default to "100m". A value of "0" is treated as unbounded. + cpuLimit: 200m + + # "memLimit" sets the request.memory value on the restic init containers during restore. + # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. + memLimit: 128Mi + + # "secCtxRunAsUser" sets the securityContext.runAsUser value on the restic init containers during restore. + secCtxRunAsUser: 1001 + + # "secCtxRunAsGroup" sets the securityContext.runAsGroup value on the restic init containers during restore. + secCtxRunAsGroup: 999 + + # "secCtxAllowPrivilegeEscalation" sets the securityContext.allowPrivilegeEscalation value on the restic init containers during restore. + secCtxAllowPrivilegeEscalation: false + + # "secCtx" sets the securityContext object value on the restic init containers during restore. + # This key override `secCtxRunAsUser`, `secCtxRunAsGroup`, `secCtxAllowPrivilegeEscalation` if `secCtx.runAsUser`, `secCtx.runAsGroup` or `secCtx.allowPrivilegeEscalation` are set. + secCtx: | + capabilities: + drop: + - ALL + add: [] + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsUser: 1001 + runAsGroup: 999 + +``` + +## Troubleshooting + +Run the following checks: + +Are your Velero server and daemonset pods running? + +```bash +kubectl get pods -n velero +``` + +Does your Restic repository exist, and is it ready? + +```bash +velero restic repo get + +velero restic repo get REPO_NAME -o yaml +``` + +Are there any errors in your Velero backup/restore? + +```bash +velero backup describe BACKUP_NAME +velero backup logs BACKUP_NAME + +velero restore describe RESTORE_NAME +velero restore logs RESTORE_NAME +``` + +What is the status of your pod volume backups/restores? + +```bash +kubectl -n velero get podvolumebackups -l velero.io/backup-name=BACKUP_NAME -o yaml + +kubectl -n velero get podvolumerestores -l velero.io/restore-name=RESTORE_NAME -o yaml +``` + +Is there any useful information in the Velero server or daemon pod logs? + +```bash +kubectl -n velero logs deploy/velero +kubectl -n velero logs DAEMON_POD_NAME +``` + +**NOTE**: You can increase the verbosity of the pod logs by adding `--log-level=debug` as an argument +to the container command in the deployment/daemonset pod template spec. + +## How backup and restore work with Restic + +Velero has three custom resource definitions and associated controllers: + +- `ResticRepository` - represents/manages the lifecycle of Velero's [restic repositories][5]. Velero creates +a Restic repository per namespace when the first Restic backup for a namespace is requested. The controller +for this custom resource executes Restic repository lifecycle commands -- `restic init`, `restic check`, +and `restic prune`. + + You can see information about your Velero's Restic repositories by running `velero restic repo get`. + +- `PodVolumeBackup` - represents a Restic backup of a volume in a pod. The main Velero backup process creates +one or more of these when it finds an annotated pod. Each node in the cluster runs a controller for this +resource (in a daemonset) that handles the `PodVolumeBackups` for pods on that node. The controller executes +`restic backup` commands to backup pod volume data. + +- `PodVolumeRestore` - represents a Restic restore of a pod volume. The main Velero restore process creates one +or more of these when it encounters a pod that has associated Restic backups. Each node in the cluster runs a +controller for this resource (in the same daemonset as above) that handles the `PodVolumeRestores` for pods +on that node. The controller executes `restic restore` commands to restore pod volume data. + +### Backup + +1. Based on configuration, the main Velero backup process uses the opt-in or opt-out approach to check each pod that it's backing up for the volumes to be backed up using Restic. +1. When found, Velero first ensures a Restic repository exists for the pod's namespace, by: + - checking if a `ResticRepository` custom resource already exists + - if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it +1. Velero then creates a `PodVolumeBackup` custom resource per volume listed in the pod annotation +1. The main Velero process now waits for the `PodVolumeBackup` resources to complete or fail +1. Meanwhile, each `PodVolumeBackup` is handled by the controller on the appropriate node, which: + - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data + - finds the pod volume's subdirectory within the above volume + - runs `restic backup` + - updates the status of the custom resource to `Completed` or `Failed` +1. As each `PodVolumeBackup` finishes, the main Velero process adds it to the Velero backup in a file named `-podvolumebackups.json.gz`. This file gets uploaded to object storage alongside the backup tarball. It will be used for restores, as seen in the next section. + +### Restore + +1. The main Velero restore process checks each existing `PodVolumeBackup` custom resource in the cluster to backup from. +1. For each `PodVolumeBackup` found, Velero first ensures a Restic repository exists for the pod's namespace, by: + - checking if a `ResticRepository` custom resource already exists + - if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it (note that + in this case, the actual repository should already exist in object storage, so the Velero controller will simply + check it for integrity) +1. Velero adds an init container to the pod, whose job is to wait for all Restic restores for the pod to complete (more +on this shortly) +1. Velero creates the pod, with the added init container, by submitting it to the Kubernetes API. Then, the Kubernetes scheduler schedules this pod to a worker node, and the pod must be in a running state. If the pod fails to start for some reason (i.e. lack of cluster resources), the Restic restore will not be done. +1. Velero creates a `PodVolumeRestore` custom resource for each volume to be restored in the pod +1. The main Velero process now waits for each `PodVolumeRestore` resource to complete or fail +1. Meanwhile, each `PodVolumeRestore` is handled by the controller on the appropriate node, which: + - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data + - waits for the pod to be running the init container + - finds the pod volume's subdirectory within the above volume + - runs `restic restore` + - on success, writes a file into the pod volume, in a `.velero` subdirectory, whose name is the UID of the Velero restore + that this pod volume restore is for + - updates the status of the custom resource to `Completed` or `Failed` +1. The init container that was added to the pod is running a process that waits until it finds a file +within each restored volume, under `.velero`, whose name is the UID of the Velero restore being run +1. Once all such files are found, the init container's process terminates successfully and the pod moves +on to running other init containers/the main containers. + +Velero won't restore a resource if a that resource is scaled to 0 and already exists in the cluster. If Velero restored the requested pods in this scenario, the Kubernetes reconciliation loops that manage resources would delete the running pods because its scaled to be 0. Velero will be able to restore once the resources is scaled up, and the pods are created and remain running. + +## 3rd party controllers + +### Monitor backup annotation + +Velero does not provide a mechanism to detect persistent volume claims that are missing the Restic backup annotation. + +To solve this, a controller was written by Thomann Bits&Beats: [velero-pvc-watcher][7] + +[1]: https://github.com/restic/restic +[2]: customize-installation.md#enable-restic-integration +[3]: https://github.com/vmware-tanzu/velero/releases/ +[4]: https://kubernetes.io/docs/concepts/storage/volumes/#local +[5]: http://restic.readthedocs.io/en/latest/100_references.html#terminology +[6]: https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation +[7]: https://github.com/bitsbeats/velero-pvc-watcher +[8]: https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv +[9]: https://github.com/restic/restic/issues/1800 +[11]: customize-installation.md#default-pod-volume-backup-to-restic diff --git a/site/content/docs/v1.9/restore-hooks.md b/site/content/docs/v1.9/restore-hooks.md new file mode 100644 index 0000000000..0ec36c5d7a --- /dev/null +++ b/site/content/docs/v1.9/restore-hooks.md @@ -0,0 +1,261 @@ +--- +title: "Restore Hooks" +layout: docs +--- + +Velero supports Restore Hooks, custom actions that can be executed during or after the restore process. There are two kinds of Restore Hooks: + +1. InitContainer Restore Hooks: These will add init containers into restored pods to perform any necessary setup before the application containers of the restored pod can start. +1. Exec Restore Hooks: These can be used to execute custom commands or scripts in containers of a restored Kubernetes pod. + +## InitContainer Restore Hooks + +Use an `InitContainer` hook to add init containers into a pod before it's restored. You can use these init containers to run any setup needed for the pod to resume running from its backed-up state. +The InitContainer added by the restore hook will be the first init container in the `podSpec` of the restored pod. +In the case where the pod had volumes backed up using restic, then, the restore hook InitContainer will be added after the `restic-wait` InitContainer. + +NOTE: This ordering can be altered by any mutating webhooks that may be installed in the cluster. + +There are two ways to specify `InitContainer` restore hooks: +1. Specifying restore hooks in annotations +1. Specifying restore hooks in the restore spec + +### Specifying Restore Hooks As Pod Annotations + +Below are the annotations that can be added to a pod to specify restore hooks: +* `init.hook.restore.velero.io/container-image` + * The container image for the init container to be added. +* `init.hook.restore.velero.io/container-name` + * The name for the init container that is being added. +* `init.hook.restore.velero.io/command` + * This is the `ENTRYPOINT` for the init container being added. This command is not executed within a shell and the container image's `ENTRYPOINT` is used if this is not provided. + +#### Example + +Use the below commands to add annotations to the pods before taking a backup. + +```bash +$ kubectl annotate pod -n \ + init.hook.restore.velero.io/container-name=restore-hook \ + init.hook.restore.velero.io/container-image=alpine:latest \ + init.hook.restore.velero.io/command='["/bin/ash", "-c", "date"]' +``` + +With the annotation above, Velero will add the following init container to the pod when it's restored. + +```json +{ + "command": [ + "/bin/ash", + "-c", + "date" + ], + "image": "alpine:latest", + "imagePullPolicy": "Always", + "name": "restore-hook" + ... +} +``` + +### Specifying Restore Hooks In Restore Spec + +Init container restore hooks can also be specified using the `RestoreSpec`. +Please refer to the documentation on the [Restore API Type][1] for how to specify hooks in the Restore spec. + +#### Example + +Below is an example of specifying restore hooks in `RestoreSpec` + +```yaml +apiVersion: velero.io/v1 +kind: Restore +metadata: + name: r2 + namespace: velero +spec: + backupName: b2 + excludedResources: + ... + includedNamespaces: + - '*' + hooks: + resources: + - name: restore-hook-1 + includedNamespaces: + - app + postHooks: + - init: + initContainers: + - name: restore-hook-init1 + image: alpine:latest + volumeMounts: + - mountPath: /restores/pvc1-vm + name: pvc1-vm + command: + - /bin/ash + - -c + - echo -n "FOOBARBAZ" >> /restores/pvc1-vm/foobarbaz + - name: restore-hook-init2 + image: alpine:latest + volumeMounts: + - mountPath: /restores/pvc2-vm + name: pvc2-vm + command: + - /bin/ash + - -c + - echo -n "DEADFEED" >> /restores/pvc2-vm/deadfeed +``` + +The `hooks` in the above `RestoreSpec`, when restored, will add two init containers to every pod in the `app` namespace + +```json +{ + "command": [ + "/bin/ash", + "-c", + "echo -n \"FOOBARBAZ\" >> /restores/pvc1-vm/foobarbaz" + ], + "image": "alpine:latest", + "imagePullPolicy": "Always", + "name": "restore-hook-init1", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/restores/pvc1-vm", + "name": "pvc1-vm" + } + ] + ... +} +``` + +and + +```json +{ + "command": [ + "/bin/ash", + "-c", + "echo -n \"DEADFEED\" >> /restores/pvc2-vm/deadfeed" + ], + "image": "alpine:latest", + "imagePullPolicy": "Always", + "name": "restore-hook-init2", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/restores/pvc2-vm", + "name": "pvc2-vm" + } + ] + ... +} +``` + +## Exec Restore Hooks + +Use an Exec Restore hook to execute commands in a restored pod's containers after they start. + +There are two ways to specify `Exec` restore hooks: +1. Specifying exec restore hooks in annotations +1. Specifying exec restore hooks in the restore spec + +If a pod has the annotation `post.hook.restore.velero.io/command` then that is the only hook that will be executed in the pod. +No hooks from the restore spec will be executed in that pod. + +### Specifying Exec Restore Hooks As Pod Annotations + +Below are the annotations that can be added to a pod to specify exec restore hooks: +* `post.hook.restore.velero.io/container` + * The container name where the hook will be executed. Defaults to the first container. Optional. +* `post.hook.restore.velero.io/command` + * The command that will be executed in the container. Required. +* `post.hook.restore.velero.io/on-error` + * How to handle execution failures. Valid values are `Fail` and `Continue`. Defaults to `Continue`. With `Continue` mode, execution failures are logged only. With `Fail` mode, no more restore hooks will be executed in any container in any pod and the status of the Restore will be `PartiallyFailed`. Optional. +* `post.hook.restore.velero.io/exec-timeout` + * How long to wait once execution begins. Defaults to 30 seconds. Optional. +* `post.hook.restore.velero.io/wait-timeout` + * How long to wait for a container to become ready. This should be long enough for the container to start plus any preceding hooks in the same container to complete. The wait timeout begins when the container is restored and may require time for the image to pull and volumes to mount. If not set the restore will wait indefinitely. Optional. + +#### Example + +Use the below commands to add annotations to the pods before taking a backup. + +```bash +$ kubectl annotate pod -n \ + post.hook.restore.velero.io/container=postgres \ + post.hook.restore.velero.io/command='["/bin/bash", "-c", "psql < /backup/backup.sql"]' \ + post.hook.restore.velero.io/wait-timeout=5m \ + post.hook.restore.velero.io/exec-timeout=45s \ + post.hook.restore.velero.io/on-error=Continue +``` + +### Specifying Exec Restore Hooks in Restore Spec + +Exec restore hooks can also be specified using the `RestoreSpec`. +Please refer to the documentation on the [Restore API Type][1] for how to specify hooks in the Restore spec. + +#### Multiple Exec Restore Hooks Example + +Below is an example of specifying restore hooks in a `RestoreSpec`. +When using the restore spec it is possible to specify multiple hooks for a single pod, as this example demonstrates. + +All hooks applicable to a single container will be executed sequentially in that container once it starts. +The ordering of hooks executed in a single container follows the order of the restore spec. +In this example, the `pg_isready` hook is guaranteed to run before the `psql` hook because they both apply to the same container and the `pg_isready` hook is defined first. + +If a pod has multiple containers with applicable hooks, all hooks for a single container will be executed before executing hooks in another container. +In this example, if the postgres container starts before the sidecar container, both postgres hooks will run before the hook in the sidecar. +This means the sidecar container may be running for several minutes before its hook is executed. + +Velero guarantees that no two hooks for a single pod are executed in parallel, but hooks executing in different pods may run in parallel. + + +```yaml +apiVersion: velero.io/v1 +kind: Restore +metadata: + name: r2 + namespace: velero +spec: + backupName: b2 + excludedResources: + ... + includedNamespaces: + - '*' + hooks: + resources: + - name: restore-hook-1 + includedNamespaces: + - app + postHooks: + - exec: + execTimeout: 1m + waitTimeout: 5m + onError: Fail + container: postgres + command: + - /bin/bash + - '-c' + - 'while ! pg_isready; do sleep 1; done' + - exec: + container: postgres + waitTimeout: 6m + execTimeout: 1m + command: + - /bin/bash + - '-c' + - 'psql < /backup/backup.sql' + - exec: + container: sidecar + command: + - /bin/bash + - '-c' + - 'date > /start' +``` + +[1]: api-types/restore.md diff --git a/site/content/docs/v1.9/restore-reference.md b/site/content/docs/v1.9/restore-reference.md new file mode 100644 index 0000000000..0511f6512f --- /dev/null +++ b/site/content/docs/v1.9/restore-reference.md @@ -0,0 +1,272 @@ +--- +title: "Restore Reference" +layout: docs +--- + +The page outlines how to use the `velero restore` command, configuration options for restores, and describes the main process Velero uses to perform restores. + +## Restore command-line options +To see all commands for restores, run `velero restore --help`. + +To see all options associated with a specific command, provide the `--help` flag to that command. For example, `velero restore create --help` shows all options associated with the `create` command. + +```Usage: + velero restore [command] + +Available Commands: + create Create a restore + delete Delete restores + describe Describe restores + get Get restores + logs Get restore logs +``` + +## Detailed Restore workflow + +The following is an overview of Velero's restore process that starts after you run `velero restore create`. + +1. The Velero client makes a call to the Kubernetes API server to create a [`Restore`](api-types/restore.md) object. + +1. The `RestoreController` notices the new Restore object and performs validation. + +1. The `RestoreController` fetches basic information about the backup being restored, like the [BackupStorageLocation](locations.md) (BSL). It also fetches a tarball of the cluster resources in the backup, any volumes that will be restored using Restic, and any volume snapshots to be restored. + +1. The `RestoreController` then extracts the tarball of backup cluster resources to the /tmp folder and performs some pre-processing on the resources, including: + + * Sorting the resources to help Velero decide the [restore order](#resource-restore-order) to use. + + * Attempting to discover the resources by their Kubernetes [Group Version Resource (GVR)](https://kubernetes.io/docs/reference/using-api/api-concepts/). If a resource is not discoverable, Velero will exclude it from the restore. See more about how [Velero backs up API versions](#backed-up-api-versions). + + * Applying any configured [resource filters](resource-filtering.md). + + * Verify the target namespace, if you have configured [`--namespace-mappings`](#restoring-into-a-different-namespace) restore option. + + +1. The `RestoreController` begins restoring the eligible resources one at a time. Velero extracts the current resource into a Kubernetes resource object. Depending on the type of resource and restore options you specified, Velero will make the following modifications to the resource or preparations to the target cluster before attempting to create the resource: + + * The `RestoreController` makes sure the target namespace exists. If the target namespace does not exist, then the `RestoreController` will create a new one on the cluster. + + * If the resource is a Persistent Volume (PV), the `RestoreController` will [rename](#persistent-volume-rename) the PV and [remap](#restoring-into-a-different-namespace) its namespace. + + * If the resource is a Persistent Volume Claim (PVC), the `RestoreController` will modify the [PVC metadata](#pvc-restore). + + * Execute the resource’s `RestoreItemAction` [custom plugins](custom-plugins/), if you have configured one. + + * Update the resource object’s namespace if you've configured [namespace remapping](#restoring-into-a-different-namespace). + + * The `RestoreController` adds a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` with the restore name to the resource. This can help you easily identify restored resources and which backup they were restored from. + +1. The `RestoreController` creates the resource object on the target cluster. If the resource is a PV then the `RestoreController` will restore the PV data from the [durable snapshot](#durable-snapshot-pv-restore), [Restic](#restic-pv-restore), or [CSI snapshot](#csi-pv-restore) depending on how the PV was backed up. + + If the resource already exists in the target cluster, which is determined by the Kubernetes API during resource creation, the `RestoreController` will skip the resource. The only [exception](#restore-existing-resource-policy) are Service Accounts, which Velero will attempt to merge differences between the backed up ServiceAccount into the ServiceAccount on the target cluster. You can [change the default existing resource restore policy](#restore-existing-resource-policy) to update resources instead of skipping them using the `--existing-resource-policy`. + +1. Once the resource is created on the target cluster, Velero may take some additional steps or wait for additional processes to complete before moving onto the next resource to restore. + + * If the resource is a Pod, the `RestoreController` will execute any [Restore Hooks](restore-hooks.md) and wait for the hook to finish. + * If the resource is a PV restored by Restic, the `RestoreController` waits for Restic’s restore to complete. The `RestoreController` sets a timeout for any resources restored with Restic during a restore. The default timeout is 4 hours, but you can configure this be setting using `--restic-timeout` restore option. + * If the resource is a Custom Resource Definition, the `RestoreController` waits for its availability in the cluster. The timeout is 1 minute. + + If any failures happen finishing these steps, the `RestoreController` will log an error in the restore result and will continue restoring. + +## Restore order + +By default, Velero will restore resources in the following order: + +* Custom Resource Definitions +* Mamespaces +* StorageClasses +* VolumeSnapshotClass +* VolumeSnapshotContents +* VolumeSnapshots +* PersistentVolumes +* PersistentVolumeClaims +* Secrets +* ConfigMaps +* ServiceAccounts +* LimitRanges +* Pods +* ReplicaSets +* Clusters +* ClusterResourceSets + +It's recommended that you use the default order for your restores. You are able to customize this order if you need to by setting the `--restore-resource-priorities` flag on the Velero server and specifying a different resource order. This customized order will apply to all future restores. You don't have to specify all resources in the `--restore-resource-priorities` flag. Velero will append resources not listed to the end of your customized list in alphabetical order. + +```shell +velero server \ +--restore-resource-priorities=customresourcedefinitions,namespaces,storageclasses,\ +volumesnapshotclass.snapshot.storage.k8s.io,volumesnapshotcontents.snapshot.storage.k8s.io,\ +volumesnapshots.snapshot.storage.k8s.io,persistentvolumes,persistentvolumeclaims,secrets,\ +configmaps,serviceaccounts,limitranges,pods,replicasets.apps,clusters.cluster.x-k8s.io,\ +clusterresourcesets.addons.cluster.x-k8s.io +``` + + +## Restoring Persistent Volumes and Persistent Volume Claims + +Velero has three approaches when restoring a PV, depending on how the backup was taken. + +1. When restoring a snapshot, Velero statically creates the PV and then binds it to a restored PVC. Velero's PV rename and remap process is used only in this case because this is the only case where Velero creates the PV resource directly. +1. When restoring with Restic, Velero uses Kubernetes’ [dynamic provision process](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) to provision the PV after creating the PVC. In this case, the PV object is not actually created by Velero. +1. When restoring with the [CSI plugin](csi.md), the PV is created from a CSI snapshot by the CSI driver. Velero doesn’t create the PV directly. Instead Velero creates a PVC with its DataSource referring to the CSI VolumeSnapshot object. + +### Snapshot PV Restore + +PV data backed up by durable snapshots is restored by VolumeSnapshot plugins. Velero calls the plugins’ interface to create a volume from a snapshot. The plugin returns the volume’s `volumeID`. This ID is created by storage vendors and will be updated in the PV object created by Velero, so that the PV object is connected to the volume restored from a snapshot. + +### Restic PV Restore + +For more information on Restic restores, see the [Restic integration](restic.md#restore) page. + +### CSI PV Restore + +A PV backed up by CSI snapshots is restored by the [CSI plugin](csi). This happens when restoring the PVC object that has been snapshotted by CSI. The CSI VolumeSnapshot object name is specified with the PVC during backup as the annotation `velero.io/volume-snapshot-name`. After validating the VolumeSnapshot object, Velero updates the PVC by adding a `DataSource` field and setting its value to the VolumeSnapshot name. + +### Persistent Volume Rename + +When restoring PVs, if the PV being restored does not exist on the target cluster, Velero will create the PV using the name from the backup. Velero will rename a PV before restoring if both of the following conditions are met: + +1. The PV already exists on the target cluster. +1. The PV’s claim namespace has been [remapped](#restoring-into-a-different-namespace). + +If both conditions are met, Velero will create the PV with a new name. The new name is the prefix `velero-clone-` and a random UUID. Velero also preserves the original name of the PV by adding an annotation `velero.io/original-pv-name` to the restored PV object. + +If you attempt to restore the PV's referenced PVC into its original namespace without remapping the namespace, Velero will not rename the PV. If a PV's referenced PVC exists already for that namespace, the restored PV creation attempt will fail, with an `Already Exist` error from the Kubernetes API Server. + +### PVC Restore + +PVC objects are created the same way as other Kubernetes resources during a restore, with some specific changes: +* For a dynamic binding PVCs, Velero removes the fields related to bindings from the PVC object. This enables the default Kubernetes [dynamic binding process](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#binding) to be used for this PVC. The fields include: + * volumeName + * pv.kubernetes.io/bind-completed annotation + * pv.kubernetes.io/bound-by-controller annotation +* For a PVC that is bound by Velero Restore, if the target PV has been renamed by the [PV restore process](#persistent-volume-rename), the RestoreController renames the `volumeName` field of the PVC object. + +### Changing PV/PVC Storage Classes + +Velero can change the storage class of persistent volumes and persistent volume claims during restores. To configure a storage class mapping, create a config map in the Velero namespace like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + # any name can be used; Velero uses the labels (below) + # to identify it rather than the name + name: change-storage-class-config + # must be in the velero namespace + namespace: velero + # the below labels should be used verbatim in your + # ConfigMap. + labels: + # this value-less label identifies the ConfigMap as + # config for a plugin (i.e. the built-in restore item action plugin) + velero.io/plugin-config: "" + # this label identifies the name and kind of plugin + # that this ConfigMap is for. + velero.io/change-storage-class: RestoreItemAction +data: + # add 1+ key-value pairs here, where the key is the old + # storage class name and the value is the new storage + # class name. + : +``` + +### Changing PVC selected-node + +Velero can update the selected-node annotation of persistent volume claim during restores, if selected-node doesn't exist in the cluster then it will remove the selected-node annotation from PersistentVolumeClaim. To configure a node mapping, create a config map in the Velero namespace like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + # any name can be used; Velero uses the labels (below) + # to identify it rather than the name + name: change-pvc-node-selector-config + # must be in the velero namespace + namespace: velero + # the below labels should be used verbatim in your + # ConfigMap. + labels: + # this value-less label identifies the ConfigMap as + # config for a plugin (i.e. the built-in restore item action plugin) + velero.io/plugin-config: "" + # this label identifies the name and kind of plugin + # that this ConfigMap is for. + velero.io/change-pvc-node-selector: RestoreItemAction +data: + # add 1+ key-value pairs here, where the key is the old + # node name and the value is the new node name. + : +``` + +## Restoring into a different namespace + +Velero can restore resources into a different namespace than the one they were backed up from. To do this, use the `--namespace-mappings` flag: + +```bash +velero restore create \ + --from-backup \ + --namespace-mappings old-ns-1:new-ns-1,old-ns-2:new-ns-2 +``` + +For example, A Persistent Volume object has a reference to the Persistent Volume Claim’s namespace in the field `Spec.ClaimRef.Namespace`. If you specify that Velero should remap the target namespace during the restore, Velero will change the `Spec.ClaimRef.Namespace` field on the PV object from `old-ns-1` to `new-ns-1`. + +## Restore existing resource policy + +By default, Velero is configured to be non-destructive during a restore. This means that it will never overwrite data that already exists in your cluster. When Velero attempts to create a resource during a restore, the resource being restored is compared to the existing resources on the target cluster by the Kubernetes API Server. If the resource already exists in the target cluster, Velero skips restoring the current resource and moves onto the next resource to restore, without making any changes to the target cluster. + +An exception to the default restore policy is ServiceAccounts. When restoring a ServiceAccount that already exists on the target cluster, Velero will attempt to merge the fields of the ServiceAccount from the backup into the existing ServiceAccount. Secrets and ImagePullSecrets are appended from the backed-up ServiceAccount. Velero adds any non-existing labels and annotations from the backed-up ServiceAccount to the existing resource, leaving the existing labels and annotations in place. + +You can change this policy for a restore by using the `--existing-resource-policy` restore flag. The available options are `none` (default) and `update`. If you choose to `update` existing resources during a restore (`--existing-resource-policy=update`), Velero will attempt to update an existing resource to match the resource being restored: + +* If the existing resource in the target cluster is the same as the resource Velero is attempting to restore, Velero will add a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` label with the restore name to the existing resource. If patching the labels fails, Velero adds a restore error and continues restoring the next resource. + +* If the existing resource in the target cluster is different from the backup, Velero will first try to patch the existing resource to match the backup resource. If the patch is successful, Velero will add a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` label with the restore name to the existing resource. If the patch fails, Velero adds a restore warning and tries to add the `velero.io/backup-name` and `velero.io/restore-name` labels on the resource. If the labels patch also fails, then Velero logs a restore error and continues restoring the next resource. + +You can also configure the existing resource policy in a [Restore](api-types/restore.md) object. + +## Removing a Restore object + +There are two ways to delete a Restore object: + +1. Deleting with `velero restore delete` will delete the Custom Resource representing the restore, along with its individual log and results files. It will not delete any objects that were created by the restore in your cluster. +2. Deleting with `kubectl -n velero delete restore` will delete the Custom Resource representing the restore. It will not delete restore log or results files from object storage, or any objects that were created during the restore in your cluster. + +## What happens to NodePorts when restoring Services + +During a restore, Velero deletes **Auto assigned** NodePorts by default and Services get new **auto assigned** nodePorts after restore. + +Velero auto detects **explicitly specified** NodePorts using **`last-applied-config`** annotation and they are **preserved** after restore. NodePorts can be explicitly specified as `.spec.ports[*].nodePort` field on Service definition. + +### Always Preserve NodePorts + +It is not always possible to set nodePorts explicitly on some big clusters because of operational complexity. As the Kubernetes [NodePort documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) states, "if you want a specific port number, you can specify a value in the `nodePort` field. The control plane will either allocate you that port or report that the API transaction failed. This means that you need to take care of possible port collisions yourself. You also have to use a valid port number, one that's inside the range configured for NodePort use."" + +The clusters which are not explicitly specifying nodePorts may still need to restore original NodePorts in the event of a disaster. Auto assigned nodePorts are typically defined on Load Balancers located in front of cluster. Changing all these nodePorts on Load Balancers is another operation complexity you are responsible for updating after disaster if nodePorts are changed. + +Use the `velero restore create ` command's `--preserve-nodeports` flag to preserve Service nodePorts always, regardless of whether nodePorts are explicitly specified or not. This flag is used for preserving the original nodePorts from a backup and can be used as `--preserve-nodeports` or `--preserve-nodeports=true`. If this flag is present, Velero will not remove the nodePorts when restoring a Service, but will try to use the nodePorts from the backup. + +Trying to preserve nodePorts may cause port conflicts when restoring on situations below: + +- If the nodePort from the backup is already allocated on the target cluster then Velero prints error log as shown below and continues the restore operation. + + ``` + time="2020-11-23T12:58:31+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123125825 + + time="2020-11-23T12:58:31+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is already allocated" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123125825 + ``` + +- If the nodePort from the backup is not in the nodePort range of target cluster then Velero prints error log as below and continues with the restore operation. Kubernetes default nodePort range is 30000-32767 but on the example cluster nodePort range is 20000-22767 and tried to restore Service with nodePort 31536. + + ``` + time="2020-11-23T13:09:17+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123130915 + + time="2020-11-23T13:09:17+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is not in the valid range. The range of valid ports is 20000-22767" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123130915 + ``` diff --git a/site/content/docs/v1.9/run-locally.md b/site/content/docs/v1.9/run-locally.md new file mode 100644 index 0000000000..897ee18936 --- /dev/null +++ b/site/content/docs/v1.9/run-locally.md @@ -0,0 +1,53 @@ +--- +title: "Run Velero locally in development" +layout: docs +--- + +Running the Velero server locally can speed up iterative development. This eliminates the need to rebuild the Velero server +image and redeploy it to the cluster with each change. + +## Run Velero locally with a remote cluster + +Velero runs against the Kubernetes API server as the endpoint (as per the `kubeconfig` configuration), so both the Velero server and client use the same `client-go` to communicate with Kubernetes. This means the Velero server can be run locally just as functionally as if it was running in the remote cluster. + +### Prerequisites + +When running Velero, you will need to ensure that you set up all of the following: + +* Appropriate RBAC permissions in the cluster + * Read access for all data from the source cluster and namespaces + * Write access to the target cluster and namespaces +* Cloud provider credentials + * Read/write access to volumes + * Read/write access to object storage for backup data +* A [BackupStorageLocation][20] object definition for the Velero server +* (Optional) A [VolumeSnapshotLocation][21] object definition for the Velero server, to take PV snapshots + +### 1. Install Velero + +See documentation on how to install Velero in some specific providers: [Install overview][22] + +### 2. Scale deployment down to zero + +After you use the `velero install` command to install Velero into your cluster, you scale the Velero deployment down to 0 so it is not simultaneously being run on the remote cluster and potentially causing things to get out of sync: + +`kubectl scale --replicas=0 deployment velero -n velero` + +#### 3. Start the Velero server locally + +* To run the server locally, use the full path according to the binary you need. Example, if you are on a Mac, and using `AWS` as a provider, this is how to run the binary you built from source using the full path: `AWS_SHARED_CREDENTIALS_FILE= ./_output/bin/darwin/amd64/velero`. Alternatively, you may add the `velero` binary to your `PATH`. + +* Start the server: `velero server [CLI flags]`. The following CLI flags may be useful to customize, but see `velero server --help` for full details: + * `--log-level`: set the Velero server's log level (default `info`, use `debug` for the most logging) + * `--kubeconfig`: set the path to the kubeconfig file the Velero server uses to talk to the Kubernetes apiserver (default `$KUBECONFIG`) + * `--namespace`: the set namespace where the Velero server should look for backups, schedules, restores (default `velero`) + * `--plugin-dir`: set the directory where the Velero server looks for plugins (default `/plugins`) + * The `--plugin-dir` flag requires the plugin binary to be present locally, and should be set to the directory containing this built binary. + * `--metrics-address`: set the bind address and port where Prometheus metrics are exposed (default `:8085`) + +[15]: https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#the-shared-credentials-file +[16]: https://cloud.google.com/docs/authentication/getting-started#setting_the_environment_variable +[18]: https://eksctl.io/ +[20]: api-types/backupstoragelocation.md +[21]: api-types/volumesnapshotlocation.md +[22]: basic-install.md diff --git a/site/content/docs/v1.9/self-signed-certificates.md b/site/content/docs/v1.9/self-signed-certificates.md new file mode 100644 index 0000000000..2aab5c1b97 --- /dev/null +++ b/site/content/docs/v1.9/self-signed-certificates.md @@ -0,0 +1,65 @@ +--- +title: "Use Velero with a storage provider secured by a self-signed certificate" +layout: docs +--- + +If you are using an S3-Compatible storage provider that is secured with a self-signed certificate, connections to the object store may fail with a `certificate signed by unknown authority` message. +To proceed, provide a certificate bundle when adding the storage provider. + +## Trusting a self-signed certificate during installation + +When using the `velero install` command, you can use the `--cacert` flag to provide a path +to a PEM-encoded certificate bundle to trust. + +```bash +velero install \ + --plugins + --provider \ + --bucket \ + --secret-file \ + --cacert +``` + +Velero will then automatically use the provided CA bundle to verify TLS connections to +that storage provider when backing up and restoring. + +## Trusting a self-signed certificate with the Velero client + +To use the describe, download, or logs commands to access a backup or restore contained +in storage secured by a self-signed certificate as in the above example, you must use +the `--cacert` flag to provide a path to the certificate to be trusted. + +```bash +velero backup describe my-backup --cacert +``` + +## Error with client certificate with custom S3 server + +In case you are using a custom S3-compatible server, you may encounter that the backup fails with an error similar to one below. + +``` +rpc error: code = Unknown desc = RequestError: send request failed caused by: +Get https://minio.com:3000/k8s-backup-bucket?delimiter=%2F&list-type=2&prefix=: remote error: tls: alert(116) +``` + +Error 116 represents certificate required as seen here in [error codes](https://datatracker.ietf.org/doc/html/rfc8446#appendix-B.2). +Velero as a client does not include its certificate while performing SSL handshake with the server. +From [TLS 1.3 spec](https://tools.ietf.org/html/rfc8446), verifying client certificate is optional on the server. +You will need to change this setting on the server to make it work. + + +## Skipping TLS verification + +**Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premise environment. Using this flag in production is not recommended. + +Velero provides a way for you to skip TLS verification on the object store when using the [AWS provider plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws) or [Restic](restic.md) by passing the `--insecure-skip-tls-verify` flag with the following Velero commands, + +* velero backup describe +* velero backup download +* velero backup logs +* velero restore describe +* velero restore log + +If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. + +Note that Velero's Restic integration uses Restic commands to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve interacting with Restic, Velero will add the Restic global command parameter `--insecure-tls` to Restic commands. diff --git a/site/content/docs/v1.9/start-contributing.md b/site/content/docs/v1.9/start-contributing.md new file mode 100644 index 0000000000..5f30ab3935 --- /dev/null +++ b/site/content/docs/v1.9/start-contributing.md @@ -0,0 +1,34 @@ +--- +title: "Start contributing" +layout: docs +--- + +## Before you start + +* Please familiarize yourself with the [Code of Conduct][1] before contributing. +* Also, see [CONTRIBUTING.md][2] for instructions on the developer certificate of origin that we require. + +## Creating a design doc + +Having a high level design document with the proposed change and the impacts helps the maintainers evaluate if a major change should be incorporated. + +To make a design pull request, you can copy the template found in the `design/_template.md` file into a new Markdown file. + +## Finding your way around + +You may join the Velero community and contribute in many different ways, including helping us design or test new features. For any significant feature we consider adding, we start with a design document. You may find a list of in progress new designs here: https://github.com/vmware-tanzu/velero/pulls?q=is%3Aopen+is%3Apr+label%3ADesign. Feel free to review and help us with your input. + +You can also vote on issues using :+1: and :-1:, as explained in our [Feature enhancement request][3] and [Bug issue][4] templates. This will help us quantify importance and prioritize issues. + +For information on how to connect with our maintainers and community, join our online meetings, or find good first issues, start on our [Velero community](https://velero.io/community/) page. + +Please browse our list of resources, including a playlist of past online community meetings, blog posts, and other resources to help you get familiar with our project: [Velero resources](https://velero.io/resources/). + +## Contributing + +If you are ready to jump in and test, add code, or help with documentation, please use the navigation on the left under `Contribute`. + +[1]: https://github.com/vmware-tanzu/velero/blob/v1.9/CODE_OF_CONDUCT.md +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/CONTRIBUTING.md +[3]: https://github.com/vmware-tanzu/velero/blob/v1.9/.github/ISSUE_TEMPLATE/feature-enhancement-request.md +[4]: https://github.com/vmware-tanzu/velero/blob/v1.9/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/site/content/docs/v1.9/style-guide.md b/site/content/docs/v1.9/style-guide.md new file mode 100644 index 0000000000..1b98e59dde --- /dev/null +++ b/site/content/docs/v1.9/style-guide.md @@ -0,0 +1,344 @@ +--- +title: "Documentation Style Guide" +layout: docs +--- + +_This style guide is adapted from the [Kubernetes style guide](https://kubernetes.io/docs/contribute/style/style-guide/)._ + +This page outlines writing style guidelines for the Velero documentation and you should use this page as a reference you write or edit content. Note that these are guidelines, not rules. Use your best judgment as you write documentation, and feel free to propose changes to these guidelines. Changes to the style guide are made by the Velero maintainers as a group. To propose a change or addition create an issue/PR, or add a suggestion to the [community meeting agenda](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA) and attend the meeting to participate in the discussion. + +The Velero documentation uses the [kramdown](https://kramdown.gettalong.org/) Markdown renderer. + +## Content best practices +### Use present tense + +{{< table caption="Do and Don't - Use present tense" >}} +|Do|Don't| +|--- |--- | +|This `command` starts a proxy.|This command will start a proxy.| +{{< /table >}} + +Exception: Use future or past tense if it is required to convey the correct meaning. + +### Use active voice + +{{< table caption="Do and Don't - Use active voice" >}} +|Do|Don't| +|--- |--- | +|You can explore the API using a browser.|The API can be explored using a browser.| +|The YAML file specifies the replica count.|The replica count is specified in the YAML file.| +{{< /table >}} + +Exception: Use passive voice if active voice leads to an awkward sentence construction. + +### Use simple and direct language + +Use simple and direct language. Avoid using unnecessary phrases, such as saying "please." + +{{< table caption="Do and Don't - Use simple and direct language" >}} +|Do|Don't| +|--- |--- | +|To create a ReplicaSet, ...|In order to create a ReplicaSet, ...| +|See the configuration file.|Please see the configuration file.| +|View the Pods.|With this next command, we'll view the Pods.| +{{< /table >}} + +### Address the reader as "you" + +{{< table caption="Do and Don't - Addressing the reader" >}} +|Do|Don't| +|--- |--- | +|You can create a Deployment by ...|We'll create a Deployment by ...| +|In the preceding output, you can see...|In the preceding output, we can see ...| +{{< /table >}} + +### Avoid Latin phrases + +Prefer English terms over Latin abbreviations. + +{{< table caption="Do and Don't - Avoid Latin phrases" >}} +|Do|Don't| +|--- |--- | +|For example, ...|e.g., ...| +|That is, ...|i.e., ...| +{{< /table >}} + +Exception: Use "etc." for et cetera. + +## Patterns to avoid + + +### Avoid using "we" + +Using "we" in a sentence can be confusing, because the reader might not know +whether they're part of the "we" you're describing. + +{{< table caption="Do and Don't - Avoid using we" >}} +|Do|Don't| +|--- |--- | +|Version 1.4 includes ...|In version 1.4, we have added ...| +|Kubernetes provides a new feature for ...|We provide a new feature ...| +|This page teaches you how to use Pods.|In this page, we are going to learn about Pods.| +{{< /table >}} + +### Avoid jargon and idioms + +Many readers speak English as a second language. Avoid jargon and idioms to help them understand better. + +{{< table caption="Do and Don't - Avoid jargon and idioms" >}} +|Do|Don't| +|--- |--- | +|Internally, ...|Under the hood, ...| +|Create a new cluster.|Turn up a new cluster.| +{{< /table >}} + +### Avoid statements about the future or that will soon be out of date + +Avoid making promises or giving hints about the future. If you need to talk about +a beta feature, put the text under a heading that identifies it as beta +information. + +Also avoid words like “recently”, "currently" and "new." A feature that is new today might not be +considered new in a few months. + +{{< table caption="Do and Don't - Avoid statements that will soon be out of date" >}} +|Do|Don't| +|--- |--- | +|In version 1.4, ...|In the current version, ...| +|The Federation feature provides ...|The new Federation feature provides ...| +{{< /table >}} + +### Language + +This documentation uses U.S. English spelling and grammar. + +## Documentation formatting standards + +### Use camel case for API objects + +When you refer to an API object, use the same uppercase and lowercase letters +that are used in the actual object name. Typically, the names of API +objects use +[camel case](https://en.wikipedia.org/wiki/Camel_case). + +Don't split the API object name into separate words. For example, use +PodTemplateList, not Pod Template List. + +Refer to API objects without saying "object," unless omitting "object" +leads to an awkward sentence construction. + +{{< table caption="Do and Don't - Do and Don't - API objects" >}} +|Do|Don't| +|--- |--- | +|The Pod has two containers.|The pod has two containers.| +|The Deployment is responsible for ...|The Deployment object is responsible for ...| +|A PodList is a list of Pods.|A Pod List is a list of pods.| +|The two ContainerPorts ...|The two ContainerPort objects ...| +|The two ContainerStateTerminated objects ...|The two ContainerStateTerminateds ...| +{{< /table >}} + +### Use angle brackets for placeholders + +Use angle brackets for placeholders. Tell the reader what a placeholder represents. + +1. Display information about a Pod: + + kubectl describe pod -n + + If the pod is in the default namespace, you can omit the '-n' parameter. + +### Use bold for user interface elements + +{{< table caption="Do and Don't - Bold interface elements" >}} +|Do|Don't| +|--- |--- | +|Click **Fork**.|Click "Fork".| +|Select **Other**.|Select "Other".| +{{< /table >}} + +### Use italics to define or introduce new terms + +{{< table caption="Do and Don't - Use italics for new terms" >}} +|Do|Don't| +|--- |--- | +|A _cluster_ is a set of nodes ...|A "cluster" is a set of nodes ...| +|These components form the _control plane_.|These components form the **control plane**.| +{{< /table >}} + +### Use code style for filenames, directories, paths, object field names and namespaces +{{< table caption="Do and Don't - Use code style for filenames, directories, paths, object field names and namespaces" >}} +|Do|Don't| +|--- |--- | +|Open the `envars.yaml` file.|Open the envars.yaml file.| +|Go to the `/docs/tutorials` directory.|Go to the /docs/tutorials directory.| +|Open the `/_data/concepts.yaml` file.|Open the /\_data/concepts.yaml file.| +{{< /table >}} + + +### Use punctuation inside quotes +{{< table caption="Do and Don't - Use code style for filenames, directories, paths, object field names and namespaces" >}} +|Do|Don't| +|--- |--- | +|events are recorded with an associated "stage."|events are recorded with an associated "stage".| +|The copy is called a "fork."|The copy is called a "fork".| +{{< /table >}} + +Exception: When the quoted word is a user input. + +Example: +* My user ID is “IM47g”. +* Did you try the password “mycatisawesome”? + +## Inline code formatting + + +### Use code style for inline code and commands + +For inline code in an HTML document, use the `` tag. In a Markdown +document, use the backtick (`` ` ``). + +{{< table caption="Do and Don't - Use code style for filenames, directories, paths, object field names and namespaces" >}} +|Do|Don't| +|--- |--- | +|The `kubectl run` command creates a Deployment.|The "kubectl run" command creates a Deployment.| +|For declarative management, use `kubectl apply`.|For declarative management, use "kubectl apply".| +|Use single backticks to enclose inline code. For example, `var example = true`.|Use two asterisks (`**`) or an underscore (`_`) to enclose inline code. For example, **var example = true**.| +|Use triple backticks (\`\`\`) before and after a multi-line block of code for fenced code blocks.|Use multi-line blocks of code to create diagrams, flowcharts, or other illustrations.| +|Use meaningful variable names that have a context.|Use variable names such as 'foo','bar', and 'baz' that are not meaningful and lack context.| +|Remove trailing spaces in the code.|Add trailing spaces in the code, where these are important, because a screen reader will read out the spaces as well.| +{{< /table >}} + +### Starting a sentence with a component tool or component name + +{{< table caption="Do and Don't - Starting a sentence with a component tool or component name" >}} +|Do|Don't| +|--- |--- | +|The `kubeadm` tool bootstraps and provisions machines in a cluster.|`kubeadm` tool bootstraps and provisions machines in a cluster.| +|The kube-scheduler is the default scheduler for Kubernetes.|kube-scheduler is the default scheduler for Kubernetes.| +{{< /table >}} + +### Use normal style for string and integer field values + +For field values of type string or integer, use normal style without quotation marks. + +{{< table caption="Do and Don't - Use normal style for string and integer field values" >}} +|Do|Don't| +|--- |--- | +|Set the value of `imagePullPolicy` to `Always`.|Set the value of `imagePullPolicy` to "Always".| +|Set the value of `image` to `nginx:1.16`.|Set the value of `image` to nginx:1.16.| +|Set the value of the `replicas` field to `2`.|Set the value of the `replicas` field to 2.| +{{< /table >}} + +## Code snippet formatting + + +### Don't include the command prompt + +{{< table caption="Do and Don't - Don't include the command prompt" >}} +|Do|Don't| +|--- |--- | +|kubectl get pods|$ kubectl get pods| +{{< /table >}} + +### Separate commands from output + +Verify that the Pod is running on your chosen node: + +``` +kubectl get pods --output=wide +``` + +The output is similar to this: + +``` +NAME READY STATUS RESTARTS AGE IP NODE +nginx 1/1 Running 0 13s 10.200.0.4 worker0 +``` + +## Velero.io word list + + +A list of Velero-specific terms and words to be used consistently across the site. + +{{< table caption="Velero.io word list" >}} +|Trem|Usage| +|--- |--- | +|Kubernetes|Kubernetes should always be capitalized.| +|Docker|Docker should always be capitalized.| +|Velero|Velero should always be capitalized.| +|VMware|VMware should always be correctly capitalized.| +|On-premises|On-premises or on-prem rather than on-premise or other variations.| +|Backup|Backup rather than back up, back-up or other variations.| +|Plugin|Plugin rather than plug-in or other variations.| +|Allowlist|Use allowlist instead of whitelist.| +|Denylist|Use denylist instead of blacklist.| +{{< /table >}} + +## Markdown elements + +### Headings +People accessing this documentation may use a screen reader or other assistive technology (AT). [Screen readers](https://en.wikipedia.org/wiki/Screen_reader) are linear output devices, they output items on a page one at a time. If there is a lot of content on a page, you can use headings to give the page an internal structure. A good page structure helps all readers to easily navigate the page or filter topics of interest. + +{{< table caption="Do and Don't - Headings" >}} +|Do|Don't| +|--- |--- | +|Include a title on each page or blog post.|Include more than one title headings (#) in a page.| +|Use ordered headings to provide a meaningful high-level outline of your content.|Use headings level 4 through 6, unless it is absolutely necessary. If your content is that detailed, it may need to be broken into separate articles.| +|Use sentence case for headings. For example, **Extend kubectl with plugins**|Use title case for headings. For example, **Extend Kubectl With Plugins**| +{{< /table >}} + +### Paragraphs + +{{< table caption="Do and Don't - Paragraphs" >}} + +|Do|Don't| +|--- |--- | +|Try to keep paragraphs under 6 sentences.|Write long-winded paragraphs.| +|Use three hyphens (`---`) to create a horizontal rule for breaks in paragraph content.|Use horizontal rules for decoration.| +{{< /table >}} + +### Links + +{{< table caption="Do and Don't - Links" >}} +|Do|Don't| +|--- |--- | +|Write hyperlinks that give you context for the content they link to. For example: Certain ports are open on your machines. See [check required ports](#check-required-ports) for more details.|Use ambiguous terms such as “click here”. For example: Certain ports are open on your machines. See [here](#check-required-ports) for more details.| +|Write Markdown-style links: `[link text](URL)`. For example: `[community meeting agenda](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA)` and the output is [community meeting agenda](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA).|Write HTML-style links: `Visit our tutorial!`| +{{< /table >}} + + +### Lists + +Group items in a list that are related to each other and need to appear in a specific order or to indicate a correlation between multiple items. When a screen reader comes across a list—whether it is an ordered or unordered list—it will be announced to the user that there is a group of list items. The user can then use the arrow keys to move up and down between the various items in the list. +Website navigation links can also be marked up as list items; after all they are nothing but a group of related links. + + - End each item in a list with a period if one or more items in the list are complete sentences. For the sake of consistency, normally either all items or none should be complete sentences. + + - Ordered lists that are part of an incomplete introductory sentence can be in lowercase and punctuated as if each item was a part of the introductory sentence. + + - Use the number one (`1.`) for ordered lists. + + - Use (`+`), (`*`), or (`-`) for unordered lists - be consistent within the same document. + + - Leave a blank line after each list. + + - Indent nested lists with four spaces (for example, ⋅⋅⋅⋅). + + - List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be indented by either four spaces or one tab. + +### Tables + +The semantic purpose of a data table is to present tabular data. Sighted users can quickly scan the table but a screen reader goes through line by line. A table [caption](https://www.w3schools.com/tags/tag_caption.asp) is used to create a descriptive title for a data table. Assistive technologies (AT) use the HTML table caption element to identify the table contents to the user within the page structure. + +If you need to create a table, create the table in markdown and use the table [Hugo shortcode](https://gohugo.io/content-management/shortcodes/) to include a caption. + +``` +{{}} +Parameter | Description | Default +:---------|:------------|:------- +`timeout` | The timeout for requests | `30s` +`logLevel` | The log level for log output | `INFO` +{{< /table */>}} + +``` +**Note:** This shortcode does not support markdown reference-style links. Use inline-style links in tables. See more information about [markdown link styles](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#links). diff --git a/site/content/docs/v1.9/support-process.md b/site/content/docs/v1.9/support-process.md new file mode 100644 index 0000000000..d3d7c8ecbe --- /dev/null +++ b/site/content/docs/v1.9/support-process.md @@ -0,0 +1,50 @@ +--- +title: "Support Process" +layout: docs +--- + + +Velero provides best effort support through the process on this page for the current version of Velero and n-1 Velero version, including all patch releases in the supported minor releases. For example, if the current version is 1.9, the Velero maintainers would offer best effort support for v1.9 and v1.8. If you have a question about a previous Velero version (for example, 1.7), please note that maintainers may ask you to upgrade to a supported version before doing any investigation into your issue. + +For more information about Velero testing and supported Kubernetes versions, see Velero's [compatibility matrix](https://github.com/vmware-tanzu/velero/blob/v1.9/README.md#velero-compatibility-matrix). + + +## Weekly Rotation + +The Velero maintainers use a weekly rotation to manage community support. Each week, a different maintainer is the point person for responding to incoming support issues via Slack, GitHub, and the Google group. The point person is *not* expected to be on-call 24x7. Instead, they choose one or more hour(s) per day to be available/responding to incoming issues. They will communicate to the community what that time slot will be each week. + +## Start of Week + +We will update the public Slack channel's topic to indicate that you are the point person for the week, and what hours you'll be available. + +## During the Week + +### Where we will monitor +- `#velero` public Slack channel in Kubernetes org +- [all Velero-related repos][0] in GitHub (`velero`, `velero-plugin-for-[aws|gcp|microsoft-azure|csi]`, `helm-charts`) +- [Project Velero Google Group][1] + +### GitHub issue flow + +Generally speaking, new GitHub issues will fall into one of several categories. We use the following process for each: + +1. **Feature request** + - Label the issue with `Enhancement/User` or `Enhancement/Dev` + - Leave the issue in the `New Issues` swimlane for triage by product mgmt +1. **Bug** + - Label the issue with `Bug` + - Leave the issue in the `New Issues` swimlane for triage by product mgmt +1. **User question/problem** that does not clearly fall into one of the previous categories + - When you start investigating/responding, label the issue with `Investigating` + - Add comments as you go, so both the user and future support people have as much context as possible + - Use the `Needs Info` label to indicate an issue is waiting for information from the user. Remove/re-add the label as needed. + - If you resolve the issue with the user, close it out + - If the issue ends up being a feature request or a bug, update the title and follow the appropriate process for it + - If the reporter becomes unresponsive after multiple pings, close out the issue due to inactivity and comment that the user can always reach out again as needed + +## End of Week + +We ensure all GitHub issues worked on during the week on are labeled with `Investigating` and `Needs Info` (if appropriate), and have updated comments so the next person can pick them up. + +[0]: https://github.com/vmware-tanzu?q=velero&type=&language= +[1]: https://groups.google.com/forum/#!forum/projectvelero diff --git a/site/content/docs/v1.9/supported-providers.md b/site/content/docs/v1.9/supported-providers.md new file mode 100644 index 0000000000..fe8c03d3a1 --- /dev/null +++ b/site/content/docs/v1.9/supported-providers.md @@ -0,0 +1,70 @@ +--- +title: "Providers" +layout: docs +--- + +Velero supports a variety of storage providers for different backup and snapshot operations. Velero has a plugin system which allows anyone to add compatibility for additional backup and volume storage platforms without modifying the Velero codebase. + +## Provider plugins maintained by the Velero maintainers + +{{< table caption="Velero supported providers" >}} + +| Provider | Object Store | Volume Snapshotter | Plugin Provider Repo | Setup Instructions | Parameters | +|-----------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|-----------------------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Amazon Web Services (AWS)](https://aws.amazon.com) | AWS S3 | AWS EBS | [Velero plugin for AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws) | [AWS Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-aws#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/backupstoragelocation.md)
    [VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/volumesnapshotlocation.md) | +| [Google Cloud Platform (GCP)](https://cloud.google.com) | [Google Cloud Storage](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/backupstoragelocation.md) | [Google Compute Engine Disks](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/volumesnapshotlocation.md) | [Velero plugin for GCP](https://github.com/vmware-tanzu/velero-plugin-for-gcp) | [GCP Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-gcp#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/backupstoragelocation.md)
    [VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/volumesnapshotlocation.md) | +| [Microsoft Azure](https://azure.com) | Azure Blob Storage | Azure Managed Disks | [Velero plugin for Microsoft Azure](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure) | [Azure Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#setup) | [BackupStorageLocation](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/backupstoragelocation.md)
    [VolumeSnapshotLocation](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/volumesnapshotlocation.md) | +| [VMware vSphere](https://www.vmware.com/ca/products/vsphere.html) | 🚫 | vSphere Volumes | [VMware vSphere](https://github.com/vmware-tanzu/velero-plugin-for-vsphere) | [vSphere Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-vsphere#velero-plugin-for-vsphere-installation-and-configuration-details) | 🚫 | +| [Container Storage Interface (CSI)](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/)| 🚫 | CSI Volumes | [Velero plugin for CSI](https://github.com/vmware-tanzu/velero-plugin-for-csi/) | [CSI Plugin Setup](https://github.com/vmware-tanzu/velero-plugin-for-csi#kinds-of-plugins-included) | 🚫 | +{{< /table >}} + +Contact: [#Velero Slack](https://kubernetes.slack.com/messages/velero), [GitHub Issues](https://github.com/vmware-tanzu/velero/issues) + +## Provider plugins maintained by the Velero community +{{< table caption="Community supported providers" >}} + +| Provider | Object Store | Volume Snapshotter | Plugin Documentation | Contact | +|---------------------------|------------------------------|------------------------------------|------------------------|---------------------------------| +| [AlibabaCloud](https://www.alibabacloud.com/) | Alibaba Cloud OSS | Alibaba Cloud | [AlibabaCloud](https://github.com/AliyunContainerService/velero-plugin) | [GitHub Issue](https://github.com/AliyunContainerService/velero-plugin/issues) | +| [DigitalOcean](https://www.digitalocean.com/) | DigitalOcean Object Storage | DigitalOcean Volumes Block Storage | [StackPointCloud](https://github.com/StackPointCloud/ark-plugin-digitalocean) | | +| [Hewlett Packard](https://www.hpe.com/us/en/storage.html) | 🚫 | HPE Storage | [Hewlett Packard](https://github.com/hpe-storage/velero-plugin) | [Slack](https://slack.hpedev.io/), [GitHub Issue](https://github.com/hpe-storage/velero-plugin/issues) | +| [OpenEBS](https://openebs.io/) | 🚫 | OpenEBS CStor Volume | [OpenEBS](https://github.com/openebs/velero-plugin) | [Slack](https://openebs-community.slack.com/), [GitHub Issue](https://github.com/openebs/velero-plugin/issues) | +| [OpenStack](https://www.openstack.org/) | Swift | Cinder | [OpenStack](https://github.com/Lirt/velero-plugin-for-openstack) | [GitHub Issue](https://github.com/Lirt/velero-plugin-for-openstack/issues) | +| [Portworx](https://portworx.com/) | 🚫 | Portworx Volume | [Portworx](https://docs.portworx.com/scheduler/kubernetes/ark.html) | [Slack](https://portworx.slack.com/messages/px-k8s), [GitHub Issue](https://github.com/portworx/ark-plugin/issues) | +| [Storj](https://storj.io) | Storj Object Storage | 🚫 | [Storj](https://github.com/storj-thirdparty/velero-plugin) | [GitHub Issue](https://github.com/storj-thirdparty/velero-plugin/issues) | +{{< /table >}} + +## S3-Compatible object store providers + +Velero's AWS Object Store plugin uses [Amazon's Go SDK][0] to connect to the AWS S3 API. Some third-party storage providers also support the S3 API, and users have reported the following providers work with Velero: + +_Note that these storage providers are not regularly tested by the Velero team._ + + * [IBM Cloud][1] + * [Oracle Cloud][2] + * [Minio][3] + * [DigitalOcean][4] + * [NooBaa][5] + * [Tencent Cloud][7] + * Ceph RADOS v12.2.7 + * Quobyte + * [Cloudian HyperStore][38] + +_Some storage providers, like Quobyte, may need a different [signature algorithm version][6]._ + +## Non-supported volume snapshots + +In the case you want to take volume snapshots but didn't find a plugin for your provider, Velero has support for snapshotting using restic. Please see the [restic integration][30] documentation. + +[0]: https://github.com/aws/aws-sdk-go/aws +[1]: contributions/ibm-config.md +[2]: contributions/oracle-config.md +[3]: contributions/minio.md +[4]: https://github.com/StackPointCloud/ark-plugin-digitalocean +[5]: http://www.noobaa.com/ +[6]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/backupstoragelocation.md +[7]: contributions/tencent-config.md +[25]: https://github.com/hpe-storage/velero-plugin +[30]: restic.md +[36]: https://github.com/vmware-tanzu/velero-plugin-for-gcp#setup +[38]: https://www.cloudian.com/ diff --git a/site/content/docs/v1.9/tilt.md b/site/content/docs/v1.9/tilt.md new file mode 100644 index 0000000000..3f2a8d90eb --- /dev/null +++ b/site/content/docs/v1.9/tilt.md @@ -0,0 +1,210 @@ +--- +title: "Rapid iterative Velero development with Tilt " +layout: docs +--- + +## Overview +This document describes how to use [Tilt](https://tilt.dev) with any cluster for a simplified +workflow that offers easy deployments and rapid iterative builds. + +This setup allows for continuing deployment of the Velero server and, if specified, any provider plugin or the restic daemonset. +It does this work by: + +1. Deploying the necessary Kubernetes resources, such as the Velero CRDs and Velero deployment +1. Building a local binary for Velero and (if specified) provider plugins as a `local_resource` +1. Invoking `docker_build` to live update any binary into the container/init container and trigger a re-start + +Tilt will look for configuration files under `velero/tilt-resources`. Most of the +files in this directory are gitignored so you may configure your setup according to your needs. + +## Prerequisites +1. [Docker](https://docs.docker.com/install/) v19.03 or newer +1. A Kubernetes cluster v1.16 or greater (does not have to be Kind) +1. [Tilt](https://docs.tilt.dev/install.html) v0.12.0 or newer +1. Clone the [Velero project](https://github.com/vmware-tanzu/velero) repository + locally +1. Access to an S3 object storage +1. Clone any [provider plugin(s)](https://velero.io/plugins/) you want to make changes to and deploy (optional, must be configured to be deployed by the Velero Tilt's setup, [more info below](#provider-plugins)) + +Note: To properly configure any plugin you use, please follow the plugin's documentation. + +## Getting started + +### tl;dr +- Copy all sample files under `velero/tilt-resources/examples` into `velero/tilt-resources`. +- Configure the `velero_v1_backupstoragelocation.yaml` file, and the `cloud` file for the storage credentials/secret. + +- Run `tilt up`. + +### Create a Tilt settings file +Create a configuration file named `tilt-settings.json` and place it in your local copy of `velero/tilt-resources`. Alternatively, +you may copy and paste the sample file found in `velero/tilt-resources/examples`. + +Here is an example: + +```json +{ + "default_registry": "", + "enable_providers": [ + "aws", + "gcp", + "azure", + "csi" + ], + "providers": { + "aws": "../velero-plugin-for-aws", + "gcp": "../velero-plugin-for-gcp", + "azure": "../velero-plugin-for-microsoft-azure", + "csi": "../velero-plugin-for-csi" + }, + "allowed_contexts": [ + "development" + ], + "enable_restic": false, + "create_backup_locations": true, + "setup-minio": true, + "enable_debug": false, + "debug_continue_on_start": true +} +``` + +#### tilt-settings.json fields +**default_registry** (String, default=""): The image registry to use if you need to push images. See the [Tilt +*documentation](https://docs.tilt.dev/api.html#api.default_registry) for more details. + +**provider_repos** (Array[]String, default=[]): A list of paths to all the provider plugins you want to make changes to. Each provider must have a +`tilt-provider.json` file describing how to build the provider. + +**enable_providers** (Array[]String, default=[]): A list of the provider plugins to enable. See [provider plugins](provider-plugins) +for more details. Note: when not making changes to a plugin, it is not necessary to load them into +Tilt: an existing image and version might be specified in the Velero deployment instead, and Tilt will load that. + +**allowed_contexts** (Array, default=[]): A list of kubeconfig contexts Tilt is allowed to use. See the Tilt documentation on +*[allow_k8s_contexts](https://docs.tilt.dev/api.html#api.allow_k8s_contexts) for more details. Note: Kind is automatically allowed. + +**enable_restic** (Bool, default=false): Indicate whether to deploy the restic Daemonset. If set to `true`, Tilt will look for a `velero/tilt-resources/restic.yaml` file +containing the configuration of the Velero restic DaemonSet. + +**create_backup_locations** (Bool, default=false): Indicate whether to create one or more backup storage locations. If set to `true`, Tilt will look for a `velero/tilt-resources/velero_v1_backupstoragelocation.yaml` file +containing at least one configuration for a Velero backup storage location. + +**setup-minio** (Bool, default=false): Configure this to `true` if you want to configure backup storage locations in a Minio instance running inside your cluster. + +**enable_debug** (Bool, default=false): Configure this to `true` if you want to debug the velero process using [Delve](https://github.com/go-delve/delve). + +**debug_continue_on_start** (Bool, default=true): Configure this to `true` if you want the velero process to continue on start when in debug mode. See [Delve CLI documentation](https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv.md). + +### Create Kubernetes resource files to deploy +All needed Kubernetes resource files are provided as ready to use samples in the `velero/tilt-resources/examples` directory. You only have to move them to the `velero/tilt-resources` level. + +Because the Velero Kubernetes deployment as well as the restic DaemonSet contain the configuration +for any plugin to be used, files for these resources are expected to be provided by the user so you may choose +which provider plugin to load as a init container. Currently, the sample files provided are configured with all the +plugins supported by Velero, feel free to remove any of them as needed. + +For Velero to operate fully, it also needs at least one backup +storage location. A sample file is provided that needs to be modified with the specific +configuration for your object storage. See the next sub-section for more details on this. + +### Configure a backup storage location +You will have to configure the `velero/tilt-resources/velero_v1_backupstoragelocation.yaml` with the proper values according to your storage provider. Read the [plugin documentation](https://velero.io/plugins/) +to learn what field/value pairs are required for your particular provider's backup storage location configuration. + +Below are some ways to configure a backup storage location for Velero. +#### As a storage with a service provider +Follow the provider documentation to provision the storage. We have a [list of all known object storage providers](supported-providers/) with corresponding plugins for Velero. + +#### Using MinIO as an object storage +Note: to use MinIO as an object storage, you will need to use the [`AWS` plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws), and configure the storage location with the `spec.provider` set to `aws` and the `spec.config.region` set to `minio`. Example: +``` +spec: + config: + region: minio + s3ForcePathStyle: "true" + s3Url: http://minio.velero.svc:9000 + objectStorage: + bucket: velero + provider: aws +``` + +Here are two ways to use MinIO as the storage: + +1) As a MinIO instance running inside your cluster (don't do this for production!) + + In the `tilt-settings.json` file, set `"setup-minio": true`. This will configure a Kubernetes deployment containing a running +instance of MinIO inside your cluster. There are [extra steps](contributions/minio/#expose-minio-outside-your-cluster-with-a-service) +necessary to expose MinIO outside the cluster. + + To access this storage, you will need to expose MinIO outside the cluster by forwarding the MinIO port to the local machine using kubectl port-forward -n svc/minio 9000. Update the BSL configuration to use that as its "public URL" by adding `publicUrl: http://localhost:9000` to the BSL config. This is necessary to do things like download a backup file. + + Note: with this setup, when your cluster is terminated so is the storage and any backup/restore in it. + +1) As a standalone MinIO instance running locally in a Docker container + + See [these instructions](https://github.com/vmware-tanzu/velero/discussions/3381) to run MinIO locally on your computer, as a standalone as opposed to running it on a Pod. + +Please see our [locations documentation](locations/) to learn more how backup locations work. + +### Configure the provider credentials (secret) +Whatever object storage provider you use, configure the credentials for in the `velero/tilt-resources/cloud` file. Read the [plugin documentation](https://velero.io/plugins/) +to learn what field/value pairs are required for your provider's credentials. The Tilt file will invoke Kustomize to create the secret under the hard-coded key `secret.cloud-credentials.data.cloud` in the Velero namespace. + +There is a sample credentials file properly formatted for a MinIO storage credentials in `velero/tilt-resources/examples/cloud`. + +### Configure debugging with Delve +If you would like to debug the Velero process, you can enable debug mode by setting the field `enable_debug` to `true` in your `tilt-resources/tile-settings.json` file. +This will enable you to debug the process using [Delve](https://github.com/go-delve/delve). +By enabling debug mode, the Velero executable will be built in debug mode (using the flags `-gcflags="-N -l"` which disables optimizations and inlining), and the process will be started in the Velero deployment using [`dlv exec`](https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_exec.md). + +The debug server will accept connections on port 2345 and Tilt is configured to forward this port to the local machine. +Once Tilt is [running](#run-tilt) and the Velero resource is ready, you can connect to the debug server to begin debugging. +To connect to the session, you can use the Delve CLI locally by running `dlv connect 127.0.0.1:2345`. See the [Delve CLI documentation](https://github.com/go-delve/delve/tree/master/Documentation/cli) for more guidance on how to use Delve. +Delve can also be used within a number of [editors and IDEs](https://github.com/go-delve/delve/blob/master/Documentation/EditorIntegration.md). + +By default, the Velero process will continue on start when in debug mode. +This means that the process will run until a breakpoint is set. +You can disable this by setting the field `debug_continue_on_start` to `false` in your `tilt-resources/tile-settings.json` file. +When this setting is disabled, the Velero process will not continue to run until a `continue` instruction is issued through your Delve session. + +When exiting your debug session, the CLI and editor integrations will typically ask if the remote process should be stopped. +It is important to leave the remote process running and just disconnect from the debugging session. +By stopping the remote process, that will cause the Velero container to stop and the pod to restart. +If backups are in progress, these will be left in a stale state as they are not resumed when the Velero pod restarts. + +### Run Tilt! +To launch your development environment, run: + +``` bash +tilt up +``` + +This will output the address to a web browser interface where you can monitor Tilt's status and the logs for each Tilt resource. After a brief amount of time, you should have a running development environment, and you should now be able to +create backups/restores and fully operate Velero. + +Note: Running `tilt down` after exiting out of Tilt [will delete all resources](https://docs.tilt.dev/cli/tilt_down.html) specified in the Tiltfile. + +Tip: Create an alias to `velero/_tuiltbuild/local/velero` and you won't have to run `make local` to get a refreshed version of the Velero CLI, just use the alias. + +Please see the documentation for [how Velero works](how-velero-works/). + +## Provider plugins +A provider must supply a `tilt-provider.json` file describing how to build it. Here is an example: + +```json +{ + "plugin_name": "velero-plugin-for-aws", + "context": ".", + "image": "velero/velero-plugin-for-aws", + "live_reload_deps": [ + "velero-plugin-for-aws" + ], + "go_main": "./velero-plugin-for-aws" +} +``` + +## Live updates +Each provider plugin configured to be deployed by Velero's Tilt setup has a `live_reload_deps` list. This defines the files and/or directories that Tilt +should monitor for changes. When a dependency is modified, Tilt rebuilds the provider's binary **on your local +machine**, copies the binary to the init container, and triggers a restart of the Velero container. This is significantly faster +than rebuilding the container image for each change. It also helps keep the size of each development image as small as +possible (the container images do not need the entire go toolchain, source code, module dependencies, etc.). diff --git a/site/content/docs/v1.9/troubleshooting.md b/site/content/docs/v1.9/troubleshooting.md new file mode 100644 index 0000000000..cdfd253059 --- /dev/null +++ b/site/content/docs/v1.9/troubleshooting.md @@ -0,0 +1,220 @@ +--- +title: "Troubleshooting" +layout: docs +--- + +These tips can help you troubleshoot known issues. If they don't help, you can [file an issue][4], or talk to us on the [#velero channel][25] on the Kubernetes Slack server. + +## Debug installation/ setup issues + +- [Debug installation/setup issues][2] + +## Debug restores + +- [Debug restores][1] + +## General troubleshooting information + +You can use the `velero bug` command to open a [Github issue][4] by launching a browser window with some prepopulated values. Values included are OS, CPU architecture, `kubectl` client and server versions (if available) and the `velero` client version. This information isn't submitted to Github until you click the `Submit new issue` button in the Github UI, so feel free to add, remove or update whatever information you like. + +You can use the `velero debug` command to generate a debug bundle, which is a tarball +that contains: +* Version information +* Logs of velero server and plugins +* Resources managed by velero server such as backup, restore, podvolumebackup, podvolumerestore, etc. +* Logs of the backup and restore, if specified in the parameters + +Please use command `velero debug --help` to see more usage details. + +### Getting velero debug logs + +You can increase the verbosity of the Velero server by editing your Velero deployment to look like this: + + +``` +kubectl edit deployment/velero -n velero +... + containers: + - name: velero + image: velero/velero:latest + command: + - /velero + args: + - server + - --log-level # Add this line + - debug # Add this line +... +``` + +## Known issue with restoring LoadBalancer Service + +Because of how Kubernetes handles Service objects of `type=LoadBalancer`, when you restore these objects you might encounter an issue with changed values for Service UIDs. Kubernetes automatically generates the name of the cloud resource based on the Service UID, which is different when restored, resulting in a different name for the cloud load balancer. If the DNS CNAME for your application points to the DNS name of your cloud load balancer, you'll need to update the CNAME pointer when you perform a Velero restore. + +Alternatively, you might be able to use the Service's `spec.loadBalancerIP` field to keep connections valid, if your cloud provider supports this value. See [the Kubernetes documentation about Services of Type LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). + +## Miscellaneous issues + +### Velero reports `custom resource not found` errors when starting up. + +Velero's server will not start if the required Custom Resource Definitions are not found in Kubernetes. Run `velero install` again to install any missing custom resource definitions. + +### `velero backup logs` returns a `SignatureDoesNotMatch` error + +Downloading artifacts from object storage utilizes temporary, signed URLs. In the case of S3-compatible +providers, such as Ceph, there may be differences between their implementation and the official S3 +API that cause errors. + +Here are some things to verify if you receive `SignatureDoesNotMatch` errors: + + * Make sure your S3-compatible layer is using [signature version 4][5] (such as Ceph RADOS v12.2.7) + * For Ceph, try using a native Ceph account for credentials instead of external providers such as OpenStack Keystone + +## Velero (or a pod it was backing up) restarted during a backup and the backup is stuck InProgress + +Velero cannot resume backups that were interrupted. Backups stuck in the `InProgress` phase can be deleted with `kubectl delete backup -n `. +Backups in the `InProgress` phase have not uploaded any files to object storage. + +## Velero is not publishing prometheus metrics + +Steps to troubleshoot: + +- Confirm that your velero deployment has metrics publishing enabled. The [latest Velero helm charts][6] have been setup with [metrics enabled by default][7]. +- Confirm that the Velero server pod exposes the port on which the metrics server listens on. By default, this value is 8085. + +```yaml + ports: + - containerPort: 8085 + name: metrics + protocol: TCP +``` + +- Confirm that the metric server is listening for and responding to connections on this port. This can be done using [port-forwarding][9] as shown below + +```bash +$ kubectl -n port-forward 8085:8085 +Forwarding from 127.0.0.1:8085 -> 8085 +Forwarding from [::1]:8085 -> 8085 +. +. +. +``` + +Now, visiting http://localhost:8085/metrics on a browser should show the metrics that are being scraped from Velero. + +- Confirm that the Velero server pod has the necessary [annotations][8] for prometheus to scrape metrics. +- Confirm, from the Prometheus UI, that the Velero pod is one of the targets being scraped from Prometheus. + + +## Is Velero using the correct cloud credentials? + +Cloud provider credentials are given to Velero to store and retrieve backups from the object store and to perform volume snapshotting operations. + +These credentials are either passed to Velero at install time using: +1. `--secret-file` flag to the `velero install` command. OR +1. `--set-file credentials.secretContents.cloud` flag to the `helm install` command. + +Or, they are specified when creating a `BackupStorageLocation` using the `--credential` flag. + +### Troubleshooting credentials provided during install + +If using the credentials provided at install time, they are stored in the cluster as a Kubernetes secret named `cloud-credentials` in the same namespace in which Velero is installed. + +Follow the below troubleshooting steps to confirm that Velero is using the correct credentials: +1. Confirm that the `cloud-credentials` secret exists and has the correct content. + ```bash + $ kubectl -n velero get secrets cloud-credentials + NAME TYPE DATA AGE + cloud-credentials Opaque 1 11h + $ kubectl -n velero get secrets cloud-credentials -ojsonpath={.data.cloud} | base64 --decode + + ``` + +1. Confirm that velero deployment is mounting the `cloud-credentials` secret. + ```bash + $ kubectl -n velero get deploy velero -ojson | jq .spec.template.spec.containers[0].volumeMounts + [ + { + "mountPath": "/plugins", + "name": "plugins" + }, + { + "mountPath": "/scratch", + "name": "scratch" + }, + { + "mountPath": "/credentials", + "name": "cloud-credentials" + } + ] + ``` + + If [restic-integration][3] is enabled, then, confirm that the restic daemonset is also mounting the `cloud-credentials` secret. + ```bash + $ kubectl -n velero get ds restic -ojson |jq .spec.template.spec.containers[0].volumeMounts + [ + { + "mountPath": "/host_pods", + "mountPropagation": "HostToContainer", + "name": "host-pods" + }, + { + "mountPath": "/scratch", + "name": "scratch" + }, + { + "mountPath": "/credentials", + "name": "cloud-credentials" + } + ] + ``` + +1. Confirm if the correct credentials are mounted into the Velero pod. + ```bash + $ kubectl -n velero exec -ti deploy/velero -- bash + nobody@velero-69f9c874c-l8mqp:/$ cat /credentials/cloud + + ``` + +### Troubleshooting `BackupStorageLocation` credentials + +Follow the below troubleshooting steps to confirm that Velero is using the correct credentials if using credentials specific to a [`BackupStorageLocation`][10]: +1. Confirm that the object storage provider plugin being used supports multiple credentials. + + If the logs from the Velero deployment contain the error message `"config has invalid keys credentialsFile"`, the version of your object storage plugin does not yet support multiple credentials. + + The object storage plugins [maintained by the Velero team][11] support this feature, so please update your plugin to the latest version if you see the above error message. + + If you are using a plugin from a different provider, please contact them for further advice. + +1. Confirm that the secret and key referenced by the `BackupStorageLocation` exists in the Velero namespace and has the correct content: + ```bash + # Determine which secret and key the BackupStorageLocation is using + BSL_SECRET=$(kubectl get backupstoragelocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.name}) + BSL_SECRET_KEY=$(kubectl get backupstoragelocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.key}) + + # Confirm that the secret exists + kubectl -n velero get secret $BSL_SECRET + + # Print the content of the secret and ensure it is correct + kubectl -n velero get secret $BSL_SECRET -ojsonpath={.data.$BSL_SECRET_KEY} | base64 --decode + ``` + If the secret can't be found, the secret does not exist within the Velero namespace and must be created. + + If no output is produced when printing the contents of the secret, the key within the secret may not exist or may have no content. + Ensure that the key exists within the secret's data by checking the output from `kubectl -n velero describe secret $BSL_SECRET`. + If it does not exist, follow the instructions for [editing a Kubernetes secret][12] to add the base64 encoded credentials data. + + +[1]: debugging-restores.md +[2]: debugging-install.md +[3]: restic.md +[4]: https://github.com/vmware-tanzu/velero/issues +[5]: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html +[6]: https://github.com/vmware-tanzu/helm-charts/blob/main/charts/velero +[7]: https://github.com/vmware-tanzu/helm-charts/blob/main/charts/velero/values.yaml#L44 +[8]: https://github.com/vmware-tanzu/helm-charts/blob/main/charts/velero/values.yaml#L49-L52 +[9]: https://kubectl.docs.kubernetes.io/pages/container_debugging/port_forward_to_pods.html +[10]: locations.md +[11]: /plugins +[12]: https://kubernetes.io/docs/concepts/configuration/secret/#editing-a-secret +[25]: https://kubernetes.slack.com/messages/velero diff --git a/site/content/docs/v1.9/uninstalling.md b/site/content/docs/v1.9/uninstalling.md new file mode 100644 index 0000000000..f0b7b8d173 --- /dev/null +++ b/site/content/docs/v1.9/uninstalling.md @@ -0,0 +1,11 @@ +--- +title: "Uninstalling Velero" +layout: docs +--- + +If you would like to completely uninstall Velero from your cluster, the following commands will remove all resources created by `velero install`: + +```bash +kubectl delete namespace/velero clusterrolebinding/velero +kubectl delete crds -l component=velero +``` diff --git a/site/content/docs/v1.9/upgrade-to-1.9.md b/site/content/docs/v1.9/upgrade-to-1.9.md new file mode 100644 index 0000000000..25186dcd23 --- /dev/null +++ b/site/content/docs/v1.9/upgrade-to-1.9.md @@ -0,0 +1,94 @@ +--- +title: "Upgrading to Velero 1.9" +layout: docs +--- + +## Prerequisites + +- Velero [v1.8.x][8] installed. + +If you're not yet running at least Velero v1.6, see the following: + +- [Upgrading to v1.1][1] +- [Upgrading to v1.2][2] +- [Upgrading to v1.3][3] +- [Upgrading to v1.4][4] +- [Upgrading to v1.5][5] +- [Upgrading to v1.6][6] +- [Upgrading to v1.7][7] +- [Upgrading to v1.8][8] + +Before upgrading, check the [Velero compatibility matrix](https://github.com/vmware-tanzu/velero#velero-compatabilty-matrix) to make sure your version of Kubernetes is supported by the new version of Velero. + +## Instructions + +1. Install the Velero v1.9 command-line interface (CLI) by following the [instructions here][0]. + + Verify that you've properly installed it by running: + + ```bash + velero version --client-only + ``` + + You should see the following output: + + ```bash + Client: + Version: v1.9.0 + Git commit: + ``` + +1. Update the Velero custom resource definitions (CRDs) to include schema changes across all CRDs that are at the core of the new features in this release: + + ```bash + velero install --crds-only --dry-run -o yaml | kubectl apply -f - + ``` + + **NOTE:** Since velero v1.9.0 only v1 CRD will be supported during installation, therefore, the v1.9.0 will only work on kubernetes version >= v1.16 + +1. Update the container image used by the Velero deployment and, optionally, the restic daemon set: + + ```bash + kubectl set image deployment/velero \ + velero=velero/velero:v1.9.0 \ + --namespace velero + + # optional, if using the restic daemon set + kubectl set image daemonset/restic \ + restic=velero/velero:v1.9.0 \ + --namespace velero + ``` + +1. Confirm that the deployment is up and running with the correct version by running: + + ```bash + velero version + ``` + + You should see the following output: + + ```bash + Client: + Version: v1.9.0 + Git commit: + + Server: + Version: v1.9.0 + ``` + +## Notes +### Default backup storage location +We have deprecated the way to indicate the default backup storage location. Previously, that was indicated according to the backup storage location name set on the velero server-side via the flag `velero server --default-backup-storage-location`. Now we configure the default backup storage location on the velero client-side. Please refer to the [About locations][9] on how to indicate which backup storage location is the default one. + +After upgrading, if there is a previously created backup storage location with the name that matches what was defined on the server side as the default, it will be automatically set as the `default`. + +[0]: basic-install.md#install-the-cli +[1]: https://velero.io/docs/v1.1.0/upgrade-to-1.1/ +[2]: https://velero.io/docs/v1.2.0/upgrade-to-1.2/ +[3]: https://velero.io/docs/v1.3.2/upgrade-to-1.3/ +[4]: https://velero.io/docs/v1.4/upgrade-to-1.4/ +[5]: https://velero.io/docs/v1.5/upgrade-to-1.5 +[6]: https://velero.io/docs/v1.6/upgrade-to-1.6 +[7]: https://velero.io/docs/v1.7/upgrade-to-1.7 +[8]: https://velero.io/docs/v1.8/upgrade-to-1.8 +[9]: https://velero.io/docs/v1.9/locations \ No newline at end of file diff --git a/site/content/docs/v1.9/velero-install.md b/site/content/docs/v1.9/velero-install.md new file mode 100644 index 0000000000..8cc6d4d2e7 --- /dev/null +++ b/site/content/docs/v1.9/velero-install.md @@ -0,0 +1,49 @@ +--- +title: "Velero Install CLI" +layout: docs +--- + +This document serves as a guide to using the `velero install` CLI command to install `velero` server components into your kubernetes cluster. + +_NOTE_: `velero install` will, by default, use the CLI's version information to determine the version of the server components to deploy. This behavior may be overridden by using the `--image` flag. Refer to [Building Server Component Container Images][1]. + +## Usage + +This section explains some of the basic flags supported by the `velero install` CLI command. For a complete explanation of the flags, please run `velero install --help` + +```bash +velero install \ + --plugins + --provider \ + --bucket \ + --secret-file \ + --velero-pod-cpu-request \ + --velero-pod-mem-request \ + --velero-pod-cpu-limit \ + --velero-pod-mem-limit \ + [--use-restic] \ + [--default-volumes-to-restic] \ + [--restic-pod-cpu-request ] \ + [--restic-pod-mem-request ] \ + [--restic-pod-cpu-limit ] \ + [--restic-pod-mem-limit ] +``` + +The values for the resource requests and limits flags follow the same format as [Kubernetes resource requirements][3] +For plugin container images, please refer to our [supported providers][2] page. + +## Examples + +This section provides examples that serve as a starting point for more customized installations. + +```bash +velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket mybucket --secret-file ./gcp-service-account.json + +velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --provider aws --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 --use-restic + +velero install --provider azure --plugins velero/velero-plugin-for-microsoft-azure:v1.0.0 --bucket $BLOB_CONTAINER --secret-file ./credentials-velero --backup-location-config resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID[,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID] --snapshot-location-config apiTimeout=[,resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID] +``` + +[1]: build-from-source.md#making-images-and-updating-velero +[2]: supported-providers.md +[3]: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ diff --git a/site/content/docs/v1.9/vendoring-dependencies.md b/site/content/docs/v1.9/vendoring-dependencies.md new file mode 100644 index 0000000000..9fc1bcac12 --- /dev/null +++ b/site/content/docs/v1.9/vendoring-dependencies.md @@ -0,0 +1,21 @@ +--- +title: "Vendoring dependencies" +layout: docs +--- + +## Overview + +We are using [dep][0] to manage dependencies. You can install it by following [these +instructions][1]. + +## Adding a new dependency + +Run `dep ensure`. If you want to see verbose output, you can append `-v` as in +`dep ensure -v`. + +## Updating an existing dependency + +Run `dep ensure -update [ ...]` to update one or more dependencies. + +[0]: https://github.com/golang/dep +[1]: https://golang.github.io/dep/docs/installation.html diff --git a/site/content/docs/v1.9/website-guidelines.md b/site/content/docs/v1.9/website-guidelines.md new file mode 100644 index 0000000000..c5373f4d8c --- /dev/null +++ b/site/content/docs/v1.9/website-guidelines.md @@ -0,0 +1,45 @@ +--- +title: "Website Guidelines" +layout: docs +--- + +## Running the website locally + +When making changes to the website, please run the site locally before submitting a PR and manually verify your changes. + +At the root of the project, run: + +```bash +make serve-docs +``` + +This runs all the Hugo dependencies in a container. + +Alternatively, for quickly loading the website, under the `velero/site/` directory run: + +```bash +hugo serve +``` + +For more information on how to run the website locally, please see our [Hugo documentation](https://gohugo.io/getting-started/). + +## Adding a blog post + +To add a blog post, create a new markdown (.MD) file in the `/site/content/posts/` folder. A blog post requires the following front matter. + +```yaml +title: "Title of the blog" +excerpt: Brief summary of thee blog post that appears as a preview on velero.io/blogs +author_name: Jane Smith +slug: URL-For-Blog +# Use different categories that apply to your blog. This is used to connect related blogs on the site +categories: ['velero','release'] +# Image to use for blog. The path is relative to the site/static/ folder +image: /img/posts/example-image.jpg +# Tag should match author to drive author pages. Tags can have multiple values. +tags: ['Velero Team', 'Nolan Brubaker'] +``` + +Include the `author_name` value in tags field so the page that lists the author's posts will work properly, for example https://velero.io/tags/carlisia-thompson/. + +Ideally each blog will have a unique image to use on the blog home page, but if you do not include an image, the default Velero logo will be used instead. Use an image that is less than 70KB and add it to the `/site/static/img/posts` folder. diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index 2b014fce1a..0402cc29cd 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -13,8 +13,8 @@ toc: url: /basic-install - page: Customize Installation url: /customize-installation - - page: Upgrade to 1.8 - url: /upgrade-to-1.8 + - page: Upgrade to 1.9 + url: /upgrade-to-1.9 - page: Supported providers url: /supported-providers - page: Evaluation install diff --git a/site/data/docs/toc-mapping.yml b/site/data/docs/toc-mapping.yml index f520c0f8b7..28b647ba26 100644 --- a/site/data/docs/toc-mapping.yml +++ b/site/data/docs/toc-mapping.yml @@ -3,6 +3,7 @@ # that the navigation for older versions still work. main: main-toc +v1.9: v1-9-toc v1.8: v1-8-toc v1.7: v1-7-toc v1.6: v1-6-toc diff --git a/site/data/docs/v1-9-toc.yml b/site/data/docs/v1-9-toc.yml new file mode 100644 index 0000000000..0402cc29cd --- /dev/null +++ b/site/data/docs/v1-9-toc.yml @@ -0,0 +1,97 @@ +toc: + - title: Introduction + subfolderitems: + - page: About Velero + url: /index.html + - page: How Velero works + url: /how-velero-works + - page: About locations + url: /locations + - title: Install + subfolderitems: + - page: Basic Install + url: /basic-install + - page: Customize Installation + url: /customize-installation + - page: Upgrade to 1.9 + url: /upgrade-to-1.9 + - page: Supported providers + url: /supported-providers + - page: Evaluation install + url: /contributions/minio + - page: Restic integration + url: /restic + - page: Examples + url: /examples + - page: Uninstalling + url: /uninstalling + - title: Use + subfolderitems: + - page: Disaster recovery + url: /disaster-case + - page: Cluster migration + url: /migration-case + - page: Enable API group versions + url: /enable-api-group-versions-feature + - page: Resource filtering + url: /resource-filtering + - page: Backup reference + url: /backup-reference + - page: Backup hooks + url: /backup-hooks + - page: Restore reference + url: /restore-reference + - page: Restore hooks + url: /restore-hooks + - page: Run in any namespace + url: /namespace + - page: CSI Support + url: /csi + - page: Verifying Self-signed Certificates + url: /self-signed-certificates + - page: Changing RBAC permissions + url: /rbac + - title: Plugins + subfolderitems: + - page: Overview + url: /overview-plugins + - page: Custom plugins + url: /custom-plugins + - title: Troubleshoot + subfolderitems: + - page: Troubleshooting + url: /troubleshooting + - page: Troubleshoot an install or setup + url: /debugging-install + - page: Troubleshoot a restore + url: /debugging-restores + - page: Troubleshoot Restic + url: /restic#troubleshooting + - title: Contribute + subfolderitems: + - page: Start Contributing + url: /start-contributing + - page: Development + url: /development + - page: Rapid development with Tilt + url: /tilt + - page: Build from source + url: /build-from-source + - page: Run locally + url: /run-locally + - page: Code standards + url: /code-standards + - page: Website guidelines + url: /website-guidelines + - page: Documentation style guide + url: /style-guide + - title: More information + subfolderitems: + - page: Backup file format + url: /output-file-format + - page: API types + url: /api-types + - page: Support process + url: /support-process + - page: For maintainers + url: /maintainers From 735d506a7d1b5036669dacd5e13883406adc6b04 Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Fri, 10 Jun 2022 07:57:58 +0000 Subject: [PATCH 192/366] Add support to download CSI json from object store Signed-off-by: GitHub --- .../crd/v1/bases/velero.io_downloadrequests.yaml | 2 ++ design/CLI/PoC/base/CRDs.yaml | 2 ++ pkg/apis/velero/v1/download_request_types.go | 16 +++++++++------- pkg/persistence/object_store.go | 4 ++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/config/crd/v1/bases/velero.io_downloadrequests.yaml b/config/crd/v1/bases/velero.io_downloadrequests.yaml index 45f229c44b..498ee8b943 100644 --- a/config/crd/v1/bases/velero.io_downloadrequests.yaml +++ b/config/crd/v1/bases/velero.io_downloadrequests.yaml @@ -50,6 +50,8 @@ spec: - BackupResourceList - RestoreLog - RestoreResults + - CSIBackupVolumeSnapshots + - CSIBackupVolumeSnapshotContents type: string name: description: Name is the name of the kubernetes resource with diff --git a/design/CLI/PoC/base/CRDs.yaml b/design/CLI/PoC/base/CRDs.yaml index f6753953f9..ae2f5f1936 100644 --- a/design/CLI/PoC/base/CRDs.yaml +++ b/design/CLI/PoC/base/CRDs.yaml @@ -505,6 +505,8 @@ spec: - BackupResourceList - RestoreLog - RestoreResults + - CSIBackupVolumeSnapshots + - CSIBackupVolumeSnapshotContents type: string name: description: Name is the name of the kubernetes resource with diff --git a/pkg/apis/velero/v1/download_request_types.go b/pkg/apis/velero/v1/download_request_types.go index 7ca5dcb17e..ad359ba88a 100644 --- a/pkg/apis/velero/v1/download_request_types.go +++ b/pkg/apis/velero/v1/download_request_types.go @@ -29,13 +29,15 @@ type DownloadRequestSpec struct { type DownloadTargetKind string const ( - DownloadTargetKindBackupLog DownloadTargetKind = "BackupLog" - DownloadTargetKindBackupContents DownloadTargetKind = "BackupContents" - DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots" - DownloadTargetKindBackupItemSnapshots DownloadTargetKind = "BackupItemSnapshots" - DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList" - DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog" - DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults" + DownloadTargetKindBackupLog DownloadTargetKind = "BackupLog" + DownloadTargetKindBackupContents DownloadTargetKind = "BackupContents" + DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots" + DownloadTargetKindBackupItemSnapshots DownloadTargetKind = "BackupItemSnapshots" + DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList" + DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog" + DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults" + DownloadTargetKindCSIBackupVolumeSnapshots DownloadTargetKind = "CSIBackupVolumeSnapshots" + DownloadTargetKindCSIBackupVolumeSnapshotContents DownloadTargetKind = "CSIBackupVolumeSnapshotContents" ) // DownloadTarget is the specification for what kind of file to download, and the name of the diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 20bf9328e2..1c0b619e1f 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -520,6 +520,10 @@ func (s *objectBackupStore) GetDownloadURL(target velerov1api.DownloadTarget) (s return s.objectStore.CreateSignedURL(s.bucket, s.layout.getRestoreLogKey(target.Name), DownloadURLTTL) case velerov1api.DownloadTargetKindRestoreResults: return s.objectStore.CreateSignedURL(s.bucket, s.layout.getRestoreResultsKey(target.Name), DownloadURLTTL) + case velerov1api.DownloadTargetKindCSIBackupVolumeSnapshots: + return s.objectStore.CreateSignedURL(s.bucket, s.layout.getCSIVolumeSnapshotKey(target.Name), DownloadURLTTL) + case velerov1api.DownloadTargetKindCSIBackupVolumeSnapshotContents: + return s.objectStore.CreateSignedURL(s.bucket, s.layout.getCSIVolumeSnapshotContentsKey(target.Name), DownloadURLTTL) default: return "", errors.Errorf("unsupported download target kind %q", target.Kind) } From a0ff46a3deab68db986f0e848ab872fe86b071ad Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Mon, 13 Jun 2022 13:22:45 +0000 Subject: [PATCH 193/366] Add kubebuilder annotation for autogen Signed-off-by: GitHub --- pkg/apis/velero/v1/download_request_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/velero/v1/download_request_types.go b/pkg/apis/velero/v1/download_request_types.go index ad359ba88a..f75ddd7032 100644 --- a/pkg/apis/velero/v1/download_request_types.go +++ b/pkg/apis/velero/v1/download_request_types.go @@ -25,7 +25,7 @@ type DownloadRequestSpec struct { } // DownloadTargetKind represents what type of file to download. -// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemSnapshots;BackupResourceList;RestoreLog;RestoreResults +// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemSnapshots;BackupResourceList;RestoreLog;RestoreResults;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents type DownloadTargetKind string const ( From a30b61b3d79f024ec2bf8793124d7cb519212822 Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Mon, 13 Jun 2022 13:37:47 +0000 Subject: [PATCH 194/366] Add changelog Signed-off-by: GitHub --- changelogs/unreleased/4980-anshulahuja98 | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4980-anshulahuja98 diff --git a/changelogs/unreleased/4980-anshulahuja98 b/changelogs/unreleased/4980-anshulahuja98 new file mode 100644 index 0000000000..8332b5f2cf --- /dev/null +++ b/changelogs/unreleased/4980-anshulahuja98 @@ -0,0 +1 @@ +Added DownloadTargetKindCSIBackupVolumeSnapshots for retrieving the signed URL to download only the ``-csi-volumesnapshots.json.gz and DownloadTargetKindCSIBackupVolumeSnapshotContents to download only ``-csi-volumesnapshotcontents.json.gz in the DownloadRequest CR structure. These files are already present in the backup layout. \ No newline at end of file From fad4b0e99f4f4a0573361e5a127e82cd33b518ee Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Mon, 13 Jun 2022 16:00:17 +0000 Subject: [PATCH 195/366] auto generated file Signed-off-by: GitHub --- config/crd/v1/crds/crds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 6ead090277..7b9cbb1d9d 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -32,7 +32,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xacӽ\x14=\xd0\xd4Xb\x97\"Y\xceЛm\xd1\xff^\f)\xf9C\x96\xf7\xe3P\xdeD\x0e\x87\x8f\x8fo\x1e\xa9EUU\v\x15\xcc-F2ޭ@\x05\x83_\x19\x9d|Q}\xf7#\xd5\xc6/w\xef\x16w\xc65+\xb8Jľ\xbfA\xf2)j|\x8f[\xe3\f\x1b\xef\x16=\xb2j\x14\xab\xd5\x02@9\xe7YI7\xc9'\x80\xf6\x8e\xa3\xb7\x16cբ\xab\xef\xd2\x067\xc9\xd8\x06cN>.\xbd{[\xffP\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\xbd\xb3^5\x11\xffJHL\xf5\x0e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05\x87\x812w\x00T6\xf3~HsS\xd2\xe4\x11k\x88\u007f\x99\x1b\xfdh\x86\x88`ST\xf6\x1cD\x1e$\xe3\xdadU<\x1b^\x00\x90\xf6\x01W\xf0I`\x04\xa5\xb1Y\x00\f{ϰ\xaaaw\xbbw%\x95\xee\xb0W\x05/\x80\x0f\xe8~\xfa|}\xfb\xfd\xfa\xa4\x1b\xa0A\xd2\xd1\x04\xce\fN0\x83!P0 \x00\xf6{P\xa0\x1c\xa8\xc8f\xab4\xc36\xfa\x1e6Jߥ\xb0\xcf\n\xe07\u007f\xa2f \xf6Q\xb5\xf8\x06(\xe9\x0e\x94\xe4+\xa1`}\v[c\xb1\xdeO\n\xd1\a\x8clF\x96K;\x12\xd7Q\xef\x04\xf8k\xd9[\x89\x82FT\x85\x04\xdc\xe1\xc8\x0f6\x03\x1d\xe0\xb7\xc0\x9d!\x88\x18\"\x12\xba\xa2\xb3\x93\xc4 A\xca\r;\xa8a\x8dQ\xd2\x00u>\xd9Fĸ\xc3\xc8\x10Q\xfb֙\xbf\xf7\xb9I\x18\x92E\xad\xe2Q\x0e\x87f\x1cct\xca\xc2Nلo@\xb9\x06z\xf5\x00\x113O\xc9\x1d\xe5\xcb!Tï>\"\x18\xb7\xf5+\xe8\x98\x03\xad\x96\xcb\xd6\xf0XT\xda\xf7}r\x86\x1f\x96\xb9>\xcc&\xb1\x8f\xb4lp\x87vI\xa6\xadTԝaԜ\".U0U\x86\xeera\xd5}\xf3M\x1cʐ^\x9f`\xe5\a\x91\x19q4\xae=\x1aȚ\u007f\xe4\x04D\xf5E0ej\xd9Łh\xe9\x12vn>\xac\xbf\xc0\xb8t>\x8c)\xfbE9\xfb\x89t8\x02!̸-\xc6r\x88Yy\x92\x13]\x13\xbcq\x9c?\xb45\xe8\xa6\xf4S\xda\xf4\x86i\x14\xb3\x9cU\rW\xd9i`\x83\x90B\xa3\x18\x9b\x1a\xae\x1d\\\xa9\x1e\xed\x95\"\xfc\xdf\x0f@\x98\xa6J\x88}\xde\x11\x1c\x9b\xe44\xb8\xb0v40:م\xf3\x9a\x94\xfa:\xa0\x96\xd3\x13\x02e\xa6\xd9\x1a\x9dK\x03\xb6>\x82:T\xfe@`}\x92y\xber38\x15[\xe4i\xef\x04˗\x1c$\xcb\xdfw\xea\xd4h\xbeź\xad\xc5+h\x00R\xdc\xe3\xbb\xfa,\xe3e\f0\xab\xdeY$\xa3\x88\x85\x06\xe1U\xac@L\xea\x18\xd3\xf9\xd2\xd2Х~~\x81\n~Θ?\xfa\xf6\xd1\xf1+\xefX\xe4\xfehЭ\xb7\xa9ǵS\x81:\xffD\xec5c\xff\xbc\xc8\xf1B\xde_R\xe7\x817(V\x8e\x9771\x04\xdc %{q\xb9\xab\xf5\xf5K\xf6q!\xfcQ\xa6.\xd4\xce\xd8\xf2\x1d\xf9\xb4\x10\xe4\x96\x1d\x85 S\xcaŁ o\x8f萑\x0e\x1evo\xb8\x9b\xcd\bp\xdf\x19\xdd\xe5\x89YEb\x8fD^\x9bl6/\x87/\xc5g\"\xce(\xb9\xca\n\x9f\xe9\x16\xf0g\xdd\x17,\xe3\xd2\x02\xd5P\xc6ϲ\x1dV\x9c\xe8\x05Ɠ\xe3G\xaau\x8a\x11\x1d\x0fY\xf2E<\x9d\xf0\\\xe7\x19\xcb\xf5\xb7\x9b\x8fO\xd8\xcf\xfbCd~j*\xe3\n\x9a\x10\xb1\"\xd3\xca\xf3A\xc6Ā\xb21\x9c\x93Q\xda\xe9s攨\xd9\x13ů\xc1\xc4l\xb3O@\xfc\xb0\x0f,.\x89\xae܀\xd3\a[N\x88\x94_\x17ZM\xdf5\xd26\b\rZdl`\xf3P\xec\xfe\x81\x18\xfbs\xdc[\x1f{\xc5+\x90\x9b\xb1b3##yT\xab\x8d\xc5\x15pL\x97T6\xbb\xf1\xd0)\x9a)Ó=\u007f\x96\x989a\xec\x8b\xf1Qe\xc0ES\xae\xe0\x13\xde\xcf\xf4~\x8e^#\x11\x9e\x97\xd1ŝ\xcc\x16\xc1Y'\xc9\xf3\xa59bix\x15\x1f\xf7\xa4\xcd\xfe96\"\x1eJ\t\xfe\xf9wq\xa8*\xa55\x06\xc6\xe6\xd3\xf4o\xe4ի\x93ߋ\xfc\xa9\xbdkL\xf9\x91\x82\xdf\xffX\x94\x85\xb1\xb9\x1d\xff\x1a\xa4\xf3\xbf\x00\x00\x00\xff\xff\x85hm\xa4\xc2\r\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), From 8a8b9d07dbc530ebafcc88e14fe4b7c4c8a24895 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 14 Jun 2022 00:15:43 +0800 Subject: [PATCH 196/366] Amend doc 1.9 version Signed-off-by: Ming --- CHANGELOG.md | 1 + site/content/docs/v1.9/_index.md | 2 +- site/content/docs/v1.9/backup-hooks.md | 2 +- site/content/docs/v1.9/code-standards.md | 2 +- site/content/docs/v1.9/custom-plugins.md | 2 +- site/content/docs/v1.9/customize-installation.md | 2 +- site/content/docs/v1.9/maintainers.md | 8 ++++---- site/content/docs/v1.9/release-instructions.md | 2 +- site/content/docs/v1.9/start-contributing.md | 8 ++++---- site/content/docs/v1.9/support-process.md | 2 +- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 302a7bfeb4..f8da3fd3cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Older releases: * [CHANGELOG-1.8.md][18] + * [CHANGELOG-1.7.md][17] * [CHANGELOG-1.6.md][16] * [CHANGELOG-1.5.md][15] * [CHANGELOG-1.4.md][14] diff --git a/site/content/docs/v1.9/_index.md b/site/content/docs/v1.9/_index.md index 911f8b571e..376b5196ef 100644 --- a/site/content/docs/v1.9/_index.md +++ b/site/content/docs/v1.9/_index.md @@ -1,7 +1,7 @@ --- toc: "false" cascade: - version: main + version: v1.9 toc: "true" --- ![100] diff --git a/site/content/docs/v1.9/backup-hooks.md b/site/content/docs/v1.9/backup-hooks.md index 3a1ceffc6c..e2c5b1eb4a 100644 --- a/site/content/docs/v1.9/backup-hooks.md +++ b/site/content/docs/v1.9/backup-hooks.md @@ -107,5 +107,5 @@ Note that the container must support the shell command you use. [1]: api-types/backup.md -[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/examples/nginx-app/with-pv.yaml +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/examples/nginx-app/with-pv.yaml [3]: cloud-common.md diff --git a/site/content/docs/v1.9/code-standards.md b/site/content/docs/v1.9/code-standards.md index 665116d06b..3d3ea252b7 100644 --- a/site/content/docs/v1.9/code-standards.md +++ b/site/content/docs/v1.9/code-standards.md @@ -70,7 +70,7 @@ Example: We use a package to generate mocks for our interfaces. -Example: if you want to change this mock: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/restic/mocks/restorer.go +Example: if you want to change this mock: https://github.com/vmware-tanzu/velero/blob/v1.9.0/pkg/restic/mocks/restorer.go Run: diff --git a/site/content/docs/v1.9/custom-plugins.md b/site/content/docs/v1.9/custom-plugins.md index ba0b357dc1..8f92ec17ba 100644 --- a/site/content/docs/v1.9/custom-plugins.md +++ b/site/content/docs/v1.9/custom-plugins.md @@ -112,4 +112,4 @@ Once parsed into a `[]string`, the features can then be registered using the `Ne Velero adds the `LD_LIBRARY_PATH` into the list of environment variables to provide the convenience for plugins that requires C libraries/extensions in the runtime. [1]: https://github.com/vmware-tanzu/velero-plugin-example -[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/plugin/logger.go +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/pkg/plugin/logger.go diff --git a/site/content/docs/v1.9/customize-installation.md b/site/content/docs/v1.9/customize-installation.md index b8721662a3..9cf00f0542 100644 --- a/site/content/docs/v1.9/customize-installation.md +++ b/site/content/docs/v1.9/customize-installation.md @@ -387,4 +387,4 @@ If you get an error like `complete:13: command not found: compdef`, then add the [8]: https://github.com/vmware-tanzu/velero/issues/2311 [9]: self-signed-certificates.md [10]: csi.md -[11]: https://github.com/vmware-tanzu/velero/blob/v1.9/pkg/apis/velero/v1/constants.go +[11]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/pkg/apis/velero/v1/constants.go diff --git a/site/content/docs/v1.9/maintainers.md b/site/content/docs/v1.9/maintainers.md index 6de609ccef..c100601a3e 100644 --- a/site/content/docs/v1.9/maintainers.md +++ b/site/content/docs/v1.9/maintainers.md @@ -4,7 +4,7 @@ layout: docs toc: "true" --- -There are some guidelines maintainers need to follow. We list them here for quick reference, especially for new maintainers. These guidelines apply to all projects in the Velero org, including the main project, the Velero Helm chart, and all other [related repositories](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#code-repositories). +There are some guidelines maintainers need to follow. We list them here for quick reference, especially for new maintainers. These guidelines apply to all projects in the Velero org, including the main project, the Velero Helm chart, and all other [related repositories](https://github.com/vmware-tanzu/velero/blob/v1.9.0/GOVERNANCE.md#code-repositories). Please be sure to also go through the guidance under the entire [Contribute](start-contributing/) section. @@ -14,12 +14,12 @@ Please be sure to also go through the guidance under the entire [Contribute](sta - As you review a PR that is not yet ready to merge, please check if the "request review" needs to be refreshed for any reviewer (this is better than @mention at them) - Refrain from @mention other maintainers to review the PR unless it is an immediate need. All maintainers already get notified through the automated add to the "request review". If it is an urgent need, please add a helpful message as to why it is so people can properly prioritize work. - There is no need to manually request reviewers: after the PR is created, all maintainers will be automatically added to the list (note: feel free to remove people if they are on PTO, etc). -- Be familiar with the [lazy consensus](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#lazy-consensus) policy for the project. +- Be familiar with the [lazy consensus](https://github.com/vmware-tanzu/velero/blob/v1.9.0/GOVERNANCE.md#lazy-consensus) policy for the project. Some tips for doing reviews: - There are some [code standards and general guidelines](https://velero.io/docs/v1.9/code-standards) we aim for - We have [guidelines for writing and reviewing documentation](https://velero.io/docs/v1.9/style-guide/) -- When reviewing a design document, ensure it follows [our format and guidelines]( https://github.com/vmware-tanzu/velero/blob/v1.9/design/_template.md). Also, when reviewing a PR that implements a previously accepted design, ensure the associated design doc is moved to the [design/implemented](https://github.com/vmware-tanzu/velero/tree/main/design/implemented) folder. +- When reviewing a design document, ensure it follows [our format and guidelines]( https://github.com/vmware-tanzu/velero/blob/v1.9.0/design/_template.md). Also, when reviewing a PR that implements a previously accepted design, ensure the associated design doc is moved to the [design/implemented](https://github.com/vmware-tanzu/velero/tree/main/design/implemented) folder. ## Creating a release @@ -34,4 +34,4 @@ Maintainers are expected to participate in the community support rotation. We ha Maintainers for the Velero project are highly involved with the open source community. All the online community meetings for the project are listed in our [community](community) page. ## How do I become a maintainer? -The Velero project welcomes contributors of all kinds. We are also always on the look out for a high level of engagement from contributors and opportunities to bring in new maintainers. If this is of interest, take a look at how [adding a maintainer](https://github.com/vmware-tanzu/velero/blob/v1.9/GOVERNANCE.md#maintainers) is decided. +The Velero project welcomes contributors of all kinds. We are also always on the look out for a high level of engagement from contributors and opportunities to bring in new maintainers. If this is of interest, take a look at how [adding a maintainer](https://github.com/vmware-tanzu/velero/blob/v1.9.0/GOVERNANCE.md#maintainers) is decided. diff --git a/site/content/docs/v1.9/release-instructions.md b/site/content/docs/v1.9/release-instructions.md index f62c355011..94d77f4d5e 100644 --- a/site/content/docs/v1.9/release-instructions.md +++ b/site/content/docs/v1.9/release-instructions.md @@ -9,7 +9,7 @@ This page covers the steps to perform when releasing a new version of Velero. - Please read the documented variables in each script to understand what they are for and how to properly format their values. - You will need to have an upstream remote configured to use to the [vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) repository. You can check this using `git remote -v`. - The release script ([`tag-release.sh`](https://github.com/vmware-tanzu/velero/blob/v1.9/hack/release-tools/tag-release.sh)) will use `upstream` as the default remote name if it is not specified using the environment variable `REMOTE`. + The release script ([`tag-release.sh`](https://github.com/vmware-tanzu/velero/blob/v1.9.0/hack/release-tools/tag-release.sh)) will use `upstream` as the default remote name if it is not specified using the environment variable `REMOTE`. - GA release: major and minor releases only. Example: 1.0 (major), 1.5 (minor). - Pre-releases: Any release leading up to a GA. Example: 1.4.0-beta.1, 1.5.0-rc.1 - RC releases: Release Candidate, contains everything that is supposed to ship with the GA release. This is still a pre-release. diff --git a/site/content/docs/v1.9/start-contributing.md b/site/content/docs/v1.9/start-contributing.md index 5f30ab3935..2199550b86 100644 --- a/site/content/docs/v1.9/start-contributing.md +++ b/site/content/docs/v1.9/start-contributing.md @@ -28,7 +28,7 @@ Please browse our list of resources, including a playlist of past online communi If you are ready to jump in and test, add code, or help with documentation, please use the navigation on the left under `Contribute`. -[1]: https://github.com/vmware-tanzu/velero/blob/v1.9/CODE_OF_CONDUCT.md -[2]: https://github.com/vmware-tanzu/velero/blob/v1.9/CONTRIBUTING.md -[3]: https://github.com/vmware-tanzu/velero/blob/v1.9/.github/ISSUE_TEMPLATE/feature-enhancement-request.md -[4]: https://github.com/vmware-tanzu/velero/blob/v1.9/.github/ISSUE_TEMPLATE/bug_report.md +[1]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/CODE_OF_CONDUCT.md +[2]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/CONTRIBUTING.md +[3]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/.github/ISSUE_TEMPLATE/feature-enhancement-request.md +[4]: https://github.com/vmware-tanzu/velero/blob/v1.9.0/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/site/content/docs/v1.9/support-process.md b/site/content/docs/v1.9/support-process.md index d3d7c8ecbe..30c6a88449 100644 --- a/site/content/docs/v1.9/support-process.md +++ b/site/content/docs/v1.9/support-process.md @@ -6,7 +6,7 @@ layout: docs Velero provides best effort support through the process on this page for the current version of Velero and n-1 Velero version, including all patch releases in the supported minor releases. For example, if the current version is 1.9, the Velero maintainers would offer best effort support for v1.9 and v1.8. If you have a question about a previous Velero version (for example, 1.7), please note that maintainers may ask you to upgrade to a supported version before doing any investigation into your issue. -For more information about Velero testing and supported Kubernetes versions, see Velero's [compatibility matrix](https://github.com/vmware-tanzu/velero/blob/v1.9/README.md#velero-compatibility-matrix). +For more information about Velero testing and supported Kubernetes versions, see Velero's [compatibility matrix](https://github.com/vmware-tanzu/velero/blob/v1.9.0/README.md#velero-compatibility-matrix). ## Weekly Rotation From c78b10e150d49e4361493dd179ff935319686651 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 14 Jun 2022 06:47:18 +0000 Subject: [PATCH 197/366] Add more explanation for gen-docs parameters Signed-off-by: Ming --- site/content/docs/main/release-instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/release-instructions.md b/site/content/docs/main/release-instructions.md index 608b441687..c8eb317dda 100644 --- a/site/content/docs/main/release-instructions.md +++ b/site/content/docs/main/release-instructions.md @@ -67,7 +67,9 @@ For each major or minor release, create and publish a blog post to let folks kno - Run `make gen-docs`, passing the appropriate variables. Examples: a) `VELERO_VERSION=v1.5.0-rc.1 NEW_DOCS_VERSION=v1.5.0-rc.1 make gen-docs`. b) `VELERO_VERSION=v1.5.0 NEW_DOCS_VERSION=v1.5 make gen-docs`). - - Note: `PREVIOUS_DOCS_VERSION=` is optional; when not set, it will default to the latest doc version. + - Note: + - `PREVIOUS_DOCS_VERSION=` is optional; when not set, it will default to the latest doc version. + - `VELERO_VERSION` and `NEW_DOCS_VERSION` are slightly different, the `VELERO_VERSION` may have lots of small release versions for one specific $major.minor, such as 'v1.5.0' and 'v1.5.1', but `NEW_DOCS_VERSION` may still be 'v1.5' for not document update. 1. Clean up when there is an existing set of pre-release versioned docs for the version you are releasing - Example: `site/content/docs/v1.5.0-beta.1` exists, and you're releasing `v1.5.0-rc.1` or `v1.5` - Remove the directory containing the pre-release docs, i.e. `site/content/docs/`. From feb411cc3f8cc49b9c1505e5ac625401e24f60b4 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 14 Jun 2022 06:47:18 +0000 Subject: [PATCH 198/366] Add more explanation for gen-docs parameters Signed-off-by: Ming --- site/content/docs/main/release-instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/release-instructions.md b/site/content/docs/main/release-instructions.md index 608b441687..c8eb317dda 100644 --- a/site/content/docs/main/release-instructions.md +++ b/site/content/docs/main/release-instructions.md @@ -67,7 +67,9 @@ For each major or minor release, create and publish a blog post to let folks kno - Run `make gen-docs`, passing the appropriate variables. Examples: a) `VELERO_VERSION=v1.5.0-rc.1 NEW_DOCS_VERSION=v1.5.0-rc.1 make gen-docs`. b) `VELERO_VERSION=v1.5.0 NEW_DOCS_VERSION=v1.5 make gen-docs`). - - Note: `PREVIOUS_DOCS_VERSION=` is optional; when not set, it will default to the latest doc version. + - Note: + - `PREVIOUS_DOCS_VERSION=` is optional; when not set, it will default to the latest doc version. + - `VELERO_VERSION` and `NEW_DOCS_VERSION` are slightly different, the `VELERO_VERSION` may have lots of small release versions for one specific $major.minor, such as 'v1.5.0' and 'v1.5.1', but `NEW_DOCS_VERSION` may still be 'v1.5' for not document update. 1. Clean up when there is an existing set of pre-release versioned docs for the version you are releasing - Example: `site/content/docs/v1.5.0-beta.1` exists, and you're releasing `v1.5.0-rc.1` or `v1.5` - Remove the directory containing the pre-release docs, i.e. `site/content/docs/`. From 3bb6252d152097ac5e61edeb333385feb67bfaba Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 14 Jun 2022 06:08:56 +0000 Subject: [PATCH 199/366] Pin the base image and golang image for v1.9.0 release Signed-off-by: Ming --- Dockerfile | 4 ++-- hack/build-image/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8ffe8abff4..8e3b99fd87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=$BUILDPLATFORM golang:1.17 as builder-env +FROM --platform=$BUILDPLATFORM golang:1.17.11 as builder-env ARG GOPROXY ARG PKG @@ -50,7 +50,7 @@ RUN mkdir -p /output/usr/bin && \ go build -o /output/${BIN} \ -ldflags "${LDFLAGS}" ${PKG}/cmd/${BIN} -FROM gcr.io/distroless/base-debian11:nonroot +FROM gcr.io/distroless/base-debian11@sha256:e672eb713e56feb13e349773973b81b1b9284f70b15cf18d1a09ad31a03abe59 LABEL maintainer="Nolan Brubaker " diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 5f35646310..8948080d03 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.17 +FROM golang:1.17.11 ARG GOPROXY From 0d9af1017bc2a8820eb55198eacf3a44251bc277 Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 14 Jun 2022 08:14:12 +0000 Subject: [PATCH 200/366] Fix wrong bucket issue in BSL deletion E2E test Signed-off-by: danfengl --- test/e2e/bsl-mgmt/deletion.go | 4 ++-- test/e2e/util/providers/common.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index b605313461..e5dd0a6b54 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -221,7 +221,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, - VeleroCfg.CloudCredentialsFile, VeleroCfg.AdditionalBSLBucket, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_2), func() { @@ -231,7 +231,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { if VeleroCfg.CloudProvider == "vsphere" { BSLCredentials = VeleroCfg.AdditionalBSLCredentials BSLConfig = VeleroCfg.AdditionalBSLConfig - } else { + } else { // Snapshotting with non-vSphere provider has nothing to do with BSL BSLCredentials = VeleroCfg.CloudCredentialsFile BSLConfig = VeleroCfg.BSLConfig } diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index 4ca8ac1436..2a4e2218b4 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -36,9 +36,9 @@ type ObjectsInStorage interface { func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { fmt.Printf("|| VERIFICATION || - %s %s should exist in storage [%s]\n", subPrefix, backupName, bslPrefix) - exist, _ := IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) + exist, err := IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if !exist { - return errors.New(fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected\n", backupName)) + return errors.Wrap(err, fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected\n", backupName)) } fmt.Printf("|| EXPECTED || - Backup %s exist in object storage bucket %s\n", backupName, bslBucket) return nil From 0c069b309870b5488dcf3853f0d5d3a674535388 Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 14 Jun 2022 08:02:19 +0000 Subject: [PATCH 201/366] Bumpup plugin version for Velero 1.9 E2E test Signed-off-by: danfengl --- test/e2e/util/velero/velero_utils.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index aa4d8eec43..46b9982e1d 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -87,6 +87,13 @@ var pluginsMatrix = map[string]map[string][]string{ "gcp": {"velero/velero-plugin-for-gcp:v1.4.0"}, "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.4.0", "velero/velero-plugin-for-csi:v0.2.0"}, }, + "v1.9": { + "aws": {"velero/velero-plugin-for-aws:v1.5.0"}, + "azure": {"velero/velero-plugin-for-microsoft-azure:v1.5.0"}, + "vsphere": {"velero/velero-plugin-for-aws:v1.5.0", "vsphereveleroplugin/velero-plugin-for-vsphere:v1.4.0"}, + "gcp": {"velero/velero-plugin-for-gcp:v1.5.0"}, + "azure-csi": {"velero/velero-plugin-for-microsoft-azure:v1.5.0", "velero/velero-plugin-for-csi:v0.3.0"}, + }, "main": { "aws": {"velero/velero-plugin-for-aws:main"}, "azure": {"velero/velero-plugin-for-microsoft-azure:main"}, From ef02113b493faa13f5bb56bac6172fb99430aab5 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 14 Jun 2022 09:34:05 +0000 Subject: [PATCH 202/366] Fix tag release error Signed-off-by: Ming --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index e7fa81f58b..f30fbe8c23 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,7 +14,7 @@ dist: _output builds: - - main: ./cmd/velero/main.go + - main: ./cmd/velero/velero.go env: - CGO_ENABLED=0 goos: From a27b74a00426065ab929c58bb1187e79b5adf103 Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Mon, 13 Jun 2022 16:00:17 +0000 Subject: [PATCH 203/366] auto generated file Signed-off-by: GitHub --- config/crd/v1/crds/crds.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 748e958c12..7b9cbb1d9d 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,15 +30,15 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMs\xdc6\f\xbd\xef\xaf\xc0\xa4\x87\xb43\x916\x99\x1e\xda\xd9[\xeb\xe4\x90i\xea\xf1\xac]_:=p)\xacĚ\"Y\x02\\\xc7\xfd\xf5\x1d\x90\xd2~h\xb5\xb6s(o\x02A\xf0\x11xx\xa4\x16UU-T0\xf7\x18\xc9x\xb7\x02\x15\f~et\xf2E\xf5\xc3\xcfT\x1b\xbf\xdc}X<\x18\u05ec\xe0*\x11\xfb~\x8d\xe4S\xd4\xf8\x11\xb7\xc6\x196\xde-zd\xd5(V\xab\x05\x80rγ\x123\xc9'\x80\xf6\x8e\xa3\xb7\x16cբ\xab\x1f\xd2\x067\xc9\xd8\x06c\x0e>n\xbd{_\xffT\xbf_\x00\xe8\x88y\xf9\x9d\xe9\x91X\xf5a\x05.Y\xbb\x00p\xaa\xc7\x154\xfe\xd1Y\xaf\x9a\x88\xff$$\xa6z\x87\x16\xa3\xaf\x8d_P@-\x9b\xb6ѧ\xb0\x82\xc3DY;\x00*\x87\xf98\x84Y\x970y\xc6\x1a\xe2\xdf\xe6f\xbf\x98\xc1#\xd8\x14\x95=\a\x91'ɸ6Y\x15Ϧ\x17\x00\xa4}\xc0\x15\\\v\x8c\xa046\v\x80\xe1\xec\x19V5\x9cn\xf7\xa1\x84\xd2\x1d\xf6\xaa\xe0\x05\xf0\x01\xdd/7\x9f\xef\u007f\xbc=1\x034H:\x9a\xc09\x83\x13\xcc`\b\x14\f\b\x80\xfd\x1e\x14(\a*\xb2\xd9*Ͱ\x8d\xbe\x87\x8d\xd2\x0f)\xec\xa3\x02\xf8\xcdߨ\x19\x88}T-\xbe\x03J\xba\x03%\xf1\x8a+X\xdf\xc2\xd6X\xac\xf7\x8bB\xf4\x01#\x9b1\xcbe\x1c\x91\xeb\xc8:\x01\xfeV\xceV\xbc\xa0\x11V!\x01w8\xe6\a\x9b!\x1d\xe0\xb7\xc0\x9d!\x88\x18\"\x12\xba³\x93\xc0 N\xca\r'\xa8\xe1\x16\xa3\x84\x01\xea|\xb2\x8d\x90q\x87\x91!\xa2\xf6\xad3\xff\xeec\x93dH6\xb5\x8aG:\x1c\x86q\x8c\xd1)\v;e\x13\xbe\x03\xe5\x1a\xe8\xd5\x13D\xccyJ\xee(^v\xa1\x1a~\xf7\x11\xc1\xb8\xad_A\xc7\x1ch\xb5\\\xb6\x86ǦҾ\xef\x933\xfc\xb4\xcc\xfda6\x89}\xa4e\x83;\xb4K2m\xa5\xa2\xee\f\xa3\xe6\x14q\xa9\x82\xa92t\x97\x1b\xab\xee\x9b\xef\xe2І\xf4\xf6\x04+?\t͈\xa3q\xed\xd1D\xe6\xfc3\x15\x10\xd6\x17\u0094\xa5\xe5\x14\x87D\x8bI\xb2\xb3\xfet{\a\xe3ֹ\x18\xd3\xec\x17\xe6\xec\x17ҡ\x04\x920\xe3\xb6\x18K\x113\xf3$&\xba&x\xe38\u007fhk\xd0M\xd3Oi\xd3\x1b\xa6\x91\xccR\xab\x1a\xae\xb2\xd2\xc0\x06!\x85F165|vp\xa5z\xb4W\x8a\xf0\u007f/\x80d\x9a*I\xec\xebJp,\x92S璵\xa3\x89Q\xc9.\xd4k\xd2\xea\xb7\x01\xb5TO\x12(+\xcd\xd6\xe8\xdc\x1a\xb0\xf5\x11ԡ\xf3\x87\x04\xd6'\x91\xe7;7\x83S\xb1E\x9eZ'X\uec93l\xffةS\xa1\xf9\x1e\xeb\xb6\x16\xad\xa0\x01HQ\x8f\x1f곈\x971\xc0,{g\x91\x8c$\x964H^E\nD\xa4\x8e1\x9do-\x03]\xea\xe77\xa8\xe0\u05cc\xf9\x8bo\x9f\x9d\xbf\xf2\x8e\x85\xee\xcf:\xdd{\x9bz\xbcu*P\xe7_\xf0\xfd\xccؿ\xces\xbc\x90\xf7\x97Թ\xe3\x1aE\xca\xf1\xf2!\x06\x875R\xb2\x17\xb6\xbb@\xebq\xe4\xeb\xeb\xe5\x1a\xc9\x058\xd6H\x96\x14MG\x90gAt\xc8H\ayy4\xdc\xcdF\x04x\xec\x8c\xee\xf2\xc2\\`Q.\"\xafMցo\x87/}a\"ΐ\xac\xca\xe4\x9b1\v\xf83\xf3\x85n\xbe\xb4A5tث\x14\x81\x15'\xfa\x06M\xc8\xfec\xaau\x8a\x11\x1d\x0fQ\xf2\x1d9]\xf0ZQ\x18;\xe9\x8f\xf5\x97\x17\x94\xe1\xe3\xc13\xbf\x02\x95q\x05M\x88X\x91i\xe5f\x979цܳ\xe7\xc9(\xe3\xf4\xa5q\x9a\xa8ي\xe2\xd7`bV\xc0\x17 ~\xda;\x16\x01CW.\xa7\xe9[*\aD\xca\x17\xbfV\xd3'\x87\x8c\rB\x83\x16\x19\x1b\xd8<\x15%~\"\xc6\xfe\x1c\xf7\xd6\xc7^\xf1\n\xe4Ҫ\xd8\xcc\xd0H\u07bbjcq\x05\x1c\xd3%\x96\xcd\x1e\xcb\f\xb8\xa8\x97\x15\\\xe3\xe3\x8c\xf5&z\x8dDx\xdeF\x17O2\xdb\x04gF\x92\x97Es\x94\xa5\xe1\xc1:X\x0e-\xa3\xb4\xc6\xc0\xd8\\O\xff\x02\u07bc9y\xd6\xe7O\xed]c\xca\x0f\f\xfc\xf9עD\xc5\xe6~|\xad\x8b\xf1\xbf\x00\x00\x00\xff\xff\xef_\x1d\xd3:\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b.\xbd{[\xffP\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\xbd\xb3^5\x11\xffJHL\xf5\x0e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05\x87\x812w\x00T6\xf3~HsS\xd2\xe4\x11k\x88\u007f\x99\x1b\xfdh\x86\x88`ST\xf6\x1cD\x1e$\xe3\xdadU<\x1b^\x00\x90\xf6\x01W\xf0I`\x04\xa5\xb1Y\x00\f{ϰ\xaaaw\xbbw%\x95\xee\xb0W\x05/\x80\x0f\xe8~\xfa|}\xfb\xfd\xfa\xa4\x1b\xa0A\xd2\xd1\x04\xce\fN0\x83!P0 \x00\xf6{P\xa0\x1c\xa8\xc8f\xab4\xc36\xfa\x1e6Jߥ\xb0\xcf\n\xe07\u007f\xa2f \xf6Q\xb5\xf8\x06(\xe9\x0e\x94\xe4+\xa1`}\v[c\xb1\xdeO\n\xd1\a\x8clF\x96K;\x12\xd7Q\xef\x04\xf8k\xd9[\x89\x82FT\x85\x04\xdc\xe1\xc8\x0f6\x03\x1d\xe0\xb7\xc0\x9d!\x88\x18\"\x12\xba\xa2\xb3\x93\xc4 A\xca\r;\xa8a\x8dQ\xd2\x00u>\xd9Fĸ\xc3\xc8\x10Q\xfb֙\xbf\xf7\xb9I\x18\x92E\xad\xe2Q\x0e\x87f\x1cct\xca\xc2Nلo@\xb9\x06z\xf5\x00\x113O\xc9\x1d\xe5\xcb!Tï>\"\x18\xb7\xf5+\xe8\x98\x03\xad\x96\xcb\xd6\xf0XT\xda\xf7}r\x86\x1f\x96\xb9>\xcc&\xb1\x8f\xb4lp\x87vI\xa6\xadTԝaԜ\".U0U\x86\xeera\xd5}\xf3M\x1cʐ^\x9f`\xe5\a\x91\x19q4\xae=\x1aȚ\u007f\xe4\x04D\xf5E0ej\xd9Łh\xe9\x12vn>\xac\xbf\xc0\xb8t>\x8c)\xfbE9\xfb\x89t8\x02!̸-\xc6r\x88Yy\x92\x13]\x13\xbcq\x9c?\xb45\xe8\xa6\xf4S\xda\xf4\x86i\x14\xb3\x9cU\rW\xd9i`\x83\x90B\xa3\x18\x9b\x1a\xae\x1d\\\xa9\x1e\xed\x95\"\xfc\xdf\x0f@\x98\xa6J\x88}\xde\x11\x1c\x9b\xe44\xb8\xb0v40:م\xf3\x9a\x94\xfa:\xa0\x96\xd3\x13\x02e\xa6\xd9\x1a\x9dK\x03\xb6>\x82:T\xfe@`}\x92y\xber38\x15[\xe4i\xef\x04˗\x1c$\xcb\xdfw\xea\xd4h\xbeź\xad\xc5+h\x00R\xdc\xe3\xbb\xfa,\xe3e\f0\xab\xdeY$\xa3\x88\x85\x06\xe1U\xac@L\xea\x18\xd3\xf9\xd2\xd2Х~~\x81\n~Θ?\xfa\xf6\xd1\xf1+\xefX\xe4\xfehЭ\xb7\xa9ǵS\x81:\xffD\xec5c\xff\xbc\xc8\xf1B\xde_R\xe7\x817(V\x8e\x9771\x04\xdc %{q\xb9\xab\xf5\xf5K\xf6q!\xfcQ\xa6.\xd4\xce\xd8\xf2\x1d\xf9\xb4\x10\xe4\x96\x1d\x85 S\xcaŁ o\x8f萑\x0e\x1evo\xb8\x9b\xcd\bp\xdf\x19\xdd\xe5\x89YEb\x8fD^\x9bl6/\x87/\xc5g\"\xce(\xb9\xca\n\x9f\xe9\x16\xf0g\xdd\x17,\xe3\xd2\x02\xd5P\xc6ϲ\x1dV\x9c\xe8\x05Ɠ\xe3G\xaau\x8a\x11\x1d\x0fY\xf2E<\x9d\xf0\\\xe7\x19\xcb\xf5\xb7\x9b\x8fO\xd8\xcf\xfbCd~j*\xe3\n\x9a\x10\xb1\"\xd3\xca\xf3A\xc6Ā\xb21\x9c\x93Q\xda\xe9s攨\xd9\x13ů\xc1\xc4l\xb3O@\xfc\xb0\x0f,.\x89\xae܀\xd3\a[N\x88\x94_\x17ZM\xdf5\xd26\b\rZdl`\xf3P\xec\xfe\x81\x18\xfbs\xdc[\x1f{\xc5+\x90\x9b\xb1b3##yT\xab\x8d\xc5\x15pL\x97T6\xbb\xf1\xd0)\x9a)Ó=\u007f\x96\x989a\xec\x8b\xf1Qe\xc0ES\xae\xe0\x13\xde\xcf\xf4~\x8e^#\x11\x9e\x97\xd1ŝ\xcc\x16\xc1Y'\xc9\xf3\xa59bix\x15\x1f\xf7\xa4\xcd\xfe96\"\x1eJ\t\xfe\xf9wq\xa8*\xa55\x06\xc6\xe6\xd3\xf4o\xe4ի\x93ߋ\xfc\xa9\xbdkL\xf9\x91\x82\xdf\xffX\x94\x85\xb1\xb9\x1d\xff\x1a\xa4\xf3\xbf\x00\x00\x00\xff\xff\x85hm\xa4\xc2\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\x03V\xa9\x92%\xa4e\x06\xddMƷ\xca\xd2\xfbG\xbd~Sr\xf6\xcf\x12\x9a50\xab\x88\xb4{\xba\x8b\fk\x9cT\xa1\xb4F\x06\x859:\u007f\xc1\xfd\xf4_rQ#\ay\xc7\xd5\xd7&H\xcb#\x84\xd2X\x97\x80\xebF\x91e\x1ffJ\\;>\xf7xgY\r\xbf\x86]\xe6o\a;\xec\x16\x0f\x13\xf7խ\f\xd8\x1d'\xd2ޝm\xc3袹[{\xc76\xa1\x85.\xa5\x8b\v$\xa5\xc4\xfe\xbfu\xcbB\xea1\xe7P\xd4\x02\xbb[\x01vy\x00L\xf0;\x96\x83\xd24/\x0ePȫ\xed7\xcc\x06\b\x99\xaa\xaa\xb2a3$X7\xd9\xed2\x90i\xdd\xda9\x9d6`[0\xa8_\x18А\x12X\x01'\xeeR?f\xdaڀc\a\xd0;T\xc0\xe5\ns\u007f<\x1c\xbc\xdf7\x17\x92`\x17\xedj\xea\xdb\x14\xe1KE\xa5Tä\xb3rH\xaf\x93\xd8)E\xf1Z\xee!V\x83w\x9d\x9d@M\xf0^\x88\xd9\xde,\xb3o\xfb\x9bƮ\xb4\xc7\x03H \v\xe0\x06ŝ\x1cǩe\xb6\xb5\xabA\xac;\xc1U\xc0\xf6\xcev\xc3-\xa9\xfb\x80U\xd4+/R\x97\a\x02)\x19\x1f鬪\xb0\xaf`\x96\xbb\xe1\xfd\x0e\xa8꺧\xd0B\xc4\xd7\xcdg\x9d\xf6mq\x80KO\xa8틽\x04\x02\\3\t\x9d\xa4\xef\xa6$\xf0\xcb\x01g\x9c\x90bI\xd5!vyc\x9e\xa9Z\xf06\x0ee\xc5)\xdf\xed\x98\x13\xf02\xdf\x06>!\xd7\xf0\xd0\xf1\xeb\xd7H\xf4hQu\x1f\xa5\t\xb9\xe27R,dWW\x88\x89?X\x1d\x142!7TjF\xb3l\xfduw\xf7I\xff\xf5 ܹ\xa9\x1c\xd4\x12\xecc\xb5\xd2ĸ=\u007f\x86R\xe9\f\xbb\xbb\xd6\xc4z\xa2j:\xee\xf6\xd8 \xb4\xa91\xaa\xc0\x1b\xe3\xac\r\x14\xaf\\(=\x81\xf9\\Hm\x95\xb4Ʉ\xb0\xb9\xe3\x9f]\xca\ae\x19\xbalm\xd2\x1ca\xba\x0e\bU\x02\xc9\xd8\xfa|M$\x12+\xb6\xfa\xcd\xe9ڦ\xdc\xd0$)\xcd\xf1geNY\xb7\xde|\xa8\x88\x1e\xd6@\xb9\xdam\xe8\xb5\x13쪇\xfd\x02l\t\x95\xade\x88\xa6K|w\xfa\x00S\xfeU\xb3g\xc9\x12+h\xeb\xa5\x14\xe5b\xe9Ip\x17\x03\xdde'\x95X\xe4\xa7\xc8ʅ!k\xe7\xd4ҥ\xe4\r\x9b̹\xb9\xd2z\xba\xfb\x80\xeeG\xe1\x1e\xb5_\xb5$\xde!\x05\xb0\xf5\xf00\xc9^\x95\xb7\xfat%\xf2\xaab\xa9\x97}d\xf3\xfb\x8d\xc77\x12\xa7\x8c\x94\xae!:yځ\x9cS6\xb7\x9e\xc1\xc4\xccz;Y\xff\x03'@=P\xc9\x19_\x1cZ\xfc\xf7\xee\xb1\x0e\xd5\xc4A\xe8PN:\x16Q\xa9+Aʉ\x9f\xe4\x8e\xdc\xfeJa\x19\xa0\x9et\x9e\xa1\xad\x1f\x91\x90\xd3\x06\x92ݗ\xdc/\xb5Zo\xcbڹ\xc4D\x8b\xdb{\xc6ӗ\xfe\x02P\x91\x95\x92f\ue7c9\xe0\x96-\xa8\x97\xe4\x87\x1f\xbf\xf0\vz\x0fRU?\xfe\xbf\x00\x00\x00\xff\xff\a{y+\x88\xf9\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ars\xf4\\d\xb50\xe1\x8f?Ϛ5*\x92\x04\vW\xa5y\xb5߶??\xe7\x1f\xe1\xe9z\xfe\x99h\xe5\xddn{\x05\xff\xfd?gP\x99\xe3\x87\xf0>=U\xfe_\x00\x00\x00\xff\xff\x11z\x10\b\t`\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V͎\xe44\x10\xbe\xe7)J\xcba/$\xbd+\x0e\xa0\xdc\xd0\xc0a\x05\x8cFӫ\xb9 \x0en\xa7\xd2mƱCU\xb9\x87\x06\xf1\xee\xc8vҝN\xd2̀\x84o\xb1\xeb端~REY\x96\x85\xea\xcd\x13\x12\x1b\xefjP\xbd\xc1\xdf\x05]\xfc\xe2\xea\xf9\x1b\xae\x8c\xdf\x1c?\x16\xcf\xc655\xdc\x05\x16\xdf=\"\xfb@\x1a\xbf\xc3\xd68#ƻ\xa2CQ\x8d\x12U\x17\x00\xca9/*^s\xfc\x04\xd0\xde\tyk\x91\xca=\xba\xea9\xecp\x17\x8cm\x90\x92\xf1\xd1\xf5\xf1C\xf5u\xf5\xa1\x00ЄI\xfd\xb3\xe9\x90Eu}\r.X[\x008\xd5a\r\x8c\x14\x95DI`\xc2\xdf\x02\xb2puD\x8b\xe4+\xe3\v\xeeQG\xc7{\xf2\xa1\xaf\xe1\xf2\x90\xf5\aP9\xa0m2\xb5M\xa6\x1e\xb3\xa9\xf4j\r\xcb\x0f\xb7$~4\x83To\x03)\xbb\x0e(\t\xf0\xc1\x93\xdc_\x9c\x96\xc0L\xf9Ÿ}\xb0\x8aV\x95\v\x00־\xc7\x1a\x92n\xaf46\x05\xc0\xc0T\xb2U\x0e\\\x1c?fs\xfa\x80\x9d\xcaN\x00|\x8f\xeeۇOO_m\xaf\xae\x01\x1adM\xa6\x97\xc4\xf7Jd`\x18\x14\f(@<(\xad\x91\x19t B'\x90Q\x82q\xad\xa7.\xe5\xe8l\x1a@\xed|\x10\x90\x03\xc2S\xa2|\x88\xac:\x8b\xf4\xe4{$1#\x1b\x83ڥ\xfa&\xb73\xac\xefc8Y\n\x9aXv\xc8\xc9\xd3@\t6\x03\x03\xe0[\x90\x83a \xec\t\x19\x9d\xccQ&~ZP\x0e\xfc\xeeW\xd4R\r9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } From d581ab6571c65684d39e44bba768e73a5724b0f8 Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Wed, 15 Jun 2022 14:16:00 +0000 Subject: [PATCH 204/366] fix crds.go Signed-off-by: GitHub --- config/crd/v1/crds/crds.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 7b9cbb1d9d..4b1197a47f 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,15 +30,15 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\x80\xef\xfa\x15\x98}\x0f{y%\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdf;$%\u007f\xc8v6=\x947\x81 \x00>\xf8\x10\xab\xba\xae+\x15\xec\x13\x12[\xefZP\xc1\xe2\x1f\x82.}q\xf3\xfc\x037\xd6/\xb6\x1f\xabg\xebL\v7\x91\xc5\x0f\xf7\xc8>\x92\xc6ϸ\xb6Ί\xf5\xae\x1aP\x94Q\xa2\xda\n@9\xe7E%1\xa7O\x00흐\xef{\xa4z\x83\xaey\x8e+\\E\xdb\x1b\xa4l|r\xbd\xfd\xd0|\xdf|\xa8\x004a>\xfeh\adQCh\xc1ž\xaf\x00\x9c\x1a\xb0\x05\x83=\n\xae\x94~\x8e\x81\xf0\xf7\x88,\xdcl\xb1G\xf2\x8d\xf5\x15\a\xd4\xc9\xf1\x86|\f-\x1c6\xca\xf91\xa8r\xa1\xcf\xd9ԧl꾘ʻ\xbde\xf9\xe9\x9a\xc6\xcfv\xd4\n}$\xd5_\x0e(+\xb0u\x9b\xd8+\xba\xa8R\x01\xb0\xf6\x01[\xb8Ka\x05\xa5\xd1T\x00#\x8f\x1cf\rʘLX\xf5K\xb2N\x90n|\x1f\x87\x89l\r\x06Y\x93\r\x92\t>v\x98\xaf\b~\r\xd2!\x14w \x1eV8F`\xf29\x80\xaf\xec\xddRI\xd7B\x93x5E5\x052*\x14ԟ\xe6b٥\x80YȺ͵\x10X\x94D\x9e\x82\xc8~\xadw@G|O\x03\xc8\xfaM\xe8\x14\x9fz\u007f\xc8\x1b\xd7<\x17\x9d\xed\xc7BZw8\xa8v\xd4\xf5\x01ݏ\xcbۧ\xef\x1eN\xc4p\x1a\xeb\x85ԂePS\xa4\t\\\xa1\x06\xde!x\x82\xc1\xd3D\x95\x9b\xbd\xd1@> \x89\x9dJ\xab\xac\xa3\xae:\x92\xceBx\x9f\xa2,Z`R;!ghc\x11\xa0\x19/V`Z\x06\xc2@\xc8\xe8J\x83\x9d\x18\x86\xa4\xa4\x1c\xf8\xd5W\xd4\xd2\xc0\x03R2\x03\xdc\xf9؛ԅ[$\x01B\xed7\xce\xfe\xb9\xb7\xcd\xe9\x9e\xc9i\xaf䐟i\xe5\xa2s\xaa\x87\xad\xea#\xfe\x1f\x9430\xa8\x1d\x10&/\x10ݑ\xbd\xac\xc2\r\xfc\x920Y\xb7\xf6-t\"\x81\xdb\xc5bce\x9a&\xda\x0fCtVv\x8b<\x18\xec*\x8a'^\x18\xdcb\xbf`\xbb\xa9\x15\xe9\xce\nj\x89\x84\v\x15l\x9dCwy\xa24\x83\xf9\x1f\x8d\xf3\x87ߟ\xc4zV e\xe5F\u007f%\x03\xa9\xcdK\xda\xcb\xd1r\x8b\x03\xe8$Jt\xee\xbf<<\xc2\xe4:'cN?s?\x1c\xe4C\n\x120\xeb\xd6H%\x89k\xf2C\xb6\x89\xce\x04o\x9d\xe4\x0f\xdd[ts\xfc\x1cW\x83\x15\x9eJ2媁\x9b.\xbd{[\xffP\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\xbd\xb3^5\x11\xffJHL\xf5\x0e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05\x87\x812w\x00T6\xf3~HsS\xd2\xe4\x11k\x88\u007f\x99\x1b\xfdh\x86\x88`ST\xf6\x1cD\x1e$\xe3\xdadU<\x1b^\x00\x90\xf6\x01W\xf0I`\x04\xa5\xb1Y\x00\f{ϰ\xaaaw\xbbw%\x95\xee\xb0W\x05/\x80\x0f\xe8~\xfa|}\xfb\xfd\xfa\xa4\x1b\xa0A\xd2\xd1\x04\xce\fN0\x83!P0 \x00\xf6{P\xa0\x1c\xa8\xc8f\xab4\xc36\xfa\x1e6Jߥ\xb0\xcf\n\xe07\u007f\xa2f \xf6Q\xb5\xf8\x06(\xe9\x0e\x94\xe4+\xa1`}\v[c\xb1\xdeO\n\xd1\a\x8clF\x96K;\x12\xd7Q\xef\x04\xf8k\xd9[\x89\x82FT\x85\x04\xdc\xe1\xc8\x0f6\x03\x1d\xe0\xb7\xc0\x9d!\x88\x18\"\x12\xba\xa2\xb3\x93\xc4 A\xca\r;\xa8a\x8dQ\xd2\x00u>\xd9Fĸ\xc3\xc8\x10Q\xfb֙\xbf\xf7\xb9I\x18\x92E\xad\xe2Q\x0e\x87f\x1cct\xca\xc2Nلo@\xb9\x06z\xf5\x00\x113O\xc9\x1d\xe5\xcb!Tï>\"\x18\xb7\xf5+\xe8\x98\x03\xad\x96\xcb\xd6\xf0XT\xda\xf7}r\x86\x1f\x96\xb9>\xcc&\xb1\x8f\xb4lp\x87vI\xa6\xadTԝaԜ\".U0U\x86\xeera\xd5}\xf3M\x1cʐ^\x9f`\xe5\a\x91\x19q4\xae=\x1aȚ\u007f\xe4\x04D\xf5E0ej\xd9Łh\xe9\x12vn>\xac\xbf\xc0\xb8t>\x8c)\xfbE9\xfb\x89t8\x02!̸-\xc6r\x88Yy\x92\x13]\x13\xbcq\x9c?\xb45\xe8\xa6\xf4S\xda\xf4\x86i\x14\xb3\x9cU\rW\xd9i`\x83\x90B\xa3\x18\x9b\x1a\xae\x1d\\\xa9\x1e\xed\x95\"\xfc\xdf\x0f@\x98\xa6J\x88}\xde\x11\x1c\x9b\xe44\xb8\xb0v40:م\xf3\x9a\x94\xfa:\xa0\x96\xd3\x13\x02e\xa6\xd9\x1a\x9dK\x03\xb6>\x82:T\xfe@`}\x92y\xber38\x15[\xe4i\xef\x04˗\x1c$\xcb\xdfw\xea\xd4h\xbeź\xad\xc5+h\x00R\xdc\xe3\xbb\xfa,\xe3e\f0\xab\xdeY$\xa3\x88\x85\x06\xe1U\xac@L\xea\x18\xd3\xf9\xd2\xd2Х~~\x81\n~Θ?\xfa\xf6\xd1\xf1+\xefX\xe4\xfehЭ\xb7\xa9ǵS\x81:\xffD\xec5c\xff\xbc\xc8\xf1B\xde_R\xe7\x817(V\x8e\x9771\x04\xdc %{q\xb9\xab\xf5\xf5K\xf6q!\xfcQ\xa6.\xd4\xce\xd8\xf2\x1d\xf9\xb4\x10\xe4\x96\x1d\x85 S\xcaŁ o\x8f萑\x0e\x1evo\xb8\x9b\xcd\bp\xdf\x19\xdd\xe5\x89YEb\x8fD^\x9bl6/\x87/\xc5g\"\xce(\xb9\xca\n\x9f\xe9\x16\xf0g\xdd\x17,\xe3\xd2\x02\xd5P\xc6ϲ\x1dV\x9c\xe8\x05Ɠ\xe3G\xaau\x8a\x11\x1d\x0fY\xf2E<\x9d\xf0\\\xe7\x19\xcb\xf5\xb7\x9b\x8fO\xd8\xcf\xfbCd~j*\xe3\n\x9a\x10\xb1\"\xd3\xca\xf3A\xc6Ā\xb21\x9c\x93Q\xda\xe9s攨\xd9\x13ů\xc1\xc4l\xb3O@\xfc\xb0\x0f,.\x89\xae܀\xd3\a[N\x88\x94_\x17ZM\xdf5\xd26\b\rZdl`\xf3P\xec\xfe\x81\x18\xfbs\xdc[\x1f{\xc5+\x90\x9b\xb1b3##yT\xab\x8d\xc5\x15pL\x97T6\xbb\xf1\xd0)\x9a)Ó=\u007f\x96\x989a\xec\x8b\xf1Qe\xc0ES\xae\xe0\x13\xde\xcf\xf4~\x8e^#\x11\x9e\x97\xd1ŝ\xcc\x16\xc1Y'\xc9\xf3\xa59bix\x15\x1f\xf7\xa4\xcd\xfe96\"\x1eJ\t\xfe\xf9wq\xa8*\xa55\x06\xc6\xe6\xd3\xf4o\xe4ի\x93ߋ\xfc\xa9\xbdkL\xf9\x91\x82\xdf\xffX\x94\x85\xb1\xb9\x1d\xff\x1a\xa4\xf3\xbf\x00\x00\x00\xff\xff\x85hm\xa4\xc2\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs#\xb7\x11\xbe\xf3Wt\xad\x0f\x8a\xab\x96C\xdbI%)\xdev\xa58\xa5\xc4֪č.[{\x00\a\xcd!\xac\x19\x00\x010\xe42.\xff\xf7T\xe31\x9c\aHJ\xaa\xac3\x17\x89x4>4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f\xa4Ê\xdc\x06~\xfdmv\xa49\xac\xa4\x02\x02\xf9\xdd\xf8I\xffM\xb8\xe0Io\xf4\xfeg\xa9d`\xd3v\t\x9f>\xcf\xd2\xd3\xddczz\xa7\xc6\xff\x06\x00\x00\xff\xff\xb6\xe8a\xa8\a!\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y[s\x1b\xb7\x15~\xe7\xaf8\xe3<\xa8\x991\x97\x89\xdbi;|\xb3\xa5\xa6\xa36\x915\xa6\xaa\x17\x8f\x1f\xc0\xc5\xe1.\xa2]`\x8b\x83%\xcdf\xf2\xdf;\a\x17ro\x14%M\x94\xec\x8b-\\\x0e>|8w\xce\xe6\xf3\xf9L4\xea\x1e-)\xa3\x97 \x1a\x85_\x1dj\xfe\x8b\xb2\x87\xbfS\xa6\xccb\xfb\xfd\xecAi\xb9\x84˖\x9c\xa9?!\x99\xd6\xe6x\x85\x1b\xa5\x95SF\xcfjtB\n'\x963\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv\xd6T\x15\xday\x81:{h\u05f8nU%\xd1z\xe1\xe9\xe8\xedw\xd9߲\xeff\x00\xb9E\xbf\xfdN\xd5HN\xd4\xcd\x12t[U3\x00-j\\Bc\xe4\xd6Tm\x8d\x16\xc9\x19\x8b\x94m\xb1Bk2ef\xd4`Χ\x16ִ\xcd\x12\x8e\x13asD\x14nsk佗\xf3)\xc8\xf1S\x95\"\xf7\xef\xc9\xe9\x1f\x159\xbf\xa4\xa9Z+\xaa\t\x1c~\x96\x94.\xdaJ\xd8\xf1\xfc\f\x80r\xd3\xe0\x12n\x18J#r\x943\x80H\x80\x876\a!\xa5\xa7TT\xb7Vi\x87\xf6\x92E$*\xe7 \x91r\xab\x1a\xe7);\xc8\x01\xb3\x01W\"\x1f\xe9\xe9\x16J+]\xf8\xa1\x00\x01\x9c\x815BD\"\xbd0\x80\x9f\xc9\xe8[\xe1\xca%dL\\\xd6\x18\x99\xe9$3\xae\t\x9c\xdf\fFݞ\xefA\xce*]\x9cB\xf6\x1b\x83\xea\xe1\xb95\xf2\x05H\x9eslX\xdb;\xf4\xbe;t\xee\xdc[#\xe3\x06\x88\n\x04\xe4\x84k\t\xa8\xcdK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\xfc\xf2\xac)\x05\xf5q\xac\xfc\xc4\xeb\xe2\xd8\x18[\v\xb7\x04\xa5\xdd_\xffr\x1a[ܔ9\xe3D\xf5a\xef\x90zH\xef\x86\xc3\x01-+v\x81\xf6\x8f\x83\xbbfHWF\xf7y\xfd0\x18\x9d\x02\xdb\x11\x9a\x9c^6rX=\xa9\uf2fe<)\\\x18\b\xd3\xdb\xef\x83\xdb\xc8K\xac\xc52\xae4\r\xea\xf7\xb7\xd7\xf7\u007f^\xf5\x86\x01\x1ak\x1a\xb4N%O\x16\xbe\x8e\a\xef\x8cB\x9f\xd9\v\x16\x18V\x81d\u05cd\x14\x8c\"\x8c\xa1\x8c\x18\x82\xb1(\x02\x8b\x8dEB\x1d\x9cyO0\xf0\"\xa1\xc1\xac\u007f\xc6\xdce\xb0B\xcbb\x80J\xd3V\xdeڷh\x1dX\xccM\xa1\xd5\xff\x0e\xb2\x89m\x8f\x0f\xad\x84\xc3\xe8N\x8f\x9f\xf7wZT\xb0\x15U\x8boAh\t\xb5\u0603E>\x05Zݑ\xe7\x97P\x06?\xb1\x86(\xbd1K(\x9dkh\xb9X\x14ʥȕ\x9b\xban\xb5r\xfb\x85\x0fBj\xdd:ci!q\x8bՂT1\x176/\x95\xc3ܵ\x16\x17\xa2Qs\x0f]\xfb\xe8\x95\xd5\xf2\x1b\x1bc\x1d]\xf4\xb0\x8e\x8c.|>\xae<\xf2\x02\x1cX@\x11\x88\xb85\xdc\xe2Htr\x8f\x9f\xfe\xb1\xba\x83t\xb4\u007f\x8c!\xfb\x9e\xf7\xe3F:>\x01\x13\xa6\xf4\x06mxč5\xb5\x97\x89Z6Fi\xe7\xff\xc8+\x85zH?\xb5\xebZ9~\xf7\xff\xb6H\x8e\xdf*\x83K\x1f\xce\xd9_\xb6\rk\xae\xcc\xe0Zå\xa8\xb1\xba\x14\x84\xaf\xfe\x00\xcc4͙ا=A7\x13\x19.\x0e\xacu&R\xb6p⽆\x19\xc0\xaa\xc1\x9c\x9f\x8f\x19\xe4\xadj\xa3ro\x1b\xec~@\x8c\xd6g=\xd1Ӧ\xcb\xdfZ\xe4\x0fm\xb3rƊ\x02\u007f4A\xe6p\xd1\x00ۇ\xa9=\t\x9c\xeeļ \x1c(\xac\x1c\t\x05\xa8\xd2\xe6]\x89\x16\xfd\x1e\x0e\x8d*g\xf52\xa4\x9c\xb1{\x16\x1c\xa2e6\x92p\xe2!\xfc\x95\x8d\xde,\xfe9\xc5\xfc\xe1\x16 \xf2\x1c\x89|\xbc\xc6\x1a\xb5{{\x88\xd9\x12IY\x94\x9c\xb8`V\v\xad6H.\x8bg\xa0\xa5\xcf\xef\xbeL\xb3\a\xf0\x83\xb1\x80_E\xddT\xf8\x16T`\xfc\xe0\xfe\x92\xce(\nt\x1c$\xc2N\xb9R\r\x83ց\x01֮x흿\xae\x13\x0f\b&^\xb7E\xa8\xd4\x03.\xe1\x8d\xcf\x04\x8f0\u007fa\xc3\xfa\xf5\xcd\t\xa9\u007f\n\x06\xf4\x86\x17\xbd\t\xe0\x0e\xf1\xaek\x91G\x90\xae\x14\x0e\x9cUE\x81\xc7Dt\xf8y\xe7\xcd.\xf1[0\x96\x19Ц#\xc2\v\xe6\xd7\v\xfe\b\xe5\b\xf4\xe7w_N\"\xee\xf3\x05JK\xfc\n\xef@\xe9\xc0Mc\xe4\xb7\x19\xdcy\xed\xd8k'\xbe\xf2Iyi\bO1kt\xb5\xe7;\x97b\x8b@\xa6F\xd8aU\xcdC\xbe!a'\xf6\xccBz8\xd67\x01\x8d\xb0\xeeQmMY\xc6\xddǫ\x8fˀ\x8c\x15\xaa\xf0\xfe\x8e\xa3\xd3Fq\xd6\xc0\xe9B\x88y^\x1bGA3}\xd4\x06\xf5q\x06\xf2R\xe8\x02\xc3}\x116-G\xa1\xec\xe2%v<\x0e\xfd\xe9\x9bH\x01\x86\x8e\xe3\x0f\v\xa2O\xbc\x9c\xcfT\x9fp\xb9n\xad\xf5\xe8\xe5\x1e\xda5Z\x8d\x0e\xfd\xfd\xa4ɉ\xaf\x96c\xe3ha\xb6h\xb7\nw\x8b\x9d\xb1\x0fJ\x17sV\xcdy\xd0\x01Z\xf8\xf2t\xf1\x8d\xff\xe7\xc5w\xf1\x95\xecS/\xd4+\xb0_\xf3V|\x0e-^t\xa9\x94+>=\x8e]\xacb\x023\xdc\xcbf\xb1+U^\xa6\" \xfa\xd8\x13Ƥ8\xe3\x94\xc15\v\xbd\u007fuUfB[ˈ\xf6\xf3\xd8X\x9a\v-\xf9\xff\xa4\xc8\xf1\xf8\x8b\x18lՓ\xcc\xf7?\xd7W\xbf\x8f\x82\xb7\xeaE\xb6z\"\xd1\r:Ҙk\xc9Tn\x14\xda3y٧\xde\xe2\x94WN䅇5\xcfJ\fI\x8b\x86J㮯\xce\xe0X\x1d\x16&\f\xc7\a\x88\xe9`\x925h\xeb<\vO\x10u\x06KlKL\xe4\xd8\x11I\xc89\xfc\b\xe7\xb5\x1eϴ\xb1>\x17!\x97d\x9c@\xf5\x11Χ+\x87\xc1\x9a\xc6\xc8\xc1H_\x13\x06\x93ǧ\x19L\xf4\x1ab]\xbc\xe3\xb2\xcaw[\x9eSX\x85\x0eOd6طK}\x1fNn_\\Z\xe5\x86\x13\xc7~7\xf9\xf1W\xbe\x1c\xef\xf0}\f+\x03:\xa7j\xf4\xf5JhN\xed\x04\xa5C\xa6^\x14:\xf2\xc2V\xefSY\x1cJ\x9f\xd6qֹ\x11\xaaB\t\x87~6\xdcq\x85\xe9\v\xfa\x8b\xa9,&\tj\t\xa5\xaf='@\x8f\xf7\xa5\x1e\x19\x97\xf1s\x161Z\xa1۪\x12\xeb\n\x97\xe0l;\x9e~Āj$\x12\xc59\v\xfa)\xac\n\x15_\xdc\x02bmZw(\xf9\xa2)E*.(j\xc1\xf3\xca\xceR\xd09(\xb7\xbcfJ\xe3\x0eF\xfd\xb8\xca\U00047ead\xc7\xc7\xcc\xe1\x06w\x13\xa3\xa3\x9eew\xf22\xa9\xd0\xc4\xdc\x0f^;\x9eE@<\xe8\x1c\aq\x19\x94\xa6J\xdam\x9c\xa8@\xb7\xf5\x1a-\x13\xe1\x1b\xa5\x89\x91\xe4\x1a\xa6jh\x9f{\x1f\x99j\x92\xa8ѠG.;\xb2c߬;Ү\x0f]\xe1Dj\x8cx\xf0˯\xb3c\xf0\x139\xe7{(o\x86?<\xbe\t\xf5x\xfa\x1d\xd1\xff\x99\x1b\x1d~\xf8\xa3%|\xfe2\x83\xd8n\xbbO?\x0e\xf2\xe0\xff\x03\x00\x00\xff\xff\x8c\x89\xe8@\xae\x1d\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WMo\xdc6\x13\xbe\xef\xaf\x18\xe4=\xe4\xf2J\x9b\xa0\x87\x16\xba\xa5n\v\x04\x8d\rc\x1d\xf8R\xf40\"gw\x19S$K\x0e\xd7\xdd\x16\xfd\xef\x05Iɫ\x95do\x1c\xa0\xbaq8\x9c\x8f\xe7\x99\x19R\xab\xaa\xaaV\xe8\xd4=\xf9\xa0\xaci\x00\x9d\xa2?\x99LZ\x85\xfa\xe1\x87P+\xbb>\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg\r\x89\xf4\xbd\x05\u007f\xff\xb3:\xb5\x19\nA\x8eI\xdeL\xff%\u07bc9\xfb5\xc8KaMyʇ\x06~\xfb}U\x1c\x93\xbc\x1f\x9e\xfbI\xf8o\x00\x00\x00\xff\xffW\xb1\xbaH\x82\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\x03V\xa9\x92%\xa4e\x06\xddMƷ\xca\xd2\xfbG\xbd~Sr\xf6\xcf\x12\x9a50\xab\x88\xb4{\xba\x8b\fk\x9cT\xa1\xb4F\x06\x859:\u007f\xc1\xfd\xf4_rQ#\ay\xc7\xd5\xd7&H\xcb#\x84\xd2X\x97\x80\xebF\x91e\x1ffJ\\;>\xf7xgY\r\xbf\x86]\xe6o\a;\xec\x16\x0f\x13\xf7խ\f\xd8\x1d'\xd2ޝm\xc3袹[{\xc76\xa1\x85.\xa5\x8b\v$\xa5\xc4\xfe\xbfu\xcbB\xea1\xe7P\xd4\x02\xbb[\x01vy\x00L\xf0;\x96\x83\xd24/\x0ePȫ\xed7\xcc\x06\b\x99\xaa\xaa\xb2a3$X7\xd9\xed2\x90i\xdd\xda9\x9d6`[0\xa8_\x18А\x12X\x01'\xeeR?f\xdaڀc\a\xd0;T\xc0\xe5\ns\u007f<\x1c\xbc\xdf7\x17\x92`\x17\xedj\xea\xdb\x14\xe1KE\xa5Tä\xb3rH\xaf\x93\xd8)E\xf1Z\xee!V\x83w\x9d\x9d@M\xf0^\x88\xd9\xde,\xb3o\xfb\x9bƮ\xb4\xc7\x03H \v\xe0\x06ŝ\x1cǩe\xb6\xb5\xabA\xac;\xc1U\xc0\xf6\xcev\xc3-\xa9\xfb\x80U\xd4+/R\x97\a\x02)\x19\x1f鬪\xb0\xaf`\x96\xbb\xe1\xfd\x0e\xa8꺧\xd0B\xc4\xd7\xcdg\x9d\xf6mq\x80KO\xa8틽\x04\x02\\3\t\x9d\xa4\xef\xa6$\xf0\xcb\x01g\x9c\x90bI\xd5!vyc\x9e\xa9Z\xf06\x0ee\xc5)\xdf\xed\x98\x13\xf02\xdf\x06>!\xd7\xf0\xd0\xf1\xeb\xd7H\xf4hQu\x1f\xa5\t\xb9\xe27R,dWW\x88\x89?X\x1d\x142!7TjF\xb3l\xfduw\xf7I\xff\xf5 ܹ\xa9\x1c\xd4\x12\xecc\xb5\xd2ĸ=\u007f\x86R\xe9\f\xbb\xbb\xd6\xc4z\xa2j:\xee\xf6\xd8 \xb4\xa91\xaa\xc0\x1b\xe3\xac\r\x14\xaf\\(=\x81\xf9\\Hm\x95\xb4Ʉ\xb0\xb9\xe3\x9f]\xca\ae\x19\xbalm\xd2\x1ca\xba\x0e\bU\x02\xc9\xd8\xfa|M$\x12+\xb6\xfa\xcd\xe9ڦ\xdc\xd0$)\xcd\xf1geNY\xb7\xde|\xa8\x88\x1e\xd6@\xb9\xdam\xe8\xb5\x13쪇\xfd\x02l\t\x95\xade\x88\xa6K|w\xfa\x00S\xfeU\xb3g\xc9\x12+h\xeb\xa5\x14\xe5b\xe9Ip\x17\x03\xdde'\x95X\xe4\xa7\xc8ʅ!k\xe7\xd4ҥ\xe4\r\x9b̹\xb9\xd2z\xba\xfb\x80\xeeG\xe1\x1e\xb5_\xb5$\xde!\x05\xb0\xf5\xf00\xc9^\x95\xb7\xfat%\xf2\xaab\xa9\x97}d\xf3\xfb\x8d\xc77\x12\xa7\x8c\x94\xae!:yځ\x9cS6\xb7\x9e\xc1\xc4\xccz;Y\xff\x03'@=P\xc9\x19_\x1cZ\xfc\xf7\xee\xb1\x0e\xd5\xc4A\xe8PN:\x16Q\xa9+Aʉ\x9f\xe4\x8e\xdc\xfeJa\x19\xa0\x9et\x9e\xa1\xad\x1f\x91\x90\xd3\x06\x92ݗ\xdc/\xb5Zo\xcbڹ\xc4D\x8b\xdb{\xc6ӗ\xfe\x02P\x91\x95\x92f\ue7c9\xe0\x96-\xa8\x97\xe4\x87\x1f\xbf\xf0\vz\x0fRU?\xfe\xbf\x00\x00\x00\xff\xff\a{y+\x88\xf9\x01\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ars\xf4\\d\xb50\xe1\x8f?Ϛ5*\x92\x04\vW\xa5y\xb5߶??\xe7\x1f\xe1\xe9z\xfe\x99h\xe5\xddn{\x05\xff\xfd?gP\x99\xe3\x87\xf0>=U\xfe_\x00\x00\x00\xff\xff\x11z\x10\b\t`\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V͎\xe44\x10\xbe\xe7)J\xcba/$\xbd+\x0e\xa0\xdc\xd0\xc0a\x05\x8cFӫ\xb9 \x0en\xa7\xd2mƱCU\xb9\x87\x06\xf1\xee\xc8vҝN\xd2̀\x84o\xb1\xeb端~REY\x96\x85\xea\xcd\x13\x12\x1b\xefjP\xbd\xc1\xdf\x05]\xfc\xe2\xea\xf9\x1b\xae\x8c\xdf\x1c?\x16\xcf\xc655\xdc\x05\x16\xdf=\"\xfb@\x1a\xbf\xc3\xd68#ƻ\xa2CQ\x8d\x12U\x17\x00\xca9/*^s\xfc\x04\xd0\xde\tyk\x91\xca=\xba\xea9\xecp\x17\x8cm\x90\x92\xf1\xd1\xf5\xf1C\xf5u\xf5\xa1\x00ЄI\xfd\xb3\xe9\x90Eu}\r.X[\x008\xd5a\r\x8c\x14\x95DI`\xc2\xdf\x02\xb2puD\x8b\xe4+\xe3\v\xeeQG\xc7{\xf2\xa1\xaf\xe1\xf2\x90\xf5\aP9\xa0m2\xb5M\xa6\x1e\xb3\xa9\xf4j\r\xcb\x0f\xb7$~4\x83To\x03)\xbb\x0e(\t\xf0\xc1\x93\xdc_\x9c\x96\xc0L\xf9Ÿ}\xb0\x8aV\x95\v\x00־\xc7\x1a\x92n\xaf46\x05\xc0\xc0T\xb2U\x0e\\\x1c?fs\xfa\x80\x9d\xcaN\x00|\x8f\xeeۇOO_m\xaf\xae\x01\x1adM\xa6\x97\xc4\xf7Jd`\x18\x14\f(@<(\xad\x91\x19t B'\x90Q\x82q\xad\xa7.\xe5\xe8l\x1a@\xed|\x10\x90\x03\xc2S\xa2|\x88\xac:\x8b\xf4\xe4{$1#\x1b\x83ڥ\xfa&\xb73\xac\xefc8Y\n\x9aXv\xc8\xc9\xd3@\t6\x03\x03\xe0[\x90\x83a \xec\t\x19\x9d\xccQ&~ZP\x0e\xfc\xeeW\xd4R\r9\xb8S\x1d\xda;\xc5\xf8\xbf' 2\xcde$\xf6m)\x98Nѹpfm\xf20\x8e\xb9\x1b\xf9Z\xe9\xeem\x8f:f0\x92\x18\xb5Mktj\x0fh=\x81ZS\xa9ބ$i\xfcK,\xc3$\xc9hf\xf3%\xf6\xe7\xebh\xd6\xc7Iz9(\xc6\xf9\xe5\f\xd3C\x94\x99\xfb\xb7\xa6E}\xd2\x16\xb3\x89N\xd64ׯύڀ\xfc\xbf\xd9\x1b\xb7\bw\x1eY\x96J\xff\xb0騞\f\xe8\xc1\x10Pp.\xf6\xedbB& \xf3I\xbe\x901\x82\xdd\n\x9aU<\x9f\\\xeb\xd3&\xa0\xa2c%\xb9\x9fpH\xf6\xe0'\xe3Z1x;\xd7\xf9,\x87כ\b\xcd'\xfdI\xff\x9br\x1c7\x86p\xd5w\x99P\xad>D\x8fk\x8c\xaf\xf7׀2X\xabv\x16k\x10\nK\xed\xac\xab\x88\xd4i^5c\xa9]\xf6\xa9W\nh\xa1\x10\xfb\xe4\xe5\x80\xeeV7\xc0\x8b\x9aO\xf9+ϰ;\xddR\xbd;/\x87˖ʥ[C\x9cݥ\x98\x15\xce\xdeD\xcaj\xf6rI\xafn\x1e\vB\xb6S\xd9qf\\\xb5Ƹ\x88,c\xb8\ta5ً\xcbd\xbe\x99\x84\xc7\xe2I\xed\xa7\x01s\u061d\xff\xf4c \xc3H\x86?\xff*.\xd39.s\xbd`s?߂߽\xbbZgӧ\xf6\xae1y\x89\x87\x9f\u007f)\xb2cl\x9e\xc6\x1d4^\xfe\x1d\x00\x00\xff\xff;,8\xce>\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), } From 79be75e18314d1cb68cb094f931aee80a700e3fe Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 15 Jun 2022 12:57:37 -0400 Subject: [PATCH 205/366] When spec.RestoreStatus is empty, don't restore status Signed-off-by: Scott Seago --- changelogs/unreleased/5008-sseago | 1 + pkg/restore/restore.go | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/5008-sseago diff --git a/changelogs/unreleased/5008-sseago b/changelogs/unreleased/5008-sseago new file mode 100644 index 0000000000..69e4039326 --- /dev/null +++ b/changelogs/unreleased/5008-sseago @@ -0,0 +1 @@ +When spec.RestoreStatus is empty, don't restore status diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index e4834ecded..691027c7c6 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -207,11 +207,8 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( ) // Get resource status includes-excludes. Defaults to excluding all resources - restoreStatusIncludesExcludes := collections.GetResourceIncludesExcludes( - kr.discoveryHelper, - []string{}, - []string{"*"}, - ) + var restoreStatusIncludesExcludes *collections.IncludesExcludes + if req.Restore.Spec.RestoreStatus != nil { restoreStatusIncludesExcludes = collections.GetResourceIncludesExcludes( kr.discoveryHelper, @@ -1362,13 +1359,14 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso return warnings, errs } - shouldRestoreStatus := ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) + shouldRestoreStatus := ctx.resourceStatusIncludesExcludes != nil && ctx.resourceStatusIncludesExcludes.ShouldInclude(groupResource.String()) if shouldRestoreStatus && statusFieldErr != nil { err := fmt.Errorf("could not get status to be restored %s: %v", kube.NamespaceAndName(obj), statusFieldErr) ctx.log.Errorf(err.Error()) errs.Add(namespace, err) return warnings, errs } + ctx.log.Debugf("status field for %s: exists: %v, should restore: %v", groupResource, statusFieldExists, shouldRestoreStatus) // if it should restore status, run a UpdateStatus if statusFieldExists && shouldRestoreStatus { if err := unstructured.SetNestedField(obj.Object, objStatus, "status"); err != nil { @@ -1379,6 +1377,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso obj.SetResourceVersion(createdObj.GetResourceVersion()) updated, err := resourceClient.UpdateStatus(obj, metav1.UpdateOptions{}) if err != nil { + ctx.log.Infof("status field update failed %s: %v", kube.NamespaceAndName(obj), err) warnings.Add(namespace, err) } else { createdObj = updated From aea127652c629c69df3d18959215b71a905f775a Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 17 Jun 2022 09:22:25 +0800 Subject: [PATCH 206/366] Post Review Session Update Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 59d64b0b8a..549a2cd2ba 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -41,6 +41,7 @@ On the other hand, based on a previous analysis and testing, we found that Kopia - Use Kopia repository to implement the Unified Repository - Use Kopia uploader as the file system data mover for Pod Volume Backup - Have Kopia uploader calling the Unified Repository Interface and save/retrieve data to/from the Unified Repository +- Make Kopia uploader generic enough to move any file system data so that other data movement cases could use it - Use the existing logic or add new logic to manage the unified repository and Kopia uploader - Preserve the legacy Restic path, this is for the consideration of backward compatibility @@ -48,14 +49,15 @@ On the other hand, based on a previous analysis and testing, we found that Kopia - The Unified Repository supports all kinds of data movers to save logic objects into it. How these logic objects are organized for a specific data mover (for example, how a volume’s block data is organized and represented by a unified repository object) should be included in the related data mover design. - At present, Velero saves Kubernetes resources, backup metedata, debug logs separately. Eventually, we want to save them in the Unified Repository. How to organize these data into the Unified Repository should be included in a separate design. -- Kopia uploader could be used as a generic file system data mover. How it is integrated in other cases, for example, CSI file system mode BR, should be included in the related data mover design. +- For PodVolume BR, this design focuses on the data path only, other parts beyond the data read/write and data persistency are irrelevant and kept unchanged. +- Kopia uploader is made generic enough to move any file system data. How it is integrated in other cases, is irrelevant to this design. Take CSI snapshot backup for example, how the snapshot is taken and exposed to Kopia uploader should be included in the related data mover design. - The adanced modes of the Unified Repository, for example, backup repository/storage plugin, backup repository extension, etc. are not included in this design. We will have separate designs to cover them whenever necessary. ## Architecture of Unified Repository Below shows the primary modules and their responsibilities: -- Kopia uploader is used as a generic file system data mover, so it could move all file system data either from the production PV (as Velero’s Pod Volume Backup does), or from any kind of snapshot (i.e., CSI snapshot). +- Kopia uploader, as been well isolated, could move all file system data either from the production PV (as Velero’s PodVolume BR does), or from any kind of snapshot (i.e., CSI snapshot). - Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. - Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. - A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. @@ -68,7 +70,7 @@ The Unified Repository takes two kinds of data: - Unified Repository Manifest: This could include all other data to maintain the object data, for example, snapshot information, etc. For Unified Repository Object/Manifest, a brief guidance to data movers are as below: -- Data movers treat the simple unit of data they recognize as an Object. For example, a file system data mover treats a file or a directory as an Object; a block data mover treats a volume as an Object +- Data movers treat the simple unit of data they recognize as an Object. For example, file system data movers treat a file or a directory as an Object; block data movers treat a volume as an Object. However, it is unnecessary that every data mover has a unique data format in the Unified Repository, to the opposite, it is recommended that data movers could share the data formats unless there is any reason not to, in this way, the data generated by one data mover could be used by other data movers. - Data movers don't need to care about the differences between full and incremental backups regarding the data organization. Data movers always have full views of their objects, if an object is partially written, they use the object writer's Seek function to skip the unchanged parts - Unified Repository may divide the data movers' logical Object into sub-objects or slices, or append internal metadata, but they are transparent to data movers - Every Object has an unified identifier, in order to retrieve the Object later, data movers need to save the identifiers into the snapshot information. The snapshot information is saved as a Manifest. @@ -256,7 +258,7 @@ The Delete Snapshot workflow follows the similar manner with BR workflow, that i Backup Repository/Backup Storage may need to periodically reorganize its data so that it could guarantee its QOS during the long-time service. Some Backup Repository/Backup Storage does this in background automatically, so the user doesn’t need to interfere; some others need the caller to explicitly call their maintenance interface periodically. Restic and Kopia both go with the second way, that is, Velero needs to periodically call their maintenance interface. Velero already has an existing workflow to call Restic maintenance (it is called “Prune” in Restic, so Velero uses the same word). The existing workflow is as follows: - The Prune is triggered at the time of the backup -- When a BackupRepsoitory CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo +- When a BackupRepository CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo - In the new design, the Repository Provider implements PruneRepo call, it uses the same way to switch between Restic Repository Provider and Unified Repository Provider, then: - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function @@ -278,7 +280,7 @@ In this way, Velero will be able to get the progress as shown in the diagram bel ### Logs In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. -Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other uploaders/data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. +Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. ![A Debug Log for Uploader](debug-log-uploader.png) ![A Debug Log for Repository](debug-log-repository.png) @@ -301,7 +303,7 @@ Changing path during upgrade is not prohibited, however some mismatches cases wi We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR -This means, we add a new CR type and desperate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. +This means, we add a new CR type and deprecate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. From b9fe1539f076198588e13efbccc71192b998d072 Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 21 Jun 2022 07:02:08 +0000 Subject: [PATCH 207/366] Wait for pod running status before timeout despite errors of accessing API server Signed-off-by: danfengl --- Dockerfile | 4 ++-- hack/build-image/Dockerfile | 2 +- test/e2e/util/k8s/common.go | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8e3b99fd87..8ffe8abff4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=$BUILDPLATFORM golang:1.17.11 as builder-env +FROM --platform=$BUILDPLATFORM golang:1.17 as builder-env ARG GOPROXY ARG PKG @@ -50,7 +50,7 @@ RUN mkdir -p /output/usr/bin && \ go build -o /output/${BIN} \ -ldflags "${LDFLAGS}" ${PKG}/cmd/${BIN} -FROM gcr.io/distroless/base-debian11@sha256:e672eb713e56feb13e349773973b81b1b9284f70b15cf18d1a09ad31a03abe59 +FROM gcr.io/distroless/base-debian11:nonroot LABEL maintainer="Nolan Brubaker " diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 8948080d03..5f35646310 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.17.11 +FROM golang:1.17 ARG GOPROXY diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 43812a74a4..d562c75b35 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -62,7 +62,9 @@ func WaitForPods(ctx context.Context, client TestClient, namespace string, pods for _, podName := range pods { checkPod, err := client.ClientGo.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) if err != nil { - return false, errors.WithMessage(err, fmt.Sprintf("Failed to verify pod %s/%s is %s", namespace, podName, corev1api.PodRunning)) + //Should ignore "etcdserver: request timed out" kind of errors, try to get pod status again before timeout. + fmt.Println(errors.Wrap(err, fmt.Sprintf("Failed to verify pod %s/%s is %s, try again...", namespace, podName, corev1api.PodRunning))) + return false, nil } // If any pod is still waiting we don't need to check any more so return and wait for next poll interval if checkPod.Status.Phase != corev1api.PodRunning { From cb273ae46991a72149efc34c0634a90072b0298b Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 22 Jun 2022 10:38:28 +0800 Subject: [PATCH 208/366] Delete opened issues triage action. Because the column and project specified by this action do not exist anymore, and Velero team doesn't use this action to assign issue and triage anymore, remove this action. Signed-off-by: Xun Jiang --- .github/workflows/opened-issues-triage.yml | 15 --------------- changelogs/unreleased/5041-jxun | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 .github/workflows/opened-issues-triage.yml create mode 100644 changelogs/unreleased/5041-jxun diff --git a/.github/workflows/opened-issues-triage.yml b/.github/workflows/opened-issues-triage.yml deleted file mode 100644 index f81443a3a4..0000000000 --- a/.github/workflows/opened-issues-triage.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Move new issues into Triage - -on: - issues: - types: [opened] - -jobs: - automate-project-columns: - runs-on: ubuntu-latest - steps: - - uses: alex-page/github-project-automation-plus@v0.3.0 - with: - project: "Velero Support Board" - column: "New" - repo-token: ${{ secrets.GH_TOKEN }} diff --git a/changelogs/unreleased/5041-jxun b/changelogs/unreleased/5041-jxun new file mode 100644 index 0000000000..eeb5524f74 --- /dev/null +++ b/changelogs/unreleased/5041-jxun @@ -0,0 +1 @@ +Delete opened issues triage action. \ No newline at end of file From 0470c961bf24f7b20c18c6fc36ec66aab8d79266 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Mon, 27 Jun 2022 10:11:47 +0800 Subject: [PATCH 209/366] Fix typo in doc. 'Mamespace' to 'Namespace' Signed-off-by: Niu Lechuan --- site/content/docs/main/restore-reference.md | 2 +- site/content/docs/v1.9/restore-reference.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/restore-reference.md b/site/content/docs/main/restore-reference.md index 0511f6512f..a91ef960db 100644 --- a/site/content/docs/main/restore-reference.md +++ b/site/content/docs/main/restore-reference.md @@ -73,7 +73,7 @@ The following is an overview of Velero's restore process that starts after you r By default, Velero will restore resources in the following order: * Custom Resource Definitions -* Mamespaces +* Namespaces * StorageClasses * VolumeSnapshotClass * VolumeSnapshotContents diff --git a/site/content/docs/v1.9/restore-reference.md b/site/content/docs/v1.9/restore-reference.md index 0511f6512f..a91ef960db 100644 --- a/site/content/docs/v1.9/restore-reference.md +++ b/site/content/docs/v1.9/restore-reference.md @@ -73,7 +73,7 @@ The following is an overview of Velero's restore process that starts after you r By default, Velero will restore resources in the following order: * Custom Resource Definitions -* Mamespaces +* Namespaces * StorageClasses * VolumeSnapshotClass * VolumeSnapshotContents From b62a12263252220feee55c74711c44a5b697a085 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Jun 2022 12:39:49 +0800 Subject: [PATCH 210/366] Post Review Update 02 Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 105 ++++++++++-------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 549a2cd2ba..3fcd124615 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -58,12 +58,14 @@ On the other hand, based on a previous analysis and testing, we found that Kopia Below shows the primary modules and their responsibilities: - Kopia uploader, as been well isolated, could move all file system data either from the production PV (as Velero’s PodVolume BR does), or from any kind of snapshot (i.e., CSI snapshot). -- Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. +- Unified Repository Interface, data movers call the Unified Repository Interface to write/read data to/from the Unified Repository. - Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. - A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. - At present, there is only one kind of backup repository -- Kopia Repository. If a new backup repository/storage is required, we need to create a new Library as an adapter to the Unified Repository Interface - At present, the Kopia Repository works as a single piece in the same process of the caller, in future, we may run its CABS into a dedicated process or node. -- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. +- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. + +Neither Kopia uploader nor Kopia Repository is invoked through CLI, instead, they are invoked through code interfaces, because we need to do lots of customizations. The Unified Repository takes two kinds of data: - Unified Repository Object: This is the user's logical data, for example, files/directories, blocks of a volume, data of a database, etc. @@ -89,7 +91,7 @@ Velero by default uses the Unified Repository for all kinds of data movement, it # Detailed Design ## The Unified Repository Interface -Below are the definitions of the Unified Repository Interface +Below are the definitions of the Unified Repository Interface. All the functions are synchronization functions. ``` ///BackupRepoService is used to initialize, open or maintain a backup repository type BackupRepoService interface { @@ -97,47 +99,47 @@ type BackupRepoService interface { ///repoOption: option to the backup repository and the underlying backup storage ///createNew: indicates whether to create a new or connect to an existing backup repository ///result: the backup repository specific output that could be used to open the backup repository later - Init(repoOption RepoOptions, createNew bool) (result map[string]string, err error) + Init(ctx context.Context, repoOption RepoOptions, createNew bool) (result map[string]string, err error) ///Open an backup repository that has been created/connected ///config: options to open the backup repository and the underlying storage - Open(config map[string]string) (BackupRepo, error) + Open(ctx context.Context, config map[string]string) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance ///config: options to open the backup repository and the underlying storage - Maintain(config map[string]string) error + Maintain(ctx context.Context, config map[string]string) error } ///BackupRepo provides the access to the backup repository type BackupRepo interface { ///Open an existing object for read ///id: the object's unified identifier - OpenObject(id ID) (ObjectReader, error) + OpenObject(ctx context.Context, id ID) (ObjectReader, error) ///Get a manifest data - GetManifest(id ID, mani *RepoManifest) error + GetManifest(ctx context.Context, id ID, mani *RepoManifest) error ///Get one or more manifest data that match the given labels - FindManifests(filter ManifestFilter) ([]*ManifestEntryMetadata, error) + FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) ///Create a new object and return the object's writer interface ///return: A unified identifier of the object on success - NewObjectWriter(opt ObjectWriteOptions) ObjectWriter + NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter ///Save a manifest object - PutManifest(mani RepoManifest) (ID, error) + PutManifest(ctx context.Context, mani RepoManifest) (ID, error) ///Delete a manifest object - DeleteManifest(id ID) error + DeleteManifest(ctx context.Context, id ID) error ///Flush all the backup repository data - Flush() error + Flush(ctx context.Context) error ///Get the local time of the backup repository. It may be different from the time of the caller Time() time.Time ///Close the backup repository - Close() error + Close(ctx context.Context) error } type ObjectReader interface { @@ -199,7 +201,7 @@ const ( ///ManifestEntryMetadata is the metadata describing one manifest data type ManifestEntryMetadata struct { ID ID ///The ID of the manifest data - Length int ///The data size of the manifest data + Length int32 ///The data size of the manifest data Labels map[string]string ///Labels saved together with the manifest data ModTime time.Time ///Modified time of the manifest data } @@ -220,7 +222,7 @@ type ManifestFilter struct { We preserve the bone of the existing BR workflow, that is: -- Still use the Velero Server pod and PodVolumeBackup daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules +- Still use the Velero Server pod and VeleroNodeAgent daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules - Still use the Backup/Restore CR and BackupRepository CR (originally called ResticRepository CR) to drive the BR workflow The modules in gray color in below diagram are the existing modules and with no significant changes. @@ -229,7 +231,8 @@ In the new design, we will have separate and independent modules/logics for back - Repository Provider provides functionalities to manage the backup repository. For example, initialize a repository, connect to a repository, manage the snapshots in the repository, maintain a repository, etc. - Uploader Provider provides functionalities to run a backup or restore. -The Repository Provider and Uploader Provider use an option called “Legacy” to choose the path --- Restic Repository vs. Unified Repository or Restic Uploader vs. Kopia Uploader. Specifically, if Legacy = true, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; if Legacy = true, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia to do the BR. +The Repository Provider and Uploader Provider use options to choose the path --- legacy path vs. new path (Kopia uploader + Unified Repository). Specifically, for legacy path, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; for legacy path, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia uploader to do the BR. + In order to manage Restic Repository, the Repository Provider calls Restic Repository Provider, the latter invokes the existing Restic CLIs. In order to manage Unified Repository, the Repository Provider calls Unified Repository Provider, the latter calls the Unified Repository module through the udmrepo.BackupRepoService interface. It doesn’t know how the Unified Repository is implemented necessarily. In order to use Restic to do BR, the Uploader Provider calls Restic Uploader Provider, the latter invokes the existing Restic CLIs. @@ -243,6 +246,7 @@ In order to use Kopia to do BR, the Uploader Provider calls Kopia Uploader Provi - Finally, the read/write calls flow to Unified Repository module The Unified Repository provides all-in-one functionalities of a Backup Repository and exposes the Unified Repository Interface. Inside, Kopia Library is an adapter for Kopia Repository to translate the Unified Repository Interface calls to Kopia Repository interface calls. +Both Kopia Shim and Kopia Library rely on Kopia Repository interface, so we need to have some Kopia version control. We may need to change Kopia Shim and Kopia Library when upgrading Kopia to a new version and the Kopia Repository interface has some changes in the new version. ![A BR Workflow](br-workflow.png) The modules in blue color in below diagram represent the newly added modules/logics or reorganized logics. The modules in yellow color in below diagram represent the called Kopia modules without changes. @@ -281,25 +285,31 @@ In this way, Velero will be able to get the progress as shown in the diagram bel In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. -Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. +Kopia’s debug logs will be written to the same log file as Velero server or VeleroNodeAgent daemonset, so Velero doesn’t need to upload/download these debug logs separately. ![A Debug Log for Uploader](debug-log-uploader.png) ![A Debug Log for Repository](debug-log-repository.png) ## Path Switch & Coexist -As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. -Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. -Specifically, we will have the “Legacy” option/mode in two places: -- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. -- Add a "legacy-mode" value in the BackupRepository CRs and PodVolumeBackup CRs. The value could be added as a tag in the CRs' spec. If the tag is missing, it by default means "legacy-mode=true", so the CRs were created to manage the Restic repository/backup using Restic. -The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. - -### Upgrade -Changing path during upgrade is not prohibited, however some mismatches cases will happen, below shows how the mismatches are handled: -- If a BackupRepository CR already exists, but users install Velero again with the “Legacy” value changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized -- If PodVolumeBackup CRs already exist, but users install Velero again with the “Legacy” value changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved -- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. - -## Velero CR Changes +As mentioned above, we will have two paths. We don’t pursue a dynamic switch as there is no user requirement. +Instead, we assume that the path is decided at the time of installation of Velero and never changed unless Velero is reinstalled. This means, if users want to switch the path, the best practice is to fresh install Velero and don't refer to any old backup data. +On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. +Specifically, we will have the option/mode values for path selection in two places: +- Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). +- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. The values could be added as tags in the CRs' spec. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the tags are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. + +The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. +In spite of the above pricipal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. + +### CR Handling Under Mismatched Path +The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: +- If a BackupRepository CR already exists, but users install Velero again with the path changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized +- If PodVolumeBackup CRs already exist, but users install Velero again with the path changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved +- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. +### Backup List, Sync and Describe +### Backup Deletion Under Mismatched Path +### Restore Under Mismatched Path + +## Velero CR Name Changes We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR @@ -308,15 +318,20 @@ As a side effect, when upgrading from an old release, even though the path is no Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation - We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has below two values: - **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: + - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced + - It indicates the file system uploader to be used by PodVolume BR + - It implies the backup repository type manner, Restic if pod-volume-backup-uploader=restic, Unified Repository in all other cases + + The flag has below two values: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` spec: containers: - args: - server - --features= - - --legacy + - --uploader-type=restic command: - /velero ``` @@ -327,7 +342,7 @@ Therefore, users are recommended to uninstall Velero and delete all the resource - restic - server - --features= - - --legacy + - --uploader-type=restic command: - /velero ``` @@ -335,7 +350,7 @@ The BackupRepository CRs and PodVolumeBackup CRs created in this case are as bel ``` spec: tags: - legacy-mode: true + repository-type: restic ``` ``` spec: @@ -347,17 +362,16 @@ spec: pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada volume: volume1 - legacy-mode: true + uploader-type: restic ``` - **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` spec: containers: - args: - server - --features= - - debug - - --legacy=false + - --uploader-type=kopia command: - /velero ``` @@ -368,16 +382,15 @@ spec: - restic - server - --features= - - debug - - --legacy=false + - --uploader-type=kopia command: - /velero ``` -The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: tags: - legacy-mode: false + repository-type: kopia ``` ``` spec: @@ -389,7 +402,7 @@ spec: pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada volume: volume1 - legacy-mode: false + uploader-type: kopia ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: From 7b320e71c9a08b0759937852a201f7dd806e718a Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 27 Jun 2022 14:44:05 +0800 Subject: [PATCH 211/366] Modify Github actions. 1. remove go.sum file from code spell check action. 2. change go version to 1.17 in CRD verify action, and add k8s 1.23 and 1.24 in verification list. Signed-off-by: Xun Jiang --- .github/workflows/crds-verify-kind.yaml | 6 ++++-- .github/workflows/pr-codespell.yml | 2 +- changelogs/unreleased/5052-jxun | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/5052-jxun diff --git a/.github/workflows/crds-verify-kind.yaml b/.github/workflows/crds-verify-kind.yaml index 45b6d71a97..398039f380 100644 --- a/.github/workflows/crds-verify-kind.yaml +++ b/.github/workflows/crds-verify-kind.yaml @@ -14,7 +14,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 id: go # Look for a CLI that's made for this PR - name: Fetch built CLI @@ -64,6 +64,8 @@ jobs: - 1.20.2 - 1.21.1 - 1.22.0 + - 1.23.6 + - 1.24.2 # All steps run in parallel unless otherwise specified. # See https://docs.github.com/en/actions/learn-github-actions/managing-complex-workflows#creating-dependent-jobs steps: @@ -81,7 +83,7 @@ jobs: velero-${{ github.event.pull_request.number }}- - uses: engineerd/setup-kind@v0.5.0 with: - version: "v0.11.1" + version: "v0.14.0" image: "kindest/node:v${{ matrix.k8s }}" - name: Install CRDs run: | diff --git a/.github/workflows/pr-codespell.yml b/.github/workflows/pr-codespell.yml index 169535a07c..22218f1f88 100644 --- a/.github/workflows/pr-codespell.yml +++ b/.github/workflows/pr-codespell.yml @@ -14,7 +14,7 @@ jobs: uses: codespell-project/actions-codespell@master with: # ignore the config/.../crd.go file as it's generated binary data that is edited elswhere. - skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go + skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./go.sum ignore_words_list: iam,aks,ist,bridget,ue check_filenames: true check_hidden: true diff --git a/changelogs/unreleased/5052-jxun b/changelogs/unreleased/5052-jxun new file mode 100644 index 0000000000..8df837959b --- /dev/null +++ b/changelogs/unreleased/5052-jxun @@ -0,0 +1 @@ +Modify Github actions. \ No newline at end of file From 34e6234ae0ca92a8680fb4c08d2a0ca7a579e40b Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Jun 2022 20:14:41 +0800 Subject: [PATCH 212/366] add more details for repository CR rename Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 3fcd124615..df4137dfb9 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -193,9 +193,9 @@ const ( OBJECT_DATA_ACCESS_MODE_FILE int = 1 OBJECT_DATA_ACCESS_MODE_BLOCK int = 2 - OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 - OBJECT_DATA_BACKUP_MODE_FULL int = 1 - OBJECT_DATA_BACKUP_MODE_INC int = 2 + OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 + OBJECT_DATA_BACKUP_MODE_FULL int = 1 + OBJECT_DATA_BACKUP_MODE_INC int = 2 ) ///ManifestEntryMetadata is the metadata describing one manifest data @@ -295,10 +295,10 @@ Instead, we assume that the path is decided at the time of installation of Veler On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. Specifically, we will have the option/mode values for path selection in two places: - Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. The values could be added as tags in the CRs' spec. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the tags are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. +- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. -In spite of the above pricipal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. +In spite of the above principal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. ### CR Handling Under Mismatched Path The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: @@ -314,8 +314,15 @@ We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR This means, we add a new CR type and deprecate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. -As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. -Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. +As a side effect, when upgrading from an old release, even though the path is not changed, the BackupRepository gets created all the time, because Velero will not refer to the old CR's status. This seems to cause the repository to initialize more than once, however, it won't happen. In the BackupRepository controller, before initializing a repository, it always tries to connect to the repository first, if it is connectable, it won't do the initialization. +When backing up with the new release, Velero always creates BackupRepository CRs instead of ResticRepository CRs. +When restoring from an old backup, Velero always creates BackupRepository CRs instead of ResticRepository CRs. +For a upgrade case, if there are already backups or restores running during the upgrade, the backups/restores could fall into below results: +- The backups/restores have started but the ResticRepository CRs have not been created. In this case, the BackupRepository CRs will be created instead, the backups/restores will finish successfully. +- The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. +- The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. + +As shown above, there are complexities for the upgrade case, so users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: @@ -349,20 +356,18 @@ Therefore, users are recommended to uninstall Velero and delete all the resource The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: ``` spec: - tags: - repository-type: restic + backupStorageLocation: default + ... + repository-type: restic + volumeNamespace: nginx-example ``` ``` spec: tags: backup: bakup-testns-36 - backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c - ns: testns-36 - pod: deployment-2636-68b9697c56-6hpz5 - pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 - pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + ... volume: volume1 - uploader-type: restic + uploader-type: restic ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` @@ -389,20 +394,18 @@ spec: The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: - tags: - repository-type: kopia + backupStorageLocation: default + ... + repository-type: kopia + volumeNamespace: nginx-example ``` ``` spec: tags: backup: bakup-testns-36 - backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c - ns: testns-36 - pod: deployment-2636-68b9697c56-6hpz5 - pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 - pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + ... volume: volume1 - uploader-type: kopia + uploader-type: kopia ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: From a08463adbaf19fbea57d8195bc31cb07b1f22dc3 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 13:36:09 +0800 Subject: [PATCH 213/366] dynamic switch Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 88 +++++++------------ 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index df4137dfb9..fc34000763 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -267,7 +267,7 @@ Velero already has an existing workflow to call Restic maintenance (it is called - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function -Kopia supports two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), in this way, the maintenance will finish very fast and make less impact. We will also take this quick maintenance into Velero. +Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On ther other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. We will add a new Due Time to Velero, finally, we have two Prune Due Time: - Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance - Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance @@ -290,21 +290,19 @@ Kopia’s debug logs will be written to the same log file as Velero server or Ve ![A Debug Log for Repository](debug-log-repository.png) ## Path Switch & Coexist -As mentioned above, we will have two paths. We don’t pursue a dynamic switch as there is no user requirement. -Instead, we assume that the path is decided at the time of installation of Velero and never changed unless Velero is reinstalled. This means, if users want to switch the path, the best practice is to fresh install Velero and don't refer to any old backup data. -On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. -Specifically, we will have the option/mode values for path selection in two places: -- Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. - -The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. -In spite of the above principal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. - -### CR Handling Under Mismatched Path -The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: -- If a BackupRepository CR already exists, but users install Velero again with the path changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized -- If PodVolumeBackup CRs already exist, but users install Velero again with the path changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved -- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. +As mentioned above, There will be two paths. The related controllers need to identify the path during runtime and adjust its working mode. +According to the requirements, path changing is fulfilled at the backup/restore level. In order to let the controllers know the path, we need to add some option values. Specifically, there will be option/mode values for path selection in two places: +- Add the “uploader-type” option as a parameter of the Velero server. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). +- Add a "uploader-type" value in the PodVolume Backup/Restore CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. + +The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: +- The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path +- The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path +- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup CRs it is going to create +- The Restore controller checks the Backup CR, from which it is going to restore, for the path and then create the Restore CR + +As described above, the “uploader-type” parameter of the Velero server is only used to decide the path when creating a new Backup, for other cases, the path selection is driven by the related CRs. Therefore, we only need to add this parameter to the Velero server. + ### Backup List, Sync and Describe ### Backup Deletion Under Mismatched Path ### Restore Under Mismatched Path @@ -322,8 +320,6 @@ For a upgrade case, if there are already backups or restores running during the - The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. - The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. -As shown above, there are complexities for the upgrade case, so users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. - ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced @@ -331,7 +327,7 @@ As shown above, there are complexities for the upgrade case, so users are recomm - It implies the backup repository type manner, Restic if pod-volume-backup-uploader=restic, Unified Repository in all other cases The flag has below two values: - **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment will be created as below: ``` spec: containers: @@ -342,17 +338,6 @@ As shown above, there are complexities for the upgrade case, so users are recomm command: - /velero ``` -``` - spec: - containers: - - args: - - restic - - server - - --features= - - --uploader-type=restic - command: - - /velero -``` The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: ``` spec: @@ -369,7 +354,7 @@ spec: volume: volume1 uploader-type: restic ``` - **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` spec: containers: @@ -380,17 +365,6 @@ spec: command: - /velero ``` -``` - spec: - containers: - - args: - - restic - - server - - --features= - - --uploader-type=kopia - command: - - /velero -``` The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: @@ -419,25 +393,27 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec - --legacy {{- end }} ``` - -``` - command: - - /velero - args: - - restic - - server - {{- with .Values.configuration }} - {{- if .pod-volume-backup-uploader "restic" }} - - --legacy - {{- end }} -``` - CLI Installation: add the "--pod-volume-backup-uploader" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below: ```velero install --pod-volume-backup-uploader=restic``` -```velero install --pod-volume-backup-uploader=kopia``` +```velero install --pod-volume-backup-uploader=kopia``` + +## Upgrade +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to privide an easy for querying it. +Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. + +## CLI +Below Velero CLI or its output needs some changes: +- ```Velero backup describe```: the output should indicate the path +- ```Velero restore describe```: the output should indicate the path +- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path + +At present, we don't have a requirement for slecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. ## User Experience Changes Below user experiences are changed for this design: - Installation CLI change: a new option is added to the installation CLI, see the Installation section for details - CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details -- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "Restic" daemonset, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes +- Velero CLI name and output change, see the CLI section for details +- Velero daemonset name change +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes From 12cdb1908e2252c2dba6f7cf67957a629ac2b2a7 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 19:07:34 +0800 Subject: [PATCH 214/366] storage configuration Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 79 ++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index fc34000763..b11085ccc3 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -267,7 +267,7 @@ Velero already has an existing workflow to call Restic maintenance (it is called - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function -Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On ther other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. +Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On the other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. We will add a new Due Time to Velero, finally, we have two Prune Due Time: - Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance - Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance @@ -320,6 +320,55 @@ For a upgrade case, if there are already backups or restores running during the - The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. - The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. +## Storage Configuration +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will use a structured storage configuration to replace the repoIdentififer string. +The configuration in BackupRepository CRs, PodVolumeBackup CRs and PodVolumeRestore CRs are as below: +``` +spec: + backupStorageLocation: default + maintenanceFrequency: 168h0m0s + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + volumeNamespace: nginx-example +``` +``` +spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + tags: + ... + volume: nginx-log +``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + snapshotID: 1741e5f1 + volume: nginx-log +``` + ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced @@ -342,17 +391,23 @@ The BackupRepository CRs and PodVolumeBackup CRs created in this case are as bel ``` spec: backupStorageLocation: default - ... + maintenanceFrequency: 168h0m0s repository-type: restic volumeNamespace: nginx-example ``` ``` spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 tags: - backup: bakup-testns-36 ... - volume: volume1 uploader-type: restic + volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` @@ -369,17 +424,23 @@ The BackupRepository CRs created in this case are hard set with "kopia" at prese ``` spec: backupStorageLocation: default - ... + maintenanceFrequency: 168h0m0s repository-type: kopia volumeNamespace: nginx-example ``` ``` spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 tags: - backup: bakup-testns-36 ... - volume: volume1 uploader-type: kopia + volume: nginx-log ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: @@ -398,7 +459,7 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec ```velero install --pod-volume-backup-uploader=kopia``` ## Upgrade -For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to privide an easy for querying it. +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. ## CLI @@ -407,7 +468,7 @@ Below Velero CLI or its output needs some changes: - ```Velero restore describe```: the output should indicate the path - ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path -At present, we don't have a requirement for slecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. +At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. ## User Experience Changes Below user experiences are changed for this design: From d63394ff604a5668d736097e6bbb7c16292aaecf Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 22:30:26 +0800 Subject: [PATCH 215/366] minor changes Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index b11085ccc3..ead91d0850 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -298,15 +298,11 @@ According to the requirements, path changing is fulfilled at the backup/restore The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: - The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path - The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path -- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup CRs it is going to create -- The Restore controller checks the Backup CR, from which it is going to restore, for the path and then create the Restore CR +- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup it is going to create and then create the PodVolume Backup CR and BackupRepository CR +- The Restore controller checks the Backup, from which it is going to restore, for the path and then create the PodVolume Restore CR and BackupRepository CR As described above, the “uploader-type” parameter of the Velero server is only used to decide the path when creating a new Backup, for other cases, the path selection is driven by the related CRs. Therefore, we only need to add this parameter to the Velero server. -### Backup List, Sync and Describe -### Backup Deletion Under Mismatched Path -### Restore Under Mismatched Path - ## Velero CR Name Changes We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR @@ -466,7 +462,7 @@ Moreover, if users upgrade from the old release, we need to change the existing Below Velero CLI or its output needs some changes: - ```Velero backup describe```: the output should indicate the path - ```Velero restore describe```: the output should indicate the path -- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path +- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print all the backup repository if Restic repository and Unified Repository exist at the same time At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. From b605bf4f07f45d1d762382cdf44659a7fb8b0a93 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 29 Jun 2022 11:27:14 +0800 Subject: [PATCH 216/366] add CR samples Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index ead91d0850..c6fb9b4521 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -343,7 +343,7 @@ spec: provider: azure param: container: container01 - prefix: /restic/nginx-example + prefix: /restic/nginx-example tags: ... volume: nginx-log @@ -356,12 +356,12 @@ spec: name: nginx-stateful-0 namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 storageConfig: provider: azure param: container: container01 prefix: /restic/nginx-example - snapshotID: 1741e5f1 volume: nginx-log ``` @@ -383,7 +383,7 @@ spec: command: - /velero ``` -The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +The BackupRepository CRs and PodVolume Backup/Restore CRs created in this case are as below: ``` spec: backupStorageLocation: default @@ -404,6 +404,18 @@ spec: ... uploader-type: restic volume: nginx-log +``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 + uploader-type: restic + volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` @@ -416,7 +428,7 @@ spec: command: - /velero ``` -The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: +The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolume Backup/Restore CRs are created with "kopia" as well: ``` spec: backupStorageLocation: default @@ -438,6 +450,18 @@ spec: uploader-type: kopia volume: nginx-log ``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 + uploader-type: kopia + volume: nginx-log +``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: ``` @@ -455,8 +479,9 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec ```velero install --pod-volume-backup-uploader=kopia``` ## Upgrade -For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. -Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. +Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade. +The recommended way for upgrade is to modify the related Velero resource directly through kubectl, the above changes will be applied in the same way. We need to modify the Velero doc for all these changes. ## CLI Below Velero CLI or its output needs some changes: @@ -464,7 +489,13 @@ Below Velero CLI or its output needs some changes: - ```Velero restore describe```: the output should indicate the path - ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print all the backup repository if Restic repository and Unified Repository exist at the same time -At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. +At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is a requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. + +## CR Example +Below sample files demonstrate complete CRs with all the changes mentioned above: +- BackupRepository CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-backup-repository-sample-yaml +- PodVolumeBackup CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvb-sample-yaml +- PodVolumeRestore CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvr-sample-yaml ## User Experience Changes Below user experiences are changed for this design: From c3f8e91f73b1a16985276d6fae493ff788f7bd66 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 29 Jun 2022 19:07:25 +0800 Subject: [PATCH 217/366] remove storage configuration redundancy Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index c6fb9b4521..792acd98b0 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -311,18 +311,17 @@ This means, we add a new CR type and deprecate the old one. As a result, if user As a side effect, when upgrading from an old release, even though the path is not changed, the BackupRepository gets created all the time, because Velero will not refer to the old CR's status. This seems to cause the repository to initialize more than once, however, it won't happen. In the BackupRepository controller, before initializing a repository, it always tries to connect to the repository first, if it is connectable, it won't do the initialization. When backing up with the new release, Velero always creates BackupRepository CRs instead of ResticRepository CRs. When restoring from an old backup, Velero always creates BackupRepository CRs instead of ResticRepository CRs. -For a upgrade case, if there are already backups or restores running during the upgrade, the backups/restores could fall into below results: -- The backups/restores have started but the ResticRepository CRs have not been created. In this case, the BackupRepository CRs will be created instead, the backups/restores will finish successfully. -- The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. -- The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. +When there are already backups or restores running during the upgrade, since after upgrade, the Velero server pods and VeleroNodeAgent daemonset pods are restarted, the existing backups/restores will fail immediately. ## Storage Configuration -The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will use a structured storage configuration to replace the repoIdentififer string. -The configuration in BackupRepository CRs, PodVolumeBackup CRs and PodVolumeRestore CRs are as below: +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will add a structured storage configuration and preserved the existing repoIdentififer string for Restic, considering backward compatibility. +Moreover, the storage configuration structure is added into the BackupRepository CRs only. When a PodVolume controller processes a PodVolume CR, it could get the corresponding BackupRepository CR first and then get the storage configuration from the CR. By this there will not be redundant information. +The configuration in BackupRepository CRs are as below: ``` spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s + resticIdentifier: azure:container01:/restic/nginx-example storageConfig: provider: azure param: @@ -330,40 +329,6 @@ spec: prefix: /restic/nginx-example volumeNamespace: nginx-example ``` -``` -spec: - backupStorageLocation: default - node: aks-agentpool-27359964-vmss000000 - pod: - kind: Pod - name: nginx-stateful-0 - namespace: nginx-example - uid: 86aaec56-2b21-4736-9964-621047717133 - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - tags: - ... - volume: nginx-log -``` -``` -spec: - backupStorageLocation: default - pod: - kind: Pod - name: nginx-stateful-0 - namespace: nginx-example - uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 - snapshotID: 1741e5f1 - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - volume: nginx-log -``` ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: From c6625d1424524e358998c53c38c05aa2824147a2 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Thu, 30 Jun 2022 10:09:29 +0800 Subject: [PATCH 218/366] add changelog file for this PR Signed-off-by: Niu Lechuan --- changelogs/unreleased/5051-niulechuan | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/5051-niulechuan diff --git a/changelogs/unreleased/5051-niulechuan b/changelogs/unreleased/5051-niulechuan new file mode 100644 index 0000000000..1cba2e2480 --- /dev/null +++ b/changelogs/unreleased/5051-niulechuan @@ -0,0 +1 @@ +Fix typo in doc, in https://velero.io/docs/main/restore-reference/ "Restore order" section, "Mamespace" should be "Namespace". From fd31336c4afbeeb003ac8390da31518a412ee1e2 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 30 Jun 2022 14:47:49 +0800 Subject: [PATCH 219/366] add user perspective Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 792acd98b0..ee9d798a47 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -462,11 +462,16 @@ Below sample files demonstrate complete CRs with all the changes mentioned above - PodVolumeBackup CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvb-sample-yaml - PodVolumeRestore CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvr-sample-yaml -## User Experience Changes +## User Perspective +This design aims to provide a flexible backup repository layer and a generic file system uploader, which are fundermental for PodVolume and other data movements. Although this will make Velero more capable, at present, we don't pursue to expose differentiated features end to end. Specifically: +- By default, Velero still uses Restic for PodVolume BR +- Even when changing to the new path, Velero still allows users to restore from the data backed up by Restic +- The capability of PodVolume BR under the new path is kept the same as it under Restic path and the same as the existing PodVolume BR +- The operational experiences are kept the same as much as possible, the known changes are listed below + Below user experiences are changed for this design: - Installation CLI change: a new option is added to the installation CLI, see the Installation section for details - CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details - Velero CLI name and output change, see the CLI section for details - Velero daemonset name change -- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes - +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes \ No newline at end of file From c633f68ac036fb4d8ca3a138ce636de0b32c0d84 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 30 Jun 2022 14:00:19 +0800 Subject: [PATCH 220/366] Propose Yonghui as a maintainer Yonghui joined the velero team earlier this year. He has been leading the effort for kopia integration, and delivered the comprehensive comparison report for kopia .vs. restic https://docs.google.com/document/d/1BMLuRzEpYWYE-Ci_eLg8gWbjDv4DSyqj/edit and the detailed design for using kopia as the unified repository: https://github.com/vmware-tanzu/velero/pull/4926 Signed-off-by: Daniel Jiang --- .github/auto-assignees.yml | 1 + MAINTAINERS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/auto-assignees.yml b/.github/auto-assignees.yml index 282591d72f..8f5a75a578 100644 --- a/.github/auto-assignees.yml +++ b/.github/auto-assignees.yml @@ -16,6 +16,7 @@ reviewers: - blackpiglet - qiuming-best - shubham-pampattiwar + - Lyndon-Li tech-writer: - a-mccarthy diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 2819a77e88..f62eb0f94f 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -13,6 +13,7 @@ | Xun Jiang | [blackpiglet](https://github.com/blackpiglet) | [VMware](https://www.github.com/vmware/) | | Ming Qiu | [qiuming-best](https://github.com/qiuming-best) | [VMware](https://www.github.com/vmware/) | | Shubham Pampattiwar | [shubham-pampattiwar](https://github.com/shubham-pampattiwar) | [OpenShift](https://github.com/openshift) +| Yonghui Li | [Lyndon-Li](https://github.com/Lyndon-Li) | [VMware](https://www.github.com/vmware/) | ## Emeritus Maintainers * Adnan Abdulhussein ([prydonius](https://github.com/prydonius)) From fb897471c01fc6623e3ed46e752eb6a04b4d98b4 Mon Sep 17 00:00:00 2001 From: niulechuan <81207605+niulechuan@users.noreply.github.com> Date: Mon, 4 Jul 2022 14:31:20 +0800 Subject: [PATCH 221/366] Move 'velero.io/exclude-from-backup' label string to const (#5053) * move 'velero.io/exclude-from-backup' label name to const Signed-off-by: Niu Lechuan * add changelog file (in changelogs/unreleased) of this PR Signed-off-by: Niu Lechuan --- changelogs/unreleased/5053-niulechuan | 1 + pkg/backup/item_backupper.go | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5053-niulechuan diff --git a/changelogs/unreleased/5053-niulechuan b/changelogs/unreleased/5053-niulechuan new file mode 100644 index 0000000000..f44c46a699 --- /dev/null +++ b/changelogs/unreleased/5053-niulechuan @@ -0,0 +1 @@ +Move 'velero.io/exclude-from-backup' label string to const diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index fd4b16028b..bd40c3bdba 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -61,6 +61,11 @@ type itemBackupper struct { snapshotLocationVolumeSnapshotters map[string]velero.VolumeSnapshotter } +const ( + // veleroExcludeFromBackupLabel labeled item should be exclude by velero in backup job. + veleroExcludeFromBackupLabel = "velero.io/exclude-from-backup" +) + // backupItem backs up an individual item to tarWriter. The item may be excluded based on the // namespaces IncludesExcludes list. // In addition to the error return, backupItem also returns a bool indicating whether the item @@ -78,8 +83,8 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr log = log.WithField("resource", groupResource.String()) log = log.WithField("namespace", namespace) - if metadata.GetLabels()["velero.io/exclude-from-backup"] == "true" { - log.Info("Excluding item because it has label velero.io/exclude-from-backup=true") + if metadata.GetLabels()[veleroExcludeFromBackupLabel] == "true" { + log.Infof("Excluding item because it has label %s=true", veleroExcludeFromBackupLabel) return false, nil } From f550f8e3cda9b6cfaad065525ccc679703622000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 28 Jun 2022 18:14:40 +0800 Subject: [PATCH 222/366] Fix bsl validation bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix bsl validation bug: the BSL is validated continually and doesn't respect the validation period configured Fixes #5056 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/5101-ywk253100 | 1 + .../backup_storage_location_controller.go | 29 +-- pkg/util/kube/predicate.go | 47 +++++ pkg/util/kube/predicate_test.go | 180 ++++++++++++++++++ 4 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 changelogs/unreleased/5101-ywk253100 create mode 100644 pkg/util/kube/predicate.go create mode 100644 pkg/util/kube/predicate_test.go diff --git a/changelogs/unreleased/5101-ywk253100 b/changelogs/unreleased/5101-ywk253100 new file mode 100644 index 0000000000..ade00f2a99 --- /dev/null +++ b/changelogs/unreleased/5101-ywk253100 @@ -0,0 +1 @@ + Fix bsl validation bug: the BSL is validated continually and doesn't respect the validation period configured \ No newline at end of file diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index ec35a89167..1b08da897e 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -24,12 +24,10 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -39,7 +37,10 @@ import ( ) const ( - backupStorageLocationSyncPeriod = 1 * time.Minute + // keep the enqueue period a smaller value to make sure the BSL can be validated as expected. + // The BSL validation frequency is 1 minute by default, if we set the enqueue period as 1 minute, + // this will cause the actual validation interval for each BSL to be 2 minutes + bslValidationEnqueuePeriod = 10 * time.Second ) // BackupStorageLocationReconciler reconciles a BackupStorageLocation object @@ -185,7 +186,7 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err r.Log, mgr.GetClient(), &velerov1api.BackupStorageLocationList{}, - backupStorageLocationSyncPeriod, + bslValidationEnqueuePeriod, // Add filter function to enqueue BSL per ValidationFrequency setting. func(object client.Object) bool { location := object.(*velerov1api.BackupStorageLocation) @@ -193,22 +194,8 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err }, ) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.BackupStorageLocation{}). - // Handle BSL's creation event and spec update event to let changed BSL got validation immediately. - WithEventFilter(predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return true - }, - UpdateFunc: func(ue event.UpdateEvent) bool { - return ue.ObjectNew.GetGeneration() != ue.ObjectOld.GetGeneration() - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - GenericFunc: func(ge event.GenericEvent) bool { - return false - }, - }). + // As the "status.LastValidationTime" field is always updated, this triggers new reconciling process, skip the update event that include no spec change to avoid the reconcile loop + For(&velerov1api.BackupStorageLocation{}, builder.WithPredicates(kube.SpecChangePredicate{})). Watches(g, nil). Complete(r) } diff --git a/pkg/util/kube/predicate.go b/pkg/util/kube/predicate.go new file mode 100644 index 0000000000..3073ef8816 --- /dev/null +++ b/pkg/util/kube/predicate.go @@ -0,0 +1,47 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kube + +import ( + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// SpecChangePredicate implements a default update predicate function on Spec change +// As Velero doesn't enable subresource in CRDs, we cannot use the object's metadata.generation field to check the spec change +// More details about the generation field refer to https://github.com/kubernetes-sigs/controller-runtime/blob/v0.12.2/pkg/predicate/predicate.go#L156 +type SpecChangePredicate struct { + predicate.Funcs +} + +func (SpecChangePredicate) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil { + return false + } + if e.ObjectNew == nil { + return false + } + oldSpec := reflect.ValueOf(e.ObjectOld).Elem().FieldByName("Spec") + // contains no field named "Spec", return false directly + if oldSpec.IsZero() { + return false + } + newSpec := reflect.ValueOf(e.ObjectNew).Elem().FieldByName("Spec") + return !reflect.DeepEqual(oldSpec.Interface(), newSpec.Interface()) +} diff --git a/pkg/util/kube/predicate_test.go b/pkg/util/kube/predicate_test.go new file mode 100644 index 0000000000..d1c3be8df5 --- /dev/null +++ b/pkg/util/kube/predicate_test.go @@ -0,0 +1,180 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kube + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestSpecChangePredicate(t *testing.T) { + cases := []struct { + name string + oldObj client.Object + newObj client.Object + changed bool + }{ + { + name: "Contains no spec field", + oldObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + }, + }, + changed: false, + }, + { + name: "ObjectMetas are different, Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + Annotations: map[string]string{"key1": "value1"}, + }, + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + Annotations: map[string]string{"key2": "value2"}, + }, + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + changed: false, + }, + { + name: "Statuses are different, Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + Status: velerov1.BackupStorageLocationStatus{ + Phase: velerov1.BackupStorageLocationPhaseAvailable, + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + Status: velerov1.BackupStorageLocationStatus{ + Phase: velerov1.BackupStorageLocationPhaseUnavailable, + }, + }, + changed: false, + }, + { + name: "Specs are different", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "aws", + }, + }, + changed: true, + }, + { + name: "Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{"key": "value"}, + Credential: &corev1api.SecretKeySelector{ + LocalObjectReference: corev1api.LocalObjectReference{ + Name: "secret", + }, + Key: "credential", + }, + StorageType: velerov1.StorageType{ + ObjectStorage: &velerov1.ObjectStorageLocation{ + Bucket: "bucket1", + Prefix: "prefix", + CACert: []byte{'a'}, + }, + }, + Default: true, + AccessMode: velerov1.BackupStorageLocationAccessModeReadWrite, + BackupSyncPeriod: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + ValidationFrequency: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{"key": "value"}, + Credential: &corev1api.SecretKeySelector{ + LocalObjectReference: corev1api.LocalObjectReference{ + Name: "secret", + }, + Key: "credential", + }, + StorageType: velerov1.StorageType{ + ObjectStorage: &velerov1.ObjectStorageLocation{ + Bucket: "bucket1", + Prefix: "prefix", + CACert: []byte{'a'}, + }, + }, + Default: true, + AccessMode: velerov1.BackupStorageLocationAccessModeReadWrite, + BackupSyncPeriod: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + ValidationFrequency: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + }, + }, + changed: false, + }, + } + + predicate := SpecChangePredicate{} + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + changed := predicate.Update(event.UpdateEvent{ + ObjectOld: c.oldObj, + ObjectNew: c.newObj, + }) + assert.Equal(t, c.changed, changed) + }) + } +} From bf8d135876e9bba6c3676f613da0f4e7664678ed Mon Sep 17 00:00:00 2001 From: danfengl Date: Fri, 8 Jul 2022 09:35:07 +0000 Subject: [PATCH 223/366] Fix resource filtering command issue in Doc Signed-off-by: danfengl --- site/content/docs/v1.4/resource-filtering.md | 8 ++++---- site/content/docs/v1.5/resource-filtering.md | 8 ++++---- site/content/docs/v1.6/resource-filtering.md | 8 ++++---- site/content/docs/v1.7/resource-filtering.md | 8 ++++---- site/content/docs/v1.8/resource-filtering.md | 8 ++++---- site/content/docs/v1.9/resource-filtering.md | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/site/content/docs/v1.4/resource-filtering.md b/site/content/docs/v1.4/resource-filtering.md index 6c1470e042..0b893c49f4 100644 --- a/site/content/docs/v1.4/resource-filtering.md +++ b/site/content/docs/v1.4/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.5/resource-filtering.md b/site/content/docs/v1.5/resource-filtering.md index 6c1470e042..0b893c49f4 100644 --- a/site/content/docs/v1.5/resource-filtering.md +++ b/site/content/docs/v1.5/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.6/resource-filtering.md b/site/content/docs/v1.6/resource-filtering.md index 6c1470e042..0b893c49f4 100644 --- a/site/content/docs/v1.6/resource-filtering.md +++ b/site/content/docs/v1.6/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.7/resource-filtering.md b/site/content/docs/v1.7/resource-filtering.md index 6c1470e042..0b893c49f4 100644 --- a/site/content/docs/v1.7/resource-filtering.md +++ b/site/content/docs/v1.7/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.8/resource-filtering.md b/site/content/docs/v1.8/resource-filtering.md index ddcd97e4ac..1f2d2133dc 100644 --- a/site/content/docs/v1.8/resource-filtering.md +++ b/site/content/docs/v1.8/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -113,7 +113,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.9/resource-filtering.md b/site/content/docs/v1.9/resource-filtering.md index 02ae3d68d0..5c0b34c730 100644 --- a/site/content/docs/v1.9/resource-filtering.md +++ b/site/content/docs/v1.9/resource-filtering.md @@ -26,7 +26,7 @@ Namespaces to include. Default is `*`, all namespaces. * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -42,7 +42,7 @@ Kubernetes resources to include in the backup, formatted as resource.group, such * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -76,7 +76,7 @@ Includes cluster-scoped resources. This option can have three possible values: * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -119,7 +119,7 @@ Namespaces to exclude. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources From 9102f53131670ba002b41ab9f2170f4212d2a759 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 11 Jul 2022 01:41:00 +0800 Subject: [PATCH 224/366] Dump stack trace when the plugin server handles panic Mitigate the issue mentioned in #4782 When there's a bug or misconfiguration that causes nil pointer there will be more stack trace information to help us debug. Signed-off-by: Daniel Jiang --- changelogs/unreleased/5110-reasonerjt | 1 + pkg/plugin/framework/handle_panic.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5110-reasonerjt diff --git a/changelogs/unreleased/5110-reasonerjt b/changelogs/unreleased/5110-reasonerjt new file mode 100644 index 0000000000..350f91fa17 --- /dev/null +++ b/changelogs/unreleased/5110-reasonerjt @@ -0,0 +1 @@ +Dump stack trace when the plugin server handles panic \ No newline at end of file diff --git a/pkg/plugin/framework/handle_panic.go b/pkg/plugin/framework/handle_panic.go index 10eb1d2b96..4ea0ec2b52 100644 --- a/pkg/plugin/framework/handle_panic.go +++ b/pkg/plugin/framework/handle_panic.go @@ -17,6 +17,8 @@ limitations under the License. package framework import ( + "runtime/debug" + "github.com/pkg/errors" "google.golang.org/grpc/codes" ) @@ -38,7 +40,8 @@ func handlePanic(p interface{}) error { if _, ok := panicErr.(stackTracer); ok { err = panicErr } else { - err = errors.Wrap(panicErr, "plugin panicked") + errWithStacktrace := errors.Errorf("%v, stack trace: %s", panicErr, debug.Stack()) + err = errors.Wrap(errWithStacktrace, "plugin panicked") } } From 9173ac117e271bbdd56200c9504b8eb581a45053 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 18 Jul 2022 19:11:53 +0800 Subject: [PATCH 225/366] Let "make shell xxx" respect GOPROXY This commit mitigates the issue for running "make update" locally when the network is not friendly for accessing the default "proxy.golang.org" Signed-off-by: Daniel Jiang --- Makefile | 1 + changelogs/unreleased/5128-reasonerjt | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelogs/unreleased/5128-reasonerjt diff --git a/Makefile b/Makefile index 8dcf6ee509..b3a6a32f21 100644 --- a/Makefile +++ b/Makefile @@ -163,6 +163,7 @@ shell: build-dirs build-env @# under $GOPATH). @docker run \ -e GOFLAGS \ + -e GOPROXY \ -i $(TTY) \ --rm \ -u $$(id -u):$$(id -g) \ diff --git a/changelogs/unreleased/5128-reasonerjt b/changelogs/unreleased/5128-reasonerjt new file mode 100644 index 0000000000..3ba53b0590 --- /dev/null +++ b/changelogs/unreleased/5128-reasonerjt @@ -0,0 +1 @@ +Let "make shell xxx" respect GOPROXY \ No newline at end of file From 64a8c44104b28d2ed94a9ebc6497d4e539904b98 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 14 Jul 2022 16:25:25 -0400 Subject: [PATCH 226/366] Modify BackupStoreGetter to avoid BSL spec changes Pass in a new copy of the map of config values rather than modifying the BSL Spec.Config and then pass in that field. Signed-off-by: Scott Seago --- changelogs/unreleased/5122-sseago | 1 + pkg/persistence/object_store.go | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/5122-sseago diff --git a/changelogs/unreleased/5122-sseago b/changelogs/unreleased/5122-sseago new file mode 100644 index 0000000000..ec8dc473ef --- /dev/null +++ b/changelogs/unreleased/5122-sseago @@ -0,0 +1 @@ +Modify BackupStoreGetter to avoid BSL spec changes diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 1c0b619e1f..acda15323a 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -131,19 +131,25 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, errors.Errorf("backup storage location's bucket name %q must not contain a '/' (if using a prefix, put it in the 'Prefix' field instead)", location.Spec.ObjectStorage.Bucket) } + // Pass a new map into the object store rather than modifying the passed-in + // location. This prevents Velero controllers from accidentally modifying + // the in-cluster BSL with data which doesn't belong in Spec.Config + objectStoreConfig := make(map[string]string) + if location.Spec.Config != nil { + for key, val := range location.Spec.Config { + objectStoreConfig[key] = val + } + } + // add the bucket name and prefix to the config map so that object stores // can use them when initializing. The AWS object store uses the bucket // name to determine the bucket's region when setting up its client. - if location.Spec.Config == nil { - location.Spec.Config = make(map[string]string) - } - - location.Spec.Config["bucket"] = bucket - location.Spec.Config["prefix"] = prefix + objectStoreConfig["bucket"] = bucket + objectStoreConfig["prefix"] = prefix // Only include a CACert if it's specified in order to maintain compatibility with plugins that don't expect it. if location.Spec.ObjectStorage.CACert != nil { - location.Spec.Config["caCert"] = string(location.Spec.ObjectStorage.CACert) + objectStoreConfig["caCert"] = string(location.Spec.ObjectStorage.CACert) } // If the BSL specifies a credential, fetch its path on disk and pass to @@ -154,7 +160,7 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, errors.Wrap(err, "unable to get credentials") } - location.Spec.Config["credentialsFile"] = credsFile + objectStoreConfig["credentialsFile"] = credsFile } objectStore, err := objectStoreGetter.GetObjectStore(location.Spec.Provider) @@ -162,7 +168,7 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, err } - if err := objectStore.Init(location.Spec.Config); err != nil { + if err := objectStore.Init(objectStoreConfig); err != nil { return nil, err } From 9a5c3aceffdabbbd321ad0dd2a2501d061c366f1 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Mon, 18 Jul 2022 12:01:24 -0400 Subject: [PATCH 227/366] typo initialise -> initialize Signed-off-by: Tiger Kaovilai --- pkg/controller/backup_deletion_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 9cbfe1e8d9..52d358042e 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -198,7 +198,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - // if the request object has no labels defined, initialise an empty map since + // if the request object has no labels defined, initialize an empty map since // we will be updating labels if dbr.Labels == nil { dbr.Labels = map[string]string{} From 3a802e160bee3d50ac804c284e86995bc7434b09 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 17:42:06 +0800 Subject: [PATCH 228/366] change storage config Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index ee9d798a47..04d0065538 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -99,15 +99,15 @@ type BackupRepoService interface { ///repoOption: option to the backup repository and the underlying backup storage ///createNew: indicates whether to create a new or connect to an existing backup repository ///result: the backup repository specific output that could be used to open the backup repository later - Init(ctx context.Context, repoOption RepoOptions, createNew bool) (result map[string]string, err error) + Init(ctx context.Context, repoOption RepoOptions, createNew bool) error ///Open an backup repository that has been created/connected - ///config: options to open the backup repository and the underlying storage - Open(ctx context.Context, config map[string]string) (BackupRepo, error) + ///repoOption: options to open the backup repository and the underlying storage + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance - ///config: options to open the backup repository and the underlying storage - Maintain(ctx context.Context, config map[string]string) error + ///repoOption: options to maintain the backup repository + Maintain(ctx context.Context, repoOption RepoOptions) error } ///BackupRepo provides the access to the backup repository @@ -156,7 +156,11 @@ type ObjectWriter interface { ///For some cases, i.e. block incremental, the object is not written sequentially io.Seeker - + // Periodically called to preserve the state of data written to the repo so far + // Return a unified identifier that represent the current state + // An empty ID could be returned on success if the backup repository doesn't support this + Checkpoint() (ID, error) + ///Wait for the completion of the object write ///Result returns the object's unified identifier after the write completes Result() (ID, error) @@ -314,21 +318,9 @@ When restoring from an old backup, Velero always creates BackupRepository CRs in When there are already backups or restores running during the upgrade, since after upgrade, the Velero server pods and VeleroNodeAgent daemonset pods are restarted, the existing backups/restores will fail immediately. ## Storage Configuration -The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will add a structured storage configuration and preserved the existing repoIdentififer string for Restic, considering backward compatibility. -Moreover, the storage configuration structure is added into the BackupRepository CRs only. When a PodVolume controller processes a PodVolume CR, it could get the corresponding BackupRepository CR first and then get the storage configuration from the CR. By this there will not be redundant information. -The configuration in BackupRepository CRs are as below: -``` -spec: - backupStorageLocation: default - maintenanceFrequency: 168h0m0s - resticIdentifier: azure:container01:/restic/nginx-example - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - volumeNamespace: nginx-example -``` +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. On the other hand, the parameters that are used to generate the repoIdentififer all come from the BackupStorageLocation. The latter has a map structure that could take parameters from any storage kind. +Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it accquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. +The legacy path will be kept as is. That is, Velero still sets/gets the repoIdentififer in BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs and then passes to Restic CLI. ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: From 7a38aa5e0fb8b9ffdb3597faf67e299d6a0d2527 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 17:50:14 +0800 Subject: [PATCH 229/366] fix alignment Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 04d0065538..d56f8ae777 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -102,12 +102,12 @@ type BackupRepoService interface { Init(ctx context.Context, repoOption RepoOptions, createNew bool) error ///Open an backup repository that has been created/connected - ///repoOption: options to open the backup repository and the underlying storage - Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) + ///repoOption: options to open the backup repository and the underlying storage + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance - ///repoOption: options to maintain the backup repository - Maintain(ctx context.Context, repoOption RepoOptions) error + ///repoOption: options to maintain the backup repository + Maintain(ctx context.Context, repoOption RepoOptions) error } ///BackupRepo provides the access to the backup repository @@ -155,11 +155,11 @@ type ObjectWriter interface { ///For some cases, i.e. block incremental, the object is not written sequentially io.Seeker - - // Periodically called to preserve the state of data written to the repo so far - // Return a unified identifier that represent the current state - // An empty ID could be returned on success if the backup repository doesn't support this - Checkpoint() (ID, error) + + // Periodically called to preserve the state of data written to the repo so far + // Return a unified identifier that represent the current state + // An empty ID could be returned on success if the backup repository doesn't support this + Checkpoint() (ID, error) ///Wait for the completion of the object write ///Result returns the object's unified identifier after the write completes From c845f0c5ea0dd4b4c7f171412e7a852e4a44d862 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 18:05:28 +0800 Subject: [PATCH 230/366] fix typo Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index d56f8ae777..122f85b43c 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -319,7 +319,7 @@ When there are already backups or restores running during the upgrade, since aft ## Storage Configuration The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. On the other hand, the parameters that are used to generate the repoIdentififer all come from the BackupStorageLocation. The latter has a map structure that could take parameters from any storage kind. -Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it accquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. +Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it acquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. The legacy path will be kept as is. That is, Velero still sets/gets the repoIdentififer in BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs and then passes to Restic CLI. ## Installation From f562a7ce2b811f1015b31094be767a399c790f0f Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 19 Jul 2022 16:06:40 +0800 Subject: [PATCH 231/366] Update the CRD for kopia integration This commit adds additional fields to podvolumebackup and podvolumerestore. The resticrepository will be renamed to backuprepository Signed-off-by: Daniel Jiang --- changelogs/unreleased/5135-reasonerjt | 1 + ...yaml => velero.io_backuprepositories.yaml} | 30 ++- .../v1/bases/velero.io_podvolumebackups.yaml | 20 +- .../v1/bases/velero.io_podvolumerestores.yaml | 16 +- config/crd/v1/crds/crds.go | 6 +- config/rbac/role.yaml | 38 ++-- .../unified-repo-and-kopia-integration.md | 18 +- ...ry_types.go => backup_repository_types.go} | 49 +++-- pkg/apis/velero/v1/pod_volume_backup_types.go | 12 +- pkg/apis/velero/v1/pod_volume_restore_type.go | 10 +- pkg/apis/velero/v1/register.go | 2 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 188 +++++++++--------- pkg/cmd/cli/backuplocation/delete.go | 6 +- pkg/cmd/cli/restic/repo/get.go | 8 +- pkg/cmd/server/server.go | 2 +- pkg/cmd/util/output/output.go | 8 +- pkg/cmd/util/output/restic_repo_printer.go | 6 +- .../restic_repository_controller.go | 50 ++--- .../restic_repository_controller_test.go | 36 ++-- pkg/controller/restore_controller.go | 4 + ...esticrepository.go => backuprepository.go} | 106 +++++----- .../velero/v1/fake/fake_backuprepository.go | 142 +++++++++++++ .../velero/v1/fake/fake_resticrepository.go | 142 ------------- .../velero/v1/fake/fake_velero_client.go | 8 +- .../typed/velero/v1/generated_expansion.go | 4 +- .../typed/velero/v1/velero_client.go | 10 +- .../informers/externalversions/generic.go | 4 +- ...esticrepository.go => backuprepository.go} | 38 ++-- .../externalversions/velero/v1/interface.go | 14 +- .../listers/velero/v1/backuprepository.go | 99 +++++++++ .../listers/velero/v1/expansion_generated.go | 16 +- .../listers/velero/v1/resticrepository.go | 99 --------- pkg/restic/backupper.go | 7 +- pkg/restic/mocks/repository_manager.go | 16 +- pkg/restic/repository_ensurer.go | 35 ++-- pkg/restic/repository_manager.go | 22 +- pkg/restic/restorer.go | 6 +- 37 files changed, 669 insertions(+), 609 deletions(-) create mode 100644 changelogs/unreleased/5135-reasonerjt rename config/crd/v1/bases/{velero.io_resticrepositories.yaml => velero.io_backuprepositories.yaml} (78%) rename pkg/apis/velero/v1/{restic_repository_types.go => backup_repository_types.go} (64%) rename pkg/generated/clientset/versioned/typed/velero/v1/{resticrepository.go => backuprepository.go} (53%) create mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.go rename pkg/generated/informers/externalversions/velero/v1/{resticrepository.go => backuprepository.go} (70%) create mode 100644 pkg/generated/listers/velero/v1/backuprepository.go delete mode 100644 pkg/generated/listers/velero/v1/resticrepository.go diff --git a/changelogs/unreleased/5135-reasonerjt b/changelogs/unreleased/5135-reasonerjt new file mode 100644 index 0000000000..0505ab0468 --- /dev/null +++ b/changelogs/unreleased/5135-reasonerjt @@ -0,0 +1 @@ +Update the CRD for kopia integration \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_resticrepositories.yaml b/config/crd/v1/bases/velero.io_backuprepositories.yaml similarity index 78% rename from config/crd/v1/bases/velero.io_resticrepositories.yaml rename to config/crd/v1/bases/velero.io_backuprepositories.yaml index 2517661369..fa7e5596ee 100644 --- a/config/crd/v1/bases/velero.io_resticrepositories.yaml +++ b/config/crd/v1/bases/velero.io_backuprepositories.yaml @@ -6,20 +6,23 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.7.0 creationTimestamp: null - name: resticrepositories.velero.io + name: backuprepositories.velero.io spec: group: velero.io names: - kind: ResticRepository - listKind: ResticRepositoryList - plural: resticrepositories - singular: resticrepository + kind: BackupRepository + listKind: BackupRepositoryList + plural: backuprepositories + singular: backuprepository scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date + - jsonPath: .spec.repositoryType + name: Repository Type + type: string name: v1 schema: openAPIV3Schema: @@ -37,7 +40,7 @@ spec: metadata: type: object spec: - description: ResticRepositorySpec is the specification for a ResticRepository. + description: BackupRepositorySpec is the specification for a BackupRepository. properties: backupStorageLocation: description: BackupStorageLocation is the name of the BackupStorageLocation @@ -47,12 +50,19 @@ spec: description: MaintenanceFrequency is how often maintenance should be run. type: string + repositoryType: + description: RepositoryType indicates the type of the backend repository + enum: + - kopia + - restic + - "" + type: string resticIdentifier: description: ResticIdentifier is the full restic-compatible string for identifying this repository. type: string volumeNamespace: - description: VolumeNamespace is the namespace this restic repository + description: VolumeNamespace is the namespace this backup repository contains pod volume backups for. type: string required: @@ -62,7 +72,7 @@ spec: - volumeNamespace type: object status: - description: ResticRepositoryStatus is the current status of a ResticRepository. + description: BackupRepositoryStatus is the current status of a BackupRepository. properties: lastMaintenanceTime: description: LastMaintenanceTime is the last time maintenance was @@ -72,10 +82,10 @@ spec: type: string message: description: Message is a message about the current status of the - ResticRepository. + BackupRepository. type: string phase: - description: Phase is the current state of the ResticRepository. + description: Phase is the current state of the BackupRepository. enum: - New - Ready diff --git a/config/crd/v1/bases/velero.io_podvolumebackups.yaml b/config/crd/v1/bases/velero.io_podvolumebackups.yaml index 69993f0d4c..6af4ce294e 100644 --- a/config/crd/v1/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/v1/bases/velero.io_podvolumebackups.yaml @@ -37,9 +37,13 @@ spec: jsonPath: .spec.volume name: Volume type: string - - description: Restic repository identifier for this backup + - description: Backup repository identifier for this backup jsonPath: .spec.repoIdentifier - name: Restic Repo + name: Repository ID + type: string + - description: The type of the uploader to handle data transfer + jsonPath: .spec.uploaderType + name: Uploader Type type: string - description: Name of the Backup Storage Location where this backup should be stored @@ -70,7 +74,7 @@ spec: properties: backupStorageLocation: description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. + location where the backup repository is stored. type: string node: description: Node is the name of the node that the Pod is running @@ -114,7 +118,7 @@ spec: type: string type: object repoIdentifier: - description: RepoIdentifier is the restic repository identifier. + description: RepoIdentifier is the backup repository identifier. type: string tags: additionalProperties: @@ -122,6 +126,14 @@ spec: description: Tags are a map of key-value pairs that should be applied to the volume backup as tags. type: object + uploaderType: + description: UploaderType is the type of the uploader to handle the + data transfer. + enum: + - kopia + - restic + - "" + type: string volume: description: Volume is the name of the volume within the Pod to be backed up. diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index 6f77cb67c9..036f58a06d 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -25,6 +25,10 @@ spec: jsonPath: .spec.pod.name name: Pod type: string + - description: The type of the uploader to handle data transfer + jsonPath: .spec.uploaderType + name: Uploader Type + type: string - description: Name of the volume to be restored jsonPath: .spec.volume name: Volume @@ -67,7 +71,7 @@ spec: properties: backupStorageLocation: description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. + location where the backup repository is stored. type: string pod: description: Pod is a reference to the pod containing the volume to @@ -107,11 +111,19 @@ spec: type: string type: object repoIdentifier: - description: RepoIdentifier is the restic repository identifier. + description: RepoIdentifier is the backup repository identifier. type: string snapshotID: description: SnapshotID is the ID of the volume snapshot to be restored. type: string + uploaderType: + description: UploaderType is the type of the uploader to handle the + data transfer. + enum: + - kopia + - restic + - "" + type: string volume: description: Volume is the name of the volume within the Pod to be restored. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 4b1197a47f..2d7135ff66 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,13 +29,13 @@ import ( ) var rawCRDs = [][]byte{ + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z\xdds\x1b\xb7\x11\u007f\xe7_\xb1\xe3<\xa8\x991\x8f\x89\xdbi;|s\xa4\xa6\xa36\x915\x96\xad\x17\x8f\x1f\xc0\xc3\xf2\x0e\xd1\x1d\x80\x028\xcal&\xff{g\xf1q\xbc\x0f\x90\x944ur/6\xf1\xb1\xf8a\xbfw\xa1\xc5r\xb9\\0-\xee\xd1X\xa1\xe4\x1a\x98\x16\xf8š\xa4_\xb6x\xf8\xbb-\x84Z\xed\xbe_<\b\xc9\xd7p\xd9Y\xa7\xda\xf7hUgJ\xbc\u00ad\x90\xc2\t%\x17-:ƙc\xeb\x05\x00\x93R9FÖ~\x02\x94J:\xa3\x9a\x06ͲBY\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0b43e2f549..6b02727a52 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -27,14 +27,7 @@ rules: - apiGroups: - velero.io resources: - - backups - verbs: - - create - - delete -- apiGroups: - - velero.io - resources: - - backupstoragelocations + - backuprepositories verbs: - create - delete @@ -46,7 +39,7 @@ rules: - apiGroups: - velero.io resources: - - backupstoragelocations/status + - backuprepositories/status verbs: - get - patch @@ -54,7 +47,14 @@ rules: - apiGroups: - velero.io resources: - - deletebackuprequests + - backups + verbs: + - create + - delete +- apiGroups: + - velero.io + resources: + - backupstoragelocations verbs: - create - delete @@ -66,7 +66,7 @@ rules: - apiGroups: - velero.io resources: - - deletebackuprequests/status + - backupstoragelocations/status verbs: - get - patch @@ -74,7 +74,7 @@ rules: - apiGroups: - velero.io resources: - - downloadrequests + - deletebackuprequests verbs: - create - delete @@ -86,7 +86,7 @@ rules: - apiGroups: - velero.io resources: - - downloadrequests/status + - deletebackuprequests/status verbs: - get - patch @@ -94,7 +94,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackups + - downloadrequests verbs: - create - delete @@ -106,7 +106,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumebackups/status + - downloadrequests/status verbs: - get - patch @@ -114,7 +114,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumerestores + - podvolumebackups verbs: - create - delete @@ -126,7 +126,7 @@ rules: - apiGroups: - velero.io resources: - - podvolumerestores/status + - podvolumebackups/status verbs: - get - patch @@ -134,7 +134,7 @@ rules: - apiGroups: - velero.io resources: - - resticrepositories + - podvolumerestores verbs: - create - delete @@ -146,7 +146,7 @@ rules: - apiGroups: - velero.io resources: - - resticrepositories/status + - podvolumerestores/status verbs: - get - patch diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 122f85b43c..5b4a897e26 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -297,11 +297,11 @@ Kopia’s debug logs will be written to the same log file as Velero server or Ve As mentioned above, There will be two paths. The related controllers need to identify the path during runtime and adjust its working mode. According to the requirements, path changing is fulfilled at the backup/restore level. In order to let the controllers know the path, we need to add some option values. Specifically, there will be option/mode values for path selection in two places: - Add the “uploader-type” option as a parameter of the Velero server. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolume Backup/Restore CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. +- Add a "uploaderType" value in the PodVolume Backup/Restore CR and a "repositoryType" value in the BackupRepository CR. "uploaderType" currently has two values , either "restic" or "kopia"; "repositoryType" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repositoryType is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploaderType=restic" and "repositoryType=restic", so the legacy CRs are handled correctly by Restic. The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: -- The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path -- The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path +- The PodVolume BR controller checks the "uploaderType" value from PodVolume CRs and decide its working path +- The BackupRepository controller checks the "repositoryType" value from BackupRepository CRs and decide its working path - The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup it is going to create and then create the PodVolume Backup CR and BackupRepository CR - The Restore controller checks the Backup, from which it is going to restore, for the path and then create the PodVolume Restore CR and BackupRepository CR @@ -345,7 +345,7 @@ The BackupRepository CRs and PodVolume Backup/Restore CRs created in this case a spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s - repository-type: restic + repositoryType: restic volumeNamespace: nginx-example ``` ``` @@ -359,7 +359,7 @@ spec: uid: 86aaec56-2b21-4736-9964-621047717133 tags: ... - uploader-type: restic + uploaderType: restic volume: nginx-log ``` ``` @@ -371,7 +371,7 @@ spec: namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 snapshotID: 1741e5f1 - uploader-type: restic + uploaderType: restic volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: @@ -390,7 +390,7 @@ The BackupRepository CRs created in this case are hard set with "kopia" at prese spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s - repository-type: kopia + repositoryType: kopia volumeNamespace: nginx-example ``` ``` @@ -404,7 +404,7 @@ spec: uid: 86aaec56-2b21-4736-9964-621047717133 tags: ... - uploader-type: kopia + uploaderType: kopia volume: nginx-log ``` ``` @@ -416,7 +416,7 @@ spec: namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 snapshotID: 1741e5f1 - uploader-type: kopia + uploaderType: kopia volume: nginx-log ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: diff --git a/pkg/apis/velero/v1/restic_repository_types.go b/pkg/apis/velero/v1/backup_repository_types.go similarity index 64% rename from pkg/apis/velero/v1/restic_repository_types.go rename to pkg/apis/velero/v1/backup_repository_types.go index 8e315592fd..300ecae9c1 100644 --- a/pkg/apis/velero/v1/restic_repository_types.go +++ b/pkg/apis/velero/v1/backup_repository_types.go @@ -20,9 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// ResticRepositorySpec is the specification for a ResticRepository. -type ResticRepositorySpec struct { - // VolumeNamespace is the namespace this restic repository contains +// BackupRepositorySpec is the specification for a BackupRepository. +type BackupRepositorySpec struct { + // VolumeNamespace is the namespace this backup repository contains // pod volume backups for. VolumeNamespace string `json:"volumeNamespace"` @@ -30,6 +30,11 @@ type ResticRepositorySpec struct { // that should contain this repository. BackupStorageLocation string `json:"backupStorageLocation"` + // RepositoryType indicates the type of the backend repository + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + RepositoryType string `json:"repositoryType"` + // ResticIdentifier is the full restic-compatible string for identifying // this repository. ResticIdentifier string `json:"resticIdentifier"` @@ -38,23 +43,23 @@ type ResticRepositorySpec struct { MaintenanceFrequency metav1.Duration `json:"maintenanceFrequency"` } -// ResticRepositoryPhase represents the lifecycle phase of a ResticRepository. +// BackupRepositoryPhase represents the lifecycle phase of a BackupRepository. // +kubebuilder:validation:Enum=New;Ready;NotReady -type ResticRepositoryPhase string +type BackupRepositoryPhase string const ( - ResticRepositoryPhaseNew ResticRepositoryPhase = "New" - ResticRepositoryPhaseReady ResticRepositoryPhase = "Ready" - ResticRepositoryPhaseNotReady ResticRepositoryPhase = "NotReady" + BackupRepositoryPhaseNew BackupRepositoryPhase = "New" + BackupRepositoryPhaseReady BackupRepositoryPhase = "Ready" + BackupRepositoryPhaseNotReady BackupRepositoryPhase = "NotReady" ) -// ResticRepositoryStatus is the current status of a ResticRepository. -type ResticRepositoryStatus struct { - // Phase is the current state of the ResticRepository. +// BackupRepositoryStatus is the current status of a BackupRepository. +type BackupRepositoryStatus struct { + // Phase is the current state of the BackupRepository. // +optional - Phase ResticRepositoryPhase `json:"phase,omitempty"` + Phase BackupRepositoryPhase `json:"phase,omitempty"` - // Message is a message about the current status of the ResticRepository. + // Message is a message about the current status of the BackupRepository. // +optional Message string `json:"message,omitempty"` @@ -72,33 +77,35 @@ type ResticRepositoryStatus struct { // +kubebuilder:object:generate=true // +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Repository Type",type="string",JSONPath=".spec.repositoryType" +// -type ResticRepository struct { +type BackupRepository struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ObjectMeta `json:"metadata,omitempty"` // +optional - Spec ResticRepositorySpec `json:"spec,omitempty"` + Spec BackupRepositorySpec `json:"spec,omitempty"` // +optional - Status ResticRepositoryStatus `json:"status,omitempty"` + Status BackupRepositoryStatus `json:"status,omitempty"` } // TODO(2.0) After converting all resources to use the runtime-controller client, // the k8s:deepcopy marker will no longer be needed and should be removed. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:object:root=true -// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=velero.io,resources=backuprepositories,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=backuprepositories/status,verbs=get;update;patch -// ResticRepositoryList is a list of ResticRepositories. -type ResticRepositoryList struct { +// BackupRepositoryList is a list of BackupRepositories. +type BackupRepositoryList struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []ResticRepository `json:"items"` + Items []BackupRepository `json:"items"` } diff --git a/pkg/apis/velero/v1/pod_volume_backup_types.go b/pkg/apis/velero/v1/pod_volume_backup_types.go index c532f096de..d34e09f6ce 100644 --- a/pkg/apis/velero/v1/pod_volume_backup_types.go +++ b/pkg/apis/velero/v1/pod_volume_backup_types.go @@ -34,12 +34,17 @@ type PodVolumeBackupSpec struct { Volume string `json:"volume"` // BackupStorageLocation is the name of the backup storage location - // where the restic repository is stored. + // where the backup repository is stored. BackupStorageLocation string `json:"backupStorageLocation"` - // RepoIdentifier is the restic repository identifier. + // RepoIdentifier is the backup repository identifier. RepoIdentifier string `json:"repoIdentifier"` + // UploaderType is the type of the uploader to handle the data transfer. + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + UploaderType string `json:"uploaderType"` + // Tags are a map of key-value pairs that should be applied to the // volume backup as tags. // +optional @@ -107,7 +112,8 @@ type PodVolumeBackupStatus struct { // +kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.pod.namespace",description="Namespace of the pod containing the volume to be backed up" // +kubebuilder:printcolumn:name="Pod",type="string",JSONPath=".spec.pod.name",description="Name of the pod containing the volume to be backed up" // +kubebuilder:printcolumn:name="Volume",type="string",JSONPath=".spec.volume",description="Name of the volume to be backed up" -// +kubebuilder:printcolumn:name="Restic Repo",type="string",JSONPath=".spec.repoIdentifier",description="Restic repository identifier for this backup" +// +kubebuilder:printcolumn:name="Repository ID",type="string",JSONPath=".spec.repoIdentifier",description="Backup repository identifier for this backup" +// +kubebuilder:printcolumn:name="Uploader Type",type="string",JSONPath=".spec.uploaderType",description="The type of the uploader to handle data transfer" // +kubebuilder:printcolumn:name="Storage Location",type="string",JSONPath=".spec.backupStorageLocation",description="Name of the Backup Storage Location where this backup should be stored" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:object:root=true diff --git a/pkg/apis/velero/v1/pod_volume_restore_type.go b/pkg/apis/velero/v1/pod_volume_restore_type.go index 45bca8e248..e0370da637 100644 --- a/pkg/apis/velero/v1/pod_volume_restore_type.go +++ b/pkg/apis/velero/v1/pod_volume_restore_type.go @@ -30,12 +30,17 @@ type PodVolumeRestoreSpec struct { Volume string `json:"volume"` // BackupStorageLocation is the name of the backup storage location - // where the restic repository is stored. + // where the backup repository is stored. BackupStorageLocation string `json:"backupStorageLocation"` - // RepoIdentifier is the restic repository identifier. + // RepoIdentifier is the backup repository identifier. RepoIdentifier string `json:"repoIdentifier"` + // UploaderType is the type of the uploader to handle the data transfer. + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + UploaderType string `json:"uploaderType"` + // SnapshotID is the ID of the volume snapshot to be restored. SnapshotID string `json:"snapshotID"` } @@ -89,6 +94,7 @@ type PodVolumeRestoreStatus struct { // +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.pod.namespace",description="Namespace of the pod containing the volume to be restored" // +kubebuilder:printcolumn:name="Pod",type="string",JSONPath=".spec.pod.name",description="Name of the pod containing the volume to be restored" +// +kubebuilder:printcolumn:name="Uploader Type",type="string",JSONPath=".spec.uploaderType",description="The type of the uploader to handle data transfer" // +kubebuilder:printcolumn:name="Volume",type="string",JSONPath=".spec.volume",description="Name of the volume to be restored" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="Pod Volume Restore status such as New/InProgress" // +kubebuilder:printcolumn:name="TotalBytes",type="integer",format="int64",JSONPath=".status.progress.totalBytes",description="Pod Volume Restore status such as New/InProgress" diff --git a/pkg/apis/velero/v1/register.go b/pkg/apis/velero/v1/register.go index ea7df3b5de..13915293a8 100644 --- a/pkg/apis/velero/v1/register.go +++ b/pkg/apis/velero/v1/register.go @@ -52,7 +52,7 @@ func CustomResources() map[string]typeInfo { "DeleteBackupRequest": newTypeInfo("deletebackuprequests", &DeleteBackupRequest{}, &DeleteBackupRequestList{}), "PodVolumeBackup": newTypeInfo("podvolumebackups", &PodVolumeBackup{}, &PodVolumeBackupList{}), "PodVolumeRestore": newTypeInfo("podvolumerestores", &PodVolumeRestore{}, &PodVolumeRestoreList{}), - "ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}), + "BackupRepository": newTypeInfo("backuprepositories", &BackupRepository{}, &BackupRepositoryList{}), "BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}), "VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}), "ServerStatusRequest": newTypeInfo("serverstatusrequests", &ServerStatusRequest{}, &ServerStatusRequestList{}), diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index c0a8f2c567..a35e5e1ef5 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -107,6 +107,100 @@ func (in *BackupProgress) DeepCopy() *BackupProgress { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepository) DeepCopyInto(out *BackupRepository) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepository. +func (in *BackupRepository) DeepCopy() *BackupRepository { + if in == nil { + return nil + } + out := new(BackupRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupRepository) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositoryList) DeepCopyInto(out *BackupRepositoryList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupRepository, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositoryList. +func (in *BackupRepositoryList) DeepCopy() *BackupRepositoryList { + if in == nil { + return nil + } + out := new(BackupRepositoryList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupRepositoryList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositorySpec) DeepCopyInto(out *BackupRepositorySpec) { + *out = *in + out.MaintenanceFrequency = in.MaintenanceFrequency +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositorySpec. +func (in *BackupRepositorySpec) DeepCopy() *BackupRepositorySpec { + if in == nil { + return nil + } + out := new(BackupRepositorySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositoryStatus) DeepCopyInto(out *BackupRepositoryStatus) { + *out = *in + if in.LastMaintenanceTime != nil { + in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositoryStatus. +func (in *BackupRepositoryStatus) DeepCopy() *BackupRepositoryStatus { + if in == nil { + return nil + } + out := new(BackupRepositoryStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupResourceHook) DeepCopyInto(out *BackupResourceHook) { *out = *in @@ -965,100 +1059,6 @@ func (in *PodVolumeRestoreStatus) DeepCopy() *PodVolumeRestoreStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepository) DeepCopyInto(out *ResticRepository) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepository. -func (in *ResticRepository) DeepCopy() *ResticRepository { - if in == nil { - return nil - } - out := new(ResticRepository) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResticRepository) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositoryList) DeepCopyInto(out *ResticRepositoryList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ResticRepository, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryList. -func (in *ResticRepositoryList) DeepCopy() *ResticRepositoryList { - if in == nil { - return nil - } - out := new(ResticRepositoryList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResticRepositoryList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositorySpec) DeepCopyInto(out *ResticRepositorySpec) { - *out = *in - out.MaintenanceFrequency = in.MaintenanceFrequency -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositorySpec. -func (in *ResticRepositorySpec) DeepCopy() *ResticRepositorySpec { - if in == nil { - return nil - } - out := new(ResticRepositorySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositoryStatus) DeepCopyInto(out *ResticRepositoryStatus) { - *out = *in - if in.LastMaintenanceTime != nil { - in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryStatus. -func (in *ResticRepositoryStatus) DeepCopy() *ResticRepositoryStatus { - if in == nil { - return nil - } - out := new(ResticRepositoryStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Restore) DeepCopyInto(out *Restore) { *out = *in diff --git a/pkg/cmd/cli/backuplocation/delete.go b/pkg/cmd/cli/backuplocation/delete.go index daedf77cd6..1222220f51 100644 --- a/pkg/cmd/cli/backuplocation/delete.go +++ b/pkg/cmd/cli/backuplocation/delete.go @@ -151,8 +151,8 @@ func findAssociatedBackups(client kbclient.Client, bslName, ns string) (velerov1 return backups, err } -func findAssociatedResticRepos(client kbclient.Client, bslName, ns string) (velerov1api.ResticRepositoryList, error) { - var repos velerov1api.ResticRepositoryList +func findAssociatedResticRepos(client kbclient.Client, bslName, ns string) (velerov1api.BackupRepositoryList, error) { + var repos velerov1api.BackupRepositoryList err := client.List(context.Background(), &repos, &kbclient.ListOptions{ Namespace: ns, Raw: &metav1.ListOptions{LabelSelector: bslLabelKey + "=" + bslName}, @@ -172,7 +172,7 @@ func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []err return errs } -func deleteResticRepos(client kbclient.Client, repos velerov1api.ResticRepositoryList) []error { +func deleteResticRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { var errs []error for _, repo := range repos.Items { if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { diff --git a/pkg/cmd/cli/restic/repo/get.go b/pkg/cmd/cli/restic/repo/get.go index 8fd848fd4c..24692f3552 100644 --- a/pkg/cmd/cli/restic/repo/get.go +++ b/pkg/cmd/cli/restic/repo/get.go @@ -41,16 +41,16 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { veleroClient, err := f.Client() cmd.CheckError(err) - var repos *api.ResticRepositoryList + var repos *api.BackupRepositoryList if len(args) > 0 { - repos = new(api.ResticRepositoryList) + repos = new(api.BackupRepositoryList) for _, name := range args { - repo, err := veleroClient.VeleroV1().ResticRepositories(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) + repo, err := veleroClient.VeleroV1().BackupRepositories(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) cmd.CheckError(err) repos.Items = append(repos.Items, *repo) } } else { - repos, err = veleroClient.VeleroV1().ResticRepositories(f.Namespace()).List(context.TODO(), listOptions) + repos, err = veleroClient.VeleroV1().BackupRepositories(f.Namespace()).List(context.TODO(), listOptions) cmd.CheckError(err) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3c83ba75a3..f257c96e72 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -527,7 +527,7 @@ func (s *server) initRestic() error { s.ctx, s.namespace, s.veleroClient, - s.sharedInformerFactory.Velero().V1().ResticRepositories(), + s.sharedInformerFactory.Velero().V1().BackupRepositories(), s.veleroClient.VeleroV1(), s.mgr.GetClient(), s.kubeClient.CoreV1(), diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index 24188ed0d7..ab3f7a95d6 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -177,15 +177,15 @@ func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) { ColumnDefinitions: scheduleColumns, Rows: printScheduleList(obj.(*velerov1api.ScheduleList)), } - case *velerov1api.ResticRepository: + case *velerov1api.BackupRepository: table = &metav1.Table{ ColumnDefinitions: resticRepoColumns, - Rows: printResticRepo(obj.(*velerov1api.ResticRepository)), + Rows: printResticRepo(obj.(*velerov1api.BackupRepository)), } - case *velerov1api.ResticRepositoryList: + case *velerov1api.BackupRepositoryList: table = &metav1.Table{ ColumnDefinitions: resticRepoColumns, - Rows: printResticRepoList(obj.(*velerov1api.ResticRepositoryList)), + Rows: printResticRepoList(obj.(*velerov1api.BackupRepositoryList)), } case *velerov1api.BackupStorageLocation: table = &metav1.Table{ diff --git a/pkg/cmd/util/output/restic_repo_printer.go b/pkg/cmd/util/output/restic_repo_printer.go index 803a3486f4..fd6766087f 100644 --- a/pkg/cmd/util/output/restic_repo_printer.go +++ b/pkg/cmd/util/output/restic_repo_printer.go @@ -33,7 +33,7 @@ var ( } ) -func printResticRepoList(list *v1.ResticRepositoryList) []metav1.TableRow { +func printResticRepoList(list *v1.BackupRepositoryList) []metav1.TableRow { rows := make([]metav1.TableRow, 0, len(list.Items)) for i := range list.Items { @@ -42,14 +42,14 @@ func printResticRepoList(list *v1.ResticRepositoryList) []metav1.TableRow { return rows } -func printResticRepo(repo *v1.ResticRepository) []metav1.TableRow { +func printResticRepo(repo *v1.BackupRepository) []metav1.TableRow { row := metav1.TableRow{ Object: runtime.RawExtension{Object: repo}, } status := repo.Status.Phase if status == "" { - status = v1.ResticRepositoryPhaseNew + status = v1.BackupRepositoryPhaseNew } var lastMaintenance string diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index d4d0ef68df..36f0e76a86 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -68,16 +68,16 @@ func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client } func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.ResticRepositoryList{}, repoSyncPeriod) + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.ResticRepository{}). + For(&velerov1api.BackupRepository{}). Watches(s, nil). Complete(r) } func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.logger.WithField("resticRepo", req.String()) - resticRepo := &velerov1api.ResticRepository{} + resticRepo := &velerov1api.BackupRepository{} if err := r.Get(ctx, req.NamespacedName, resticRepo); err != nil { if apierrors.IsNotFound(err) { log.Warnf("restic repository %s in namespace %s is not found", req.Name, req.Namespace) @@ -87,7 +87,7 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.ResticRepositoryPhaseNew { + if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { if err := r.initializeRepo(ctx, resticRepo, log); err != nil { log.WithError(err).Error("error initialize repository") return ctrl.Result{}, errors.WithStack(err) @@ -105,16 +105,16 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) } switch resticRepo.Status.Phase { - case velerov1api.ResticRepositoryPhaseReady: + case velerov1api.BackupRepositoryPhaseReady: return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, log) - case velerov1api.ResticRepositoryPhaseNotReady: + case velerov1api.BackupRepositoryPhaseNotReady: return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, log) } return ctrl.Result{}, nil } -func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid @@ -129,9 +129,9 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() - rr.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady + rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady if rr.Spec.MaintenanceFrequency.Duration <= 0 { rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} @@ -140,7 +140,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Spec.ResticIdentifier = repoIdentifier if rr.Spec.MaintenanceFrequency.Duration <= 0 { @@ -154,8 +154,8 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { - rr.Status.Phase = velerov1api.ResticRepositoryPhaseReady + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + rr.Status.Phase = velerov1api.BackupRepositoryPhaseReady rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) } @@ -163,7 +163,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 // ensureRepo checks to see if a repository exists, and attempts to initialize it if // it does not exist. An error is returned if the repository can't be connected to // or initialized. -func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.RepositoryManager) error { +func ensureRepo(repo *velerov1api.BackupRepository, repoManager restic.RepositoryManager) error { if err := repoManager.ConnectToRepo(repo); err != nil { // If the repository has not yet been initialized, the error message will always include // the following string. This is the only scenario where we should try to initialize it. @@ -179,7 +179,7 @@ func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.Repositor return nil } -func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { log.Debug("resticRepositoryController.runMaintenanceIfDue") now := r.clock.Now() @@ -196,21 +196,21 @@ func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *vel log.Debug("Pruning repo") if err := r.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() }) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } -func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool { +func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool { return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now) } -func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil @@ -226,16 +226,16 @@ func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *veler return r.patchResticRepository(ctx, req, repoReady()) } -func repoNotReady(msg string) func(*velerov1api.ResticRepository) { - return func(r *velerov1api.ResticRepository) { - r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady +func repoNotReady(msg string) func(*velerov1api.BackupRepository) { + return func(r *velerov1api.BackupRepository) { + r.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady r.Status.Message = msg } } -func repoReady() func(*velerov1api.ResticRepository) { - return func(r *velerov1api.ResticRepository) { - r.Status.Phase = velerov1api.ResticRepositoryPhaseReady +func repoReady() func(*velerov1api.BackupRepository) { + return func(r *velerov1api.BackupRepository) { + r.Status.Phase = velerov1api.BackupRepositoryPhaseReady r.Status.Message = "" } } @@ -243,7 +243,7 @@ func repoReady() func(*velerov1api.ResticRepository) { // patchResticRepository mutates req with the provided mutate function, and patches it // through the Kube API. After executing this function, req will be updated with both // the mutation and the results of the Patch() API call. -func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error { +func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.BackupRepository, mutate func(*velerov1api.BackupRepository)) error { original := req.DeepCopy() mutate(req) if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil { diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index 2e6b4308a3..28e899329c 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -30,7 +30,7 @@ import ( const defaultMaintenanceFrequency = 10 * time.Minute -func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { +func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { mgr := &resticmokes.RepositoryManager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) @@ -44,13 +44,13 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mo ) } -func mockResticRepositoryCR() *velerov1api.ResticRepository { - return &velerov1api.ResticRepository{ +func mockResticRepositoryCR() *velerov1api.BackupRepository { + return &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, } @@ -64,10 +64,10 @@ func TestPatchResticRepository(t *testing.T) { assert.NoError(t, err) err = reconciler.patchResticRepository(context.Background(), rr, repoReady()) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) err = reconciler.patchResticRepository(context.Background(), rr, repoNotReady("not ready")) assert.NoError(t, err) - assert.NotEqual(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.NotEqual(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestCheckNotReadyRepo(t *testing.T) { @@ -77,11 +77,11 @@ func TestCheckNotReadyRepo(t *testing.T) { assert.NoError(t, err) err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhase("")) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhase("")) rr.Spec.ResticIdentifier = "s3:test.amazonaws.com/bucket/restic" err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestRunMaintenanceIfDue(t *testing.T) { @@ -121,23 +121,23 @@ func TestInitializeRepo(t *testing.T) { assert.NoError(t, err) err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestResticRepoReconcile(t *testing.T) { tests := []struct { name string - repo *velerov1api.ResticRepository + repo *velerov1api.BackupRepository expectNil bool }{ { name: "test on api server not found", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "unknown", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, }, @@ -145,12 +145,12 @@ func TestResticRepoReconcile(t *testing.T) { }, { name: "test on initialize repo", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, }, @@ -158,16 +158,16 @@ func TestResticRepoReconcile(t *testing.T) { }, { name: "test on repo with new phase", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, - Status: velerov1api.ResticRepositoryStatus{ - Phase: velerov1api.ResticRepositoryPhaseNew, + Status: velerov1api.BackupRepositoryStatus{ + Phase: velerov1api.BackupRepositoryPhaseNew, }, }, expectNil: true, diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 092b90002f..3799259fad 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -71,10 +71,14 @@ var nonRestorableResources = []string{ // https://github.com/vmware-tanzu/velero/issues/622 "restores.velero.io", + // TODO: Remove this in v1.11 or v1.12 // Restic repositories are automatically managed by Velero and will be automatically // created as needed if they don't exist. // https://github.com/vmware-tanzu/velero/issues/1113 "resticrepositories.velero.io", + + // Backup repositories were renamed from Restic repositories + "backuprepositories.velero.io", } type restoreController struct { diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go similarity index 53% rename from pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go rename to pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go index 44d5c0760b..7ecef6dcf6 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go @@ -30,46 +30,46 @@ import ( rest "k8s.io/client-go/rest" ) -// ResticRepositoriesGetter has a method to return a ResticRepositoryInterface. +// BackupRepositoriesGetter has a method to return a BackupRepositoryInterface. // A group's client should implement this interface. -type ResticRepositoriesGetter interface { - ResticRepositories(namespace string) ResticRepositoryInterface +type BackupRepositoriesGetter interface { + BackupRepositories(namespace string) BackupRepositoryInterface } -// ResticRepositoryInterface has methods to work with ResticRepository resources. -type ResticRepositoryInterface interface { - Create(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.CreateOptions) (*v1.ResticRepository, error) - Update(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (*v1.ResticRepository, error) - UpdateStatus(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (*v1.ResticRepository, error) +// BackupRepositoryInterface has methods to work with BackupRepository resources. +type BackupRepositoryInterface interface { + Create(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.CreateOptions) (*v1.BackupRepository, error) + Update(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (*v1.BackupRepository, error) + UpdateStatus(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (*v1.BackupRepository, error) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ResticRepository, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.ResticRepositoryList, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.BackupRepository, error) + List(ctx context.Context, opts metav1.ListOptions) (*v1.BackupRepositoryList, error) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ResticRepository, err error) - ResticRepositoryExpansion + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupRepository, err error) + BackupRepositoryExpansion } -// resticRepositories implements ResticRepositoryInterface -type resticRepositories struct { +// backupRepositories implements BackupRepositoryInterface +type backupRepositories struct { client rest.Interface ns string } -// newResticRepositories returns a ResticRepositories -func newResticRepositories(c *VeleroV1Client, namespace string) *resticRepositories { - return &resticRepositories{ +// newBackupRepositories returns a BackupRepositories +func newBackupRepositories(c *VeleroV1Client, namespace string) *backupRepositories { + return &backupRepositories{ client: c.RESTClient(), ns: namespace, } } -// Get takes name of the resticRepository, and returns the corresponding resticRepository object, and an error if there is any. -func (c *resticRepositories) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// Get takes name of the backupRepository, and returns the corresponding backupRepository object, and an error if there is any. +func (c *backupRepositories) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.BackupRepository, err error) { + result = &v1.BackupRepository{} err = c.client.Get(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). Name(name). VersionedParams(&options, scheme.ParameterCodec). Do(ctx). @@ -77,16 +77,16 @@ func (c *resticRepositories) Get(ctx context.Context, name string, options metav return } -// List takes label and field selectors, and returns the list of ResticRepositories that match those selectors. -func (c *resticRepositories) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ResticRepositoryList, err error) { +// List takes label and field selectors, and returns the list of BackupRepositories that match those selectors. +func (c *backupRepositories) List(ctx context.Context, opts metav1.ListOptions) (result *v1.BackupRepositoryList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } - result = &v1.ResticRepositoryList{} + result = &v1.BackupRepositoryList{} err = c.client.Get(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Do(ctx). @@ -94,8 +94,8 @@ func (c *resticRepositories) List(ctx context.Context, opts metav1.ListOptions) return } -// Watch returns a watch.Interface that watches the requested resticRepositories. -func (c *resticRepositories) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { +// Watch returns a watch.Interface that watches the requested backupRepositories. +func (c *backupRepositories) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second @@ -103,34 +103,34 @@ func (c *resticRepositories) Watch(ctx context.Context, opts metav1.ListOptions) opts.Watch = true return c.client.Get(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Watch(ctx) } -// Create takes the representation of a resticRepository and creates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *resticRepositories) Create(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.CreateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// Create takes the representation of a backupRepository and creates it. Returns the server's representation of the backupRepository, and an error, if there is any. +func (c *backupRepositories) Create(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.CreateOptions) (result *v1.BackupRepository, err error) { + result = &v1.BackupRepository{} err = c.client.Post(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return } -// Update takes the representation of a resticRepository and updates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *resticRepositories) Update(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// Update takes the representation of a backupRepository and updates it. Returns the server's representation of the backupRepository, and an error, if there is any. +func (c *backupRepositories) Update(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (result *v1.BackupRepository, err error) { + result = &v1.BackupRepository{} err = c.client.Put(). Namespace(c.ns). - Resource("resticrepositories"). - Name(resticRepository.Name). + Resource("backuprepositories"). + Name(backupRepository.Name). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return @@ -138,25 +138,25 @@ func (c *resticRepositories) Update(ctx context.Context, resticRepository *v1.Re // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *resticRepositories) UpdateStatus(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +func (c *backupRepositories) UpdateStatus(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (result *v1.BackupRepository, err error) { + result = &v1.BackupRepository{} err = c.client.Put(). Namespace(c.ns). - Resource("resticrepositories"). - Name(resticRepository.Name). + Resource("backuprepositories"). + Name(backupRepository.Name). SubResource("status"). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return } -// Delete takes name of the resticRepository and deletes it. Returns an error if one occurs. -func (c *resticRepositories) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { +// Delete takes name of the backupRepository and deletes it. Returns an error if one occurs. +func (c *backupRepositories) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { return c.client.Delete(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). Name(name). Body(&opts). Do(ctx). @@ -164,14 +164,14 @@ func (c *resticRepositories) Delete(ctx context.Context, name string, opts metav } // DeleteCollection deletes a collection of objects. -func (c *resticRepositories) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { +func (c *backupRepositories) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { var timeout time.Duration if listOpts.TimeoutSeconds != nil { timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second } return c.client.Delete(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&listOpts, scheme.ParameterCodec). Timeout(timeout). Body(&opts). @@ -179,12 +179,12 @@ func (c *resticRepositories) DeleteCollection(ctx context.Context, opts metav1.D Error() } -// Patch applies the patch and returns the patched resticRepository. -func (c *resticRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// Patch applies the patch and returns the patched backupRepository. +func (c *backupRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupRepository, err error) { + result = &v1.BackupRepository{} err = c.client.Patch(pt). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). Name(name). SubResource(subresources...). VersionedParams(&opts, scheme.ParameterCodec). diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go new file mode 100644 index 0000000000..ef9d6b41c8 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go @@ -0,0 +1,142 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeBackupRepositories implements BackupRepositoryInterface +type FakeBackupRepositories struct { + Fake *FakeVeleroV1 + ns string +} + +var backuprepositoriesResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backuprepositories"} + +var backuprepositoriesKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupRepository"} + +// Get takes name of the backupRepository, and returns the corresponding backupRepository object, and an error if there is any. +func (c *FakeBackupRepositories) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.BackupRepository, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(backuprepositoriesResource, c.ns, name), &velerov1.BackupRepository{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupRepository), err +} + +// List takes label and field selectors, and returns the list of BackupRepositories that match those selectors. +func (c *FakeBackupRepositories) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.BackupRepositoryList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(backuprepositoriesResource, backuprepositoriesKind, c.ns, opts), &velerov1.BackupRepositoryList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &velerov1.BackupRepositoryList{ListMeta: obj.(*velerov1.BackupRepositoryList).ListMeta} + for _, item := range obj.(*velerov1.BackupRepositoryList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested backupRepositories. +func (c *FakeBackupRepositories) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(backuprepositoriesResource, c.ns, opts)) + +} + +// Create takes the representation of a backupRepository and creates it. Returns the server's representation of the backupRepository, and an error, if there is any. +func (c *FakeBackupRepositories) Create(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.CreateOptions) (result *velerov1.BackupRepository, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(backuprepositoriesResource, c.ns, backupRepository), &velerov1.BackupRepository{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupRepository), err +} + +// Update takes the representation of a backupRepository and updates it. Returns the server's representation of the backupRepository, and an error, if there is any. +func (c *FakeBackupRepositories) Update(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.UpdateOptions) (result *velerov1.BackupRepository, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(backuprepositoriesResource, c.ns, backupRepository), &velerov1.BackupRepository{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupRepository), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeBackupRepositories) UpdateStatus(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.UpdateOptions) (*velerov1.BackupRepository, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(backuprepositoriesResource, "status", c.ns, backupRepository), &velerov1.BackupRepository{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupRepository), err +} + +// Delete takes name of the backupRepository and deletes it. Returns an error if one occurs. +func (c *FakeBackupRepositories) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(backuprepositoriesResource, c.ns, name), &velerov1.BackupRepository{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeBackupRepositories) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(backuprepositoriesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &velerov1.BackupRepositoryList{}) + return err +} + +// Patch applies the patch and returns the patched backupRepository. +func (c *FakeBackupRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.BackupRepository, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(backuprepositoriesResource, c.ns, name, pt, data, subresources...), &velerov1.BackupRepository{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupRepository), err +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.go deleted file mode 100644 index aeda0c9cb6..0000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeResticRepositories implements ResticRepositoryInterface -type FakeResticRepositories struct { - Fake *FakeVeleroV1 - ns string -} - -var resticrepositoriesResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "resticrepositories"} - -var resticrepositoriesKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "ResticRepository"} - -// Get takes name of the resticRepository, and returns the corresponding resticRepository object, and an error if there is any. -func (c *FakeResticRepositories) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(resticrepositoriesResource, c.ns, name), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// List takes label and field selectors, and returns the list of ResticRepositories that match those selectors. -func (c *FakeResticRepositories) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.ResticRepositoryList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(resticrepositoriesResource, resticrepositoriesKind, c.ns, opts), &velerov1.ResticRepositoryList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.ResticRepositoryList{ListMeta: obj.(*velerov1.ResticRepositoryList).ListMeta} - for _, item := range obj.(*velerov1.ResticRepositoryList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested resticRepositories. -func (c *FakeResticRepositories) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(resticrepositoriesResource, c.ns, opts)) - -} - -// Create takes the representation of a resticRepository and creates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *FakeResticRepositories) Create(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.CreateOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(resticrepositoriesResource, c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// Update takes the representation of a resticRepository and updates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *FakeResticRepositories) Update(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.UpdateOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(resticrepositoriesResource, c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeResticRepositories) UpdateStatus(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.UpdateOptions) (*velerov1.ResticRepository, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(resticrepositoriesResource, "status", c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// Delete takes name of the resticRepository and deletes it. Returns an error if one occurs. -func (c *FakeResticRepositories) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(resticrepositoriesResource, c.ns, name), &velerov1.ResticRepository{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeResticRepositories) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(resticrepositoriesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.ResticRepositoryList{}) - return err -} - -// Patch applies the patch and returns the patched resticRepository. -func (c *FakeResticRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(resticrepositoriesResource, c.ns, name, pt, data, subresources...), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go index e901158134..444c1f89f1 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go @@ -32,6 +32,10 @@ func (c *FakeVeleroV1) Backups(namespace string) v1.BackupInterface { return &FakeBackups{c, namespace} } +func (c *FakeVeleroV1) BackupRepositories(namespace string) v1.BackupRepositoryInterface { + return &FakeBackupRepositories{c, namespace} +} + func (c *FakeVeleroV1) BackupStorageLocations(namespace string) v1.BackupStorageLocationInterface { return &FakeBackupStorageLocations{c, namespace} } @@ -52,10 +56,6 @@ func (c *FakeVeleroV1) PodVolumeRestores(namespace string) v1.PodVolumeRestoreIn return &FakePodVolumeRestores{c, namespace} } -func (c *FakeVeleroV1) ResticRepositories(namespace string) v1.ResticRepositoryInterface { - return &FakeResticRepositories{c, namespace} -} - func (c *FakeVeleroV1) Restores(namespace string) v1.RestoreInterface { return &FakeRestores{c, namespace} } diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go index 5deaaa51af..5032fd6a4e 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go @@ -20,6 +20,8 @@ package v1 type BackupExpansion interface{} +type BackupRepositoryExpansion interface{} + type BackupStorageLocationExpansion interface{} type DeleteBackupRequestExpansion interface{} @@ -30,8 +32,6 @@ type PodVolumeBackupExpansion interface{} type PodVolumeRestoreExpansion interface{} -type ResticRepositoryExpansion interface{} - type RestoreExpansion interface{} type ScheduleExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go index 5758967efa..39f85628c2 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go @@ -27,12 +27,12 @@ import ( type VeleroV1Interface interface { RESTClient() rest.Interface BackupsGetter + BackupRepositoriesGetter BackupStorageLocationsGetter DeleteBackupRequestsGetter DownloadRequestsGetter PodVolumeBackupsGetter PodVolumeRestoresGetter - ResticRepositoriesGetter RestoresGetter SchedulesGetter ServerStatusRequestsGetter @@ -48,6 +48,10 @@ func (c *VeleroV1Client) Backups(namespace string) BackupInterface { return newBackups(c, namespace) } +func (c *VeleroV1Client) BackupRepositories(namespace string) BackupRepositoryInterface { + return newBackupRepositories(c, namespace) +} + func (c *VeleroV1Client) BackupStorageLocations(namespace string) BackupStorageLocationInterface { return newBackupStorageLocations(c, namespace) } @@ -68,10 +72,6 @@ func (c *VeleroV1Client) PodVolumeRestores(namespace string) PodVolumeRestoreInt return newPodVolumeRestores(c, namespace) } -func (c *VeleroV1Client) ResticRepositories(namespace string) ResticRepositoryInterface { - return newResticRepositories(c, namespace) -} - func (c *VeleroV1Client) Restores(namespace string) RestoreInterface { return newRestores(c, namespace) } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 15770dcebb..6058870247 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -55,6 +55,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=velero.io, Version=v1 case v1.SchemeGroupVersion.WithResource("backups"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Backups().Informer()}, nil + case v1.SchemeGroupVersion.WithResource("backuprepositories"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupRepositories().Informer()}, nil case v1.SchemeGroupVersion.WithResource("backupstoragelocations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupStorageLocations().Informer()}, nil case v1.SchemeGroupVersion.WithResource("deletebackuprequests"): @@ -65,8 +67,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().PodVolumeBackups().Informer()}, nil case v1.SchemeGroupVersion.WithResource("podvolumerestores"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().PodVolumeRestores().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("resticrepositories"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().ResticRepositories().Informer()}, nil case v1.SchemeGroupVersion.WithResource("restores"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Restores().Informer()}, nil case v1.SchemeGroupVersion.WithResource("schedules"): diff --git a/pkg/generated/informers/externalversions/velero/v1/resticrepository.go b/pkg/generated/informers/externalversions/velero/v1/backuprepository.go similarity index 70% rename from pkg/generated/informers/externalversions/velero/v1/resticrepository.go rename to pkg/generated/informers/externalversions/velero/v1/backuprepository.go index f92565554b..59865c8943 100644 --- a/pkg/generated/informers/externalversions/velero/v1/resticrepository.go +++ b/pkg/generated/informers/externalversions/velero/v1/backuprepository.go @@ -32,59 +32,59 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// ResticRepositoryInformer provides access to a shared informer and lister for -// ResticRepositories. -type ResticRepositoryInformer interface { +// BackupRepositoryInformer provides access to a shared informer and lister for +// BackupRepositories. +type BackupRepositoryInformer interface { Informer() cache.SharedIndexInformer - Lister() v1.ResticRepositoryLister + Lister() v1.BackupRepositoryLister } -type resticRepositoryInformer struct { +type backupRepositoryInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc namespace string } -// NewResticRepositoryInformer constructs a new informer for ResticRepository type. +// NewBackupRepositoryInformer constructs a new informer for BackupRepository type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewResticRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredResticRepositoryInformer(client, namespace, resyncPeriod, indexers, nil) +func NewBackupRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredBackupRepositoryInformer(client, namespace, resyncPeriod, indexers, nil) } -// NewFilteredResticRepositoryInformer constructs a new informer for ResticRepository type. +// NewFilteredBackupRepositoryInformer constructs a new informer for BackupRepository type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredResticRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredBackupRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VeleroV1().ResticRepositories(namespace).List(context.TODO(), options) + return client.VeleroV1().BackupRepositories(namespace).List(context.TODO(), options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VeleroV1().ResticRepositories(namespace).Watch(context.TODO(), options) + return client.VeleroV1().BackupRepositories(namespace).Watch(context.TODO(), options) }, }, - &velerov1.ResticRepository{}, + &velerov1.BackupRepository{}, resyncPeriod, indexers, ) } -func (f *resticRepositoryInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredResticRepositoryInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *backupRepositoryInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredBackupRepositoryInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *resticRepositoryInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.ResticRepository{}, f.defaultInformer) +func (f *backupRepositoryInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&velerov1.BackupRepository{}, f.defaultInformer) } -func (f *resticRepositoryInformer) Lister() v1.ResticRepositoryLister { - return v1.NewResticRepositoryLister(f.Informer().GetIndexer()) +func (f *backupRepositoryInformer) Lister() v1.BackupRepositoryLister { + return v1.NewBackupRepositoryLister(f.Informer().GetIndexer()) } diff --git a/pkg/generated/informers/externalversions/velero/v1/interface.go b/pkg/generated/informers/externalversions/velero/v1/interface.go index 981470c409..087dd33563 100644 --- a/pkg/generated/informers/externalversions/velero/v1/interface.go +++ b/pkg/generated/informers/externalversions/velero/v1/interface.go @@ -26,6 +26,8 @@ import ( type Interface interface { // Backups returns a BackupInformer. Backups() BackupInformer + // BackupRepositories returns a BackupRepositoryInformer. + BackupRepositories() BackupRepositoryInformer // BackupStorageLocations returns a BackupStorageLocationInformer. BackupStorageLocations() BackupStorageLocationInformer // DeleteBackupRequests returns a DeleteBackupRequestInformer. @@ -36,8 +38,6 @@ type Interface interface { PodVolumeBackups() PodVolumeBackupInformer // PodVolumeRestores returns a PodVolumeRestoreInformer. PodVolumeRestores() PodVolumeRestoreInformer - // ResticRepositories returns a ResticRepositoryInformer. - ResticRepositories() ResticRepositoryInformer // Restores returns a RestoreInformer. Restores() RestoreInformer // Schedules returns a ScheduleInformer. @@ -64,6 +64,11 @@ func (v *version) Backups() BackupInformer { return &backupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// BackupRepositories returns a BackupRepositoryInformer. +func (v *version) BackupRepositories() BackupRepositoryInformer { + return &backupRepositoryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // BackupStorageLocations returns a BackupStorageLocationInformer. func (v *version) BackupStorageLocations() BackupStorageLocationInformer { return &backupStorageLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} @@ -89,11 +94,6 @@ func (v *version) PodVolumeRestores() PodVolumeRestoreInformer { return &podVolumeRestoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// ResticRepositories returns a ResticRepositoryInformer. -func (v *version) ResticRepositories() ResticRepositoryInformer { - return &resticRepositoryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - // Restores returns a RestoreInformer. func (v *version) Restores() RestoreInformer { return &restoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/generated/listers/velero/v1/backuprepository.go b/pkg/generated/listers/velero/v1/backuprepository.go new file mode 100644 index 0000000000..ef619baf10 --- /dev/null +++ b/pkg/generated/listers/velero/v1/backuprepository.go @@ -0,0 +1,99 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// BackupRepositoryLister helps list BackupRepositories. +// All objects returned here must be treated as read-only. +type BackupRepositoryLister interface { + // List lists all BackupRepositories in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.BackupRepository, err error) + // BackupRepositories returns an object that can list and get BackupRepositories. + BackupRepositories(namespace string) BackupRepositoryNamespaceLister + BackupRepositoryListerExpansion +} + +// backupRepositoryLister implements the BackupRepositoryLister interface. +type backupRepositoryLister struct { + indexer cache.Indexer +} + +// NewBackupRepositoryLister returns a new BackupRepositoryLister. +func NewBackupRepositoryLister(indexer cache.Indexer) BackupRepositoryLister { + return &backupRepositoryLister{indexer: indexer} +} + +// List lists all BackupRepositories in the indexer. +func (s *backupRepositoryLister) List(selector labels.Selector) (ret []*v1.BackupRepository, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.BackupRepository)) + }) + return ret, err +} + +// BackupRepositories returns an object that can list and get BackupRepositories. +func (s *backupRepositoryLister) BackupRepositories(namespace string) BackupRepositoryNamespaceLister { + return backupRepositoryNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// BackupRepositoryNamespaceLister helps list and get BackupRepositories. +// All objects returned here must be treated as read-only. +type BackupRepositoryNamespaceLister interface { + // List lists all BackupRepositories in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.BackupRepository, err error) + // Get retrieves the BackupRepository from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1.BackupRepository, error) + BackupRepositoryNamespaceListerExpansion +} + +// backupRepositoryNamespaceLister implements the BackupRepositoryNamespaceLister +// interface. +type backupRepositoryNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all BackupRepositories in the indexer for a given namespace. +func (s backupRepositoryNamespaceLister) List(selector labels.Selector) (ret []*v1.BackupRepository, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.BackupRepository)) + }) + return ret, err +} + +// Get retrieves the BackupRepository from the indexer for a given namespace and name. +func (s backupRepositoryNamespaceLister) Get(name string) (*v1.BackupRepository, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("backuprepository"), name) + } + return obj.(*v1.BackupRepository), nil +} diff --git a/pkg/generated/listers/velero/v1/expansion_generated.go b/pkg/generated/listers/velero/v1/expansion_generated.go index b57656650d..c0cd576546 100644 --- a/pkg/generated/listers/velero/v1/expansion_generated.go +++ b/pkg/generated/listers/velero/v1/expansion_generated.go @@ -26,6 +26,14 @@ type BackupListerExpansion interface{} // BackupNamespaceLister. type BackupNamespaceListerExpansion interface{} +// BackupRepositoryListerExpansion allows custom methods to be added to +// BackupRepositoryLister. +type BackupRepositoryListerExpansion interface{} + +// BackupRepositoryNamespaceListerExpansion allows custom methods to be added to +// BackupRepositoryNamespaceLister. +type BackupRepositoryNamespaceListerExpansion interface{} + // BackupStorageLocationListerExpansion allows custom methods to be added to // BackupStorageLocationLister. type BackupStorageLocationListerExpansion interface{} @@ -66,14 +74,6 @@ type PodVolumeRestoreListerExpansion interface{} // PodVolumeRestoreNamespaceLister. type PodVolumeRestoreNamespaceListerExpansion interface{} -// ResticRepositoryListerExpansion allows custom methods to be added to -// ResticRepositoryLister. -type ResticRepositoryListerExpansion interface{} - -// ResticRepositoryNamespaceListerExpansion allows custom methods to be added to -// ResticRepositoryNamespaceLister. -type ResticRepositoryNamespaceListerExpansion interface{} - // RestoreListerExpansion allows custom methods to be added to // RestoreLister. type RestoreListerExpansion interface{} diff --git a/pkg/generated/listers/velero/v1/resticrepository.go b/pkg/generated/listers/velero/v1/resticrepository.go deleted file mode 100644 index 96bcfdc7c8..0000000000 --- a/pkg/generated/listers/velero/v1/resticrepository.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ResticRepositoryLister helps list ResticRepositories. -// All objects returned here must be treated as read-only. -type ResticRepositoryLister interface { - // List lists all ResticRepositories in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ResticRepository, err error) - // ResticRepositories returns an object that can list and get ResticRepositories. - ResticRepositories(namespace string) ResticRepositoryNamespaceLister - ResticRepositoryListerExpansion -} - -// resticRepositoryLister implements the ResticRepositoryLister interface. -type resticRepositoryLister struct { - indexer cache.Indexer -} - -// NewResticRepositoryLister returns a new ResticRepositoryLister. -func NewResticRepositoryLister(indexer cache.Indexer) ResticRepositoryLister { - return &resticRepositoryLister{indexer: indexer} -} - -// List lists all ResticRepositories in the indexer. -func (s *resticRepositoryLister) List(selector labels.Selector) (ret []*v1.ResticRepository, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResticRepository)) - }) - return ret, err -} - -// ResticRepositories returns an object that can list and get ResticRepositories. -func (s *resticRepositoryLister) ResticRepositories(namespace string) ResticRepositoryNamespaceLister { - return resticRepositoryNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ResticRepositoryNamespaceLister helps list and get ResticRepositories. -// All objects returned here must be treated as read-only. -type ResticRepositoryNamespaceLister interface { - // List lists all ResticRepositories in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ResticRepository, err error) - // Get retrieves the ResticRepository from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.ResticRepository, error) - ResticRepositoryNamespaceListerExpansion -} - -// resticRepositoryNamespaceLister implements the ResticRepositoryNamespaceLister -// interface. -type resticRepositoryNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ResticRepositories in the indexer for a given namespace. -func (s resticRepositoryNamespaceLister) List(selector labels.Selector) (ret []*v1.ResticRepository, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResticRepository)) - }) - return ret, err -} - -// Get retrieves the ResticRepository from the indexer for a given namespace and name. -func (s resticRepositoryNamespaceLister) Get(name string) (*v1.ResticRepository, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("resticrepository"), name) - } - return obj.(*v1.ResticRepository), nil -} diff --git a/pkg/restic/backupper.go b/pkg/restic/backupper.go index fd366a36bc..589b396f39 100644 --- a/pkg/restic/backupper.go +++ b/pkg/restic/backupper.go @@ -176,8 +176,8 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. log.Warnf("Volume %s is declared in pod %s/%s but not mounted by any container, skipping", volumeName, pod.Namespace, pod.Name) continue } - - volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, pvc) + // TODO: Remove the hard-coded uploader type before v1.10 FC + volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, "restic", pvc) if volumeBackup, err = b.repoManager.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { errs = append(errs, err) continue @@ -236,7 +236,7 @@ func isHostPathVolume(volume *corev1api.Volume, pvc *corev1api.PersistentVolumeC return pv.Spec.HostPath != nil, nil } -func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume corev1api.Volume, repoIdentifier string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeBackup { +func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume corev1api.Volume, repoIdentifier, uploaderType string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeBackup { pvb := &velerov1api.PodVolumeBackup{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, @@ -274,6 +274,7 @@ func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume c }, BackupStorageLocation: backup.Spec.StorageLocation, RepoIdentifier: repoIdentifier, + UploaderType: uploaderType, }, } diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/restic/mocks/repository_manager.go index b164cb3e00..de8770c375 100644 --- a/pkg/restic/mocks/repository_manager.go +++ b/pkg/restic/mocks/repository_manager.go @@ -31,11 +31,11 @@ type RepositoryManager struct { } // ConnectToRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) ConnectToRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) ConnectToRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -59,11 +59,11 @@ func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 restic.SnapshotIden } // InitRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) InitRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -119,11 +119,11 @@ func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) ( } // PruneRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) PruneRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) PruneRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -133,11 +133,11 @@ func (_m *RepositoryManager) PruneRepo(repo *v1.ResticRepository) error { } // UnlockRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) UnlockRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) UnlockRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) diff --git a/pkg/restic/repository_ensurer.go b/pkg/restic/repository_ensurer.go index f1f4f168a3..d764a49c7d 100644 --- a/pkg/restic/repository_ensurer.go +++ b/pkg/restic/repository_ensurer.go @@ -38,11 +38,11 @@ import ( // repositoryEnsurer ensures that Velero restic repositories are created and ready. type repositoryEnsurer struct { log logrus.FieldLogger - repoLister velerov1listers.ResticRepositoryLister - repoClient velerov1client.ResticRepositoriesGetter + repoLister velerov1listers.BackupRepositoryLister + repoClient velerov1client.BackupRepositoriesGetter repoChansLock sync.Mutex - repoChans map[string]chan *velerov1api.ResticRepository + repoChans map[string]chan *velerov1api.BackupRepository // repoLocksMu synchronizes reads/writes to the repoLocks map itself // since maps are not threadsafe. @@ -55,20 +55,20 @@ type repoKey struct { backupLocation string } -func newRepositoryEnsurer(repoInformer velerov1informers.ResticRepositoryInformer, repoClient velerov1client.ResticRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { +func newRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { r := &repositoryEnsurer{ log: log, repoLister: repoInformer.Lister(), repoClient: repoClient, - repoChans: make(map[string]chan *velerov1api.ResticRepository), + repoChans: make(map[string]chan *velerov1api.BackupRepository), repoLocks: make(map[repoKey]*sync.Mutex), } repoInformer.Informer().AddEventHandler( cache.ResourceEventHandlerFuncs{ UpdateFunc: func(old, upd interface{}) { - oldObj := old.(*velerov1api.ResticRepository) - newObj := upd.(*velerov1api.ResticRepository) + oldObj := old.(*velerov1api.BackupRepository) + newObj := upd.(*velerov1api.BackupRepository) // we're only interested in phase-changing updates if oldObj.Status.Phase == newObj.Status.Phase { @@ -76,7 +76,7 @@ func newRepositoryEnsurer(repoInformer velerov1informers.ResticRepositoryInforme } // we're only interested in updates where the updated object is either Ready or NotReady - if newObj.Status.Phase != velerov1api.ResticRepositoryPhaseReady && newObj.Status.Phase != velerov1api.ResticRepositoryPhaseNotReady { + if newObj.Status.Phase != velerov1api.BackupRepositoryPhaseReady && newObj.Status.Phase != velerov1api.BackupRepositoryPhaseNotReady { return } @@ -105,7 +105,7 @@ func repoLabels(volumeNamespace, backupLocation string) labels.Set { } } -func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.ResticRepository, error) { +func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation) // It's only safe to have one instance of this method executing concurrently for a @@ -132,7 +132,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam selector := labels.SelectorFromSet(repoLabels(volumeNamespace, backupLocation)) - repos, err := r.repoLister.ResticRepositories(namespace).List(selector) + repos, err := r.repoLister.BackupRepositories(namespace).List(selector) if err != nil { return nil, errors.WithStack(err) } @@ -140,7 +140,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam return nil, errors.Errorf("more than one ResticRepository found for workload namespace %q, backup storage location %q", volumeNamespace, backupLocation) } if len(repos) == 1 { - if repos[0].Status.Phase != velerov1api.ResticRepositoryPhaseReady { + if repos[0].Status.Phase != velerov1api.BackupRepositoryPhaseReady { return nil, errors.Errorf("restic repository is not ready: %s", repos[0].Status.Message) } @@ -151,13 +151,13 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam log.Debug("No repository found, creating one") // no repo found: create one and wait for it to be ready - repo := &velerov1api.ResticRepository{ + repo := &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, GenerateName: fmt.Sprintf("%s-%s-", volumeNamespace, backupLocation), Labels: repoLabels(volumeNamespace, backupLocation), }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ VolumeNamespace: volumeNamespace, BackupStorageLocation: backupLocation, }, @@ -169,7 +169,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam close(repoChan) }() - if _, err := r.repoClient.ResticRepositories(namespace).Create(context.TODO(), repo, metav1.CreateOptions{}); err != nil { + if _, err := r.repoClient.BackupRepositories(namespace).Create(context.TODO(), repo, metav1.CreateOptions{}); err != nil { return nil, errors.Wrapf(err, "unable to create restic repository resource") } @@ -181,7 +181,8 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam case <-ctx.Done(): return nil, errors.New("timed out waiting for restic repository to become ready") case res := <-repoChan: - if res.Status.Phase == velerov1api.ResticRepositoryPhaseNotReady { + + if res.Status.Phase == velerov1api.BackupRepositoryPhaseNotReady { return nil, errors.Errorf("restic repository is not ready: %s", res.Status.Message) } @@ -189,11 +190,11 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam } } -func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.ResticRepository { +func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { r.repoChansLock.Lock() defer r.repoChansLock.Unlock() - r.repoChans[name] = make(chan *velerov1api.ResticRepository) + r.repoChans[name] = make(chan *velerov1api.BackupRepository) return r.repoChans[name] } diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index f3ff735f9d..f0ab633868 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -43,19 +43,19 @@ import ( // RepositoryManager executes commands against restic repositories. type RepositoryManager interface { // InitRepo initializes a repo with the specified name and identifier. - InitRepo(repo *velerov1api.ResticRepository) error + InitRepo(repo *velerov1api.BackupRepository) error // ConnectToRepo runs the 'restic snapshots' command against the // specified repo, and returns an error if it fails. This is // intended to be used to ensure that the repo exists/can be // authenticated to. - ConnectToRepo(repo *velerov1api.ResticRepository) error + ConnectToRepo(repo *velerov1api.BackupRepository) error // PruneRepo deletes unused data from a repo. - PruneRepo(repo *velerov1api.ResticRepository) error + PruneRepo(repo *velerov1api.BackupRepository) error // UnlockRepo removes stale locks from a repo. - UnlockRepo(repo *velerov1api.ResticRepository) error + UnlockRepo(repo *velerov1api.BackupRepository) error // Forget removes a snapshot from the list of // available snapshots in a repo. @@ -83,7 +83,7 @@ type RestorerFactory interface { type repositoryManager struct { namespace string veleroClient clientset.Interface - repoLister velerov1listers.ResticRepositoryLister + repoLister velerov1listers.BackupRepositoryLister repoInformerSynced cache.InformerSynced kbClient kbclient.Client log logrus.FieldLogger @@ -111,8 +111,8 @@ func NewRepositoryManager( ctx context.Context, namespace string, veleroClient clientset.Interface, - repoInformer velerov1informers.ResticRepositoryInformer, - repoClient velerov1client.ResticRepositoriesGetter, + repoInformer velerov1informers.BackupRepositoryInformer, + repoClient velerov1client.BackupRepositoriesGetter, kbClient kbclient.Client, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, @@ -181,7 +181,7 @@ func (rm *repositoryManager) NewRestorer(ctx context.Context, restore *velerov1a return r, nil } -func (rm *repositoryManager) InitRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) InitRepo(repo *velerov1api.BackupRepository) error { // restic init requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) defer rm.repoLocker.UnlockExclusive(repo.Name) @@ -189,7 +189,7 @@ func (rm *repositoryManager) InitRepo(repo *velerov1api.ResticRepository) error return rm.exec(InitCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.BackupRepository) error { // restic snapshots requires a non-exclusive lock rm.repoLocker.Lock(repo.Name) defer rm.repoLocker.Unlock(repo.Name) @@ -204,7 +204,7 @@ func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.ResticRepository) e return rm.exec(snapshotsCmd, repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) PruneRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) PruneRepo(repo *velerov1api.BackupRepository) error { // restic prune requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) defer rm.repoLocker.UnlockExclusive(repo.Name) @@ -212,7 +212,7 @@ func (rm *repositoryManager) PruneRepo(repo *velerov1api.ResticRepository) error return rm.exec(PruneCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) UnlockRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) UnlockRepo(repo *velerov1api.BackupRepository) error { // restic unlock requires a non-exclusive lock rm.repoLocker.Lock(repo.Name) defer rm.repoLocker.Unlock(repo.Name) diff --git a/pkg/restic/restorer.go b/pkg/restic/restorer.go index 242cc717d7..e747b0b6d5 100644 --- a/pkg/restic/restorer.go +++ b/pkg/restic/restorer.go @@ -139,8 +139,8 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { } } } - - volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, pvc) + // TODO: Remove the hard-coded uploader type before v1.10 FC + volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, "restic", pvc) if err := errorOnly(r.repoManager.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) @@ -169,7 +169,7 @@ ForEachVolume: return errs } -func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { +func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier, uploaderType string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { pvr := &velerov1api.PodVolumeRestore{ ObjectMeta: metav1.ObjectMeta{ Namespace: restore.Namespace, From 82ac228a01eb99bf590f0b06ca16bbbf5f56cc7c Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 22 Jul 2022 16:19:47 +0800 Subject: [PATCH 232/366] Delay CA file deletion in PVB controller Fix #5140. Signed-off-by: Xun Jiang --- changelogs/unreleased/5145-jxun | 1 + pkg/controller/pod_volume_backup_controller.go | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/5145-jxun diff --git a/changelogs/unreleased/5145-jxun b/changelogs/unreleased/5145-jxun new file mode 100644 index 0000000000..959ecf73df --- /dev/null +++ b/changelogs/unreleased/5145-jxun @@ -0,0 +1 @@ +Delay CA file deletion in PVB controller. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 322592f808..9cb6a3ae26 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -124,7 +124,11 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ if err != nil { return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command", log) } - defer os.Remove(resticDetails.credsFile) + + defer func() { + os.Remove(resticDetails.credsFile) + os.Remove(resticDetails.caCertFile) + }() backupLocation := &velerov1api.BackupStorageLocation{} if err := r.Client.Get(context.Background(), client.ObjectKey{ @@ -344,8 +348,6 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log if err != nil { log.WithError(err).Error("creating temporary caCert file") } - defer os.Remove(details.caCertFile) - } cmd.CACertFile = details.caCertFile From 396e68b8108f2b8369f59bc156be527997b9798d Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 22 Jul 2022 14:16:51 +0800 Subject: [PATCH 233/366] VolumeSnapshotLocation refactor with kubebuilder. 1. modify VSL CRD API file name. Add kubebuilder related marker. 2. Add Labels init code for VSL create command. Signed-off-by: Xun Jiang --- changelogs/unreleased/5148-jxun | 1 + .../v1/bases/velero.io_volumesnapshotlocations.yaml | 2 ++ config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 12 ++++++++++++ ..._location.go => volume_snapshot_location_type.go} | 8 +++++++- pkg/cmd/cli/snapshotlocation/create.go | 1 + 6 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5148-jxun rename pkg/apis/velero/v1/{volume_snapshot_location.go => volume_snapshot_location_type.go} (88%) diff --git a/changelogs/unreleased/5148-jxun b/changelogs/unreleased/5148-jxun new file mode 100644 index 0000000000..e0e489b8dc --- /dev/null +++ b/changelogs/unreleased/5148-jxun @@ -0,0 +1 @@ +VolumeSnapshotLocation refactor with kubebuilder. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml index 56ef139d28..b47713497a 100644 --- a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml @@ -13,6 +13,8 @@ spec: kind: VolumeSnapshotLocation listKind: VolumeSnapshotLocationList plural: volumesnapshotlocations + shortNames: + - vsl singular: volumesnapshotlocation scope: Namespaced versions: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 4b1197a47f..db0c6d9344 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -39,7 +39,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), } var CRDs = crds() diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0b43e2f549..61eefdf1b2 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -191,3 +191,15 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - volumesnapshotlocations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/pkg/apis/velero/v1/volume_snapshot_location.go b/pkg/apis/velero/v1/volume_snapshot_location_type.go similarity index 88% rename from pkg/apis/velero/v1/volume_snapshot_location.go rename to pkg/apis/velero/v1/volume_snapshot_location_type.go index a2ba652a65..505e1d994a 100644 --- a/pkg/apis/velero/v1/volume_snapshot_location.go +++ b/pkg/apis/velero/v1/volume_snapshot_location_type.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,10 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=vsl +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion // VolumeSnapshotLocation is a location where Velero stores volume snapshots. type VolumeSnapshotLocation struct { @@ -36,6 +40,8 @@ type VolumeSnapshotLocation struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:rbac:groups=velero.io,resources=volumesnapshotlocations,verbs=get;list;watch;create;update;patch;delete // VolumeSnapshotLocationList is a list of VolumeSnapshotLocations. type VolumeSnapshotLocationList struct { diff --git a/pkg/cmd/cli/snapshotlocation/create.go b/pkg/cmd/cli/snapshotlocation/create.go index 824156eac5..2de6b28277 100644 --- a/pkg/cmd/cli/snapshotlocation/create.go +++ b/pkg/cmd/cli/snapshotlocation/create.go @@ -63,6 +63,7 @@ type CreateOptions struct { func NewCreateOptions() *CreateOptions { return &CreateOptions{ Config: flag.NewMap(), + Labels: flag.NewMap(), } } From a109a11851608bb33bb901b2e0fac35dcd096c0b Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Tue, 26 Jul 2022 14:46:42 +0300 Subject: [PATCH 234/366] Fix zoom link for new meeting Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index 8680073fef..b243ce1d91 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -14,7 +14,7 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Follow us on Twitter at [@projectvelero](https://twitter.com/projectvelero) * Join our Kubernetes Slack channel and talk to over 800 other community members: [#velero](https://kubernetes.slack.com/messages/velero) * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. -* Join the Velero community meetings - [Zoom link](https://vmware.zoom.us/j/551441444?pwd=dHJyMWZtdHFPWWFJaTh5TnFuYWMvZz09): +* Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09): * 1st and 3rd Tuesday at 12PM ET / 9AM PT ([Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=12pm)) * 2nd and 4th Wednesday at 8am China Standard Time / Tuesday 7pm EST (8pm EDT) / Tuesday 4pm PST (5pm PDT) ([Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) From cd643bbac9c14d5ef8b64c16453808a89329eb35 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 28 Jul 2022 17:43:34 +0800 Subject: [PATCH 235/366] Add labeled and unlabeled events for PR changelog check action. Signed-off-by: Xun Jiang --- .github/workflows/pr-changelog-check.yml | 6 +++++- changelogs/unreleased/5157-jxun | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5157-jxun diff --git a/.github/workflows/pr-changelog-check.yml b/.github/workflows/pr-changelog-check.yml index 647c9d89a6..308e07d2d9 100644 --- a/.github/workflows/pr-changelog-check.yml +++ b/.github/workflows/pr-changelog-check.yml @@ -1,5 +1,9 @@ name: Pull Request Changelog Check -on: [pull_request] +# by setting `on: [pull_request]`, that means action will be trigger when PR is opened, synchronize, reopened. +# Add labeled and unlabeled events too. +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] jobs: build: diff --git a/changelogs/unreleased/5157-jxun b/changelogs/unreleased/5157-jxun new file mode 100644 index 0000000000..e42a0c93c3 --- /dev/null +++ b/changelogs/unreleased/5157-jxun @@ -0,0 +1 @@ +Add labeled and unlabeled events for PR changelog check action. \ No newline at end of file From 52fd18e9db3451e4ef35f6fff7cca6ca54b9b18a Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:06:18 +0800 Subject: [PATCH 236/366] Kopia Integration Change - Storage Configuration (#5142) * unified repo storge config Signed-off-by: Lyndon-Li * add UT Signed-off-by: Lyndon-Li --- changelogs/unreleased/5142-lyndon | 4 + .../restic_repository_controller.go | 3 +- pkg/repository/config/aws.go | 99 +++++ pkg/{restic => repository/config}/aws_test.go | 4 +- pkg/{restic => repository/config}/azure.go | 28 +- .../config}/azure_test.go | 2 +- pkg/{restic => repository/config}/config.go | 49 +-- .../config}/config_test.go | 2 +- pkg/{restic => repository/config}/gcp.go | 20 +- pkg/{restic => repository/config}/gcp_test.go | 4 +- pkg/repository/provider/provider.go | 56 +++ pkg/repository/provider/unified_repo.go | 292 ++++++++++++ pkg/repository/provider/unified_repo_test.go | 414 ++++++++++++++++++ pkg/repository/udmrepo/repo-option-consts.go | 58 +++ pkg/repository/udmrepo/repo.go | 171 ++++++++ pkg/restic/aws.go | 41 -- pkg/restic/common.go | 21 +- pkg/util/ownership/backup_owner.go | 42 ++ 18 files changed, 1208 insertions(+), 102 deletions(-) create mode 100644 changelogs/unreleased/5142-lyndon create mode 100644 pkg/repository/config/aws.go rename pkg/{restic => repository/config}/aws_test.go (96%) rename pkg/{restic => repository/config}/azure.go (88%) rename pkg/{restic => repository/config}/azure_test.go (99%) rename pkg/{restic => repository/config}/config.go (74%) rename pkg/{restic => repository/config}/config_test.go (99%) rename pkg/{restic => repository/config}/gcp.go (59%) rename pkg/{restic => repository/config}/gcp_test.go (95%) create mode 100644 pkg/repository/provider/provider.go create mode 100644 pkg/repository/provider/unified_repo.go create mode 100644 pkg/repository/provider/unified_repo_test.go create mode 100644 pkg/repository/udmrepo/repo-option-consts.go create mode 100644 pkg/repository/udmrepo/repo.go delete mode 100644 pkg/restic/aws.go create mode 100644 pkg/util/ownership/backup_owner.go diff --git a/changelogs/unreleased/5142-lyndon b/changelogs/unreleased/5142-lyndon new file mode 100644 index 0000000000..10286cf0bc --- /dev/null +++ b/changelogs/unreleased/5142-lyndon @@ -0,0 +1,4 @@ +Kopia Integration: Add the Unified Repository Interface definition. +Kopia Integration: Add the changes for Unified Repository storage config. + +Related Issues; #5076, #5080 \ No newline at end of file diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 36f0e76a86..3f27de36fc 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -31,6 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -127,7 +128,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) + repoIdentifier, err := repoconfig.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() diff --git a/pkg/repository/config/aws.go b/pkg/repository/config/aws.go new file mode 100644 index 0000000000..0ff4ca218a --- /dev/null +++ b/pkg/repository/config/aws.go @@ -0,0 +1,99 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "context" + "os" + + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/pkg/errors" +) + +const ( + // AWS specific environment variable + awsProfileEnvVar = "AWS_PROFILE" + awsProfileKey = "profile" + awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" +) + +// GetS3ResticEnvVars gets the environment variables that restic +// relies on (AWS_PROFILE) based on info in the provided object +// storage location config map. +func GetS3ResticEnvVars(config map[string]string) (map[string]string, error) { + result := make(map[string]string) + + if credentialsFile, ok := config[CredentialsFileKey]; ok { + result[awsCredentialsFileEnvVar] = credentialsFile + } + + if profile, ok := config[awsProfileKey]; ok { + result[awsProfileEnvVar] = profile + } + + return result, nil +} + +// GetS3Credentials gets the S3 credential values according to the information +// of the provided config or the system's environment variables +func GetS3Credentials(config map[string]string) (credentials.Value, error) { + credentialsFile := config[CredentialsFileKey] + if credentialsFile == "" { + credentialsFile = os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + } + + if credentialsFile == "" { + return credentials.Value{}, errors.New("missing credential file") + } + + creds := credentials.NewSharedCredentials(credentialsFile, "") + credValue, err := creds.Get() + if err != nil { + return credValue, err + } + + return credValue, nil +} + +// GetAWSBucketRegion returns the AWS region that a bucket is in, or an error +// if the region cannot be determined. +func GetAWSBucketRegion(bucket string) (string, error) { + var region string + + sess, err := session.NewSession() + if err != nil { + return "", errors.WithStack(err) + } + + for _, partition := range endpoints.DefaultPartitions() { + for regionHint := range partition.Regions() { + region, _ = s3manager.GetBucketRegion(context.Background(), sess, bucket, regionHint) + + // we only need to try a single region hint per partition, so break after the first + break + } + + if region != "" { + return region, nil + } + } + + return "", errors.New("unable to determine bucket's region") +} diff --git a/pkg/restic/aws_test.go b/pkg/repository/config/aws_test.go similarity index 96% rename from pkg/restic/aws_test.go rename to pkg/repository/config/aws_test.go index 51f3ceb993..bdd3e4fa23 100644 --- a/pkg/restic/aws_test.go +++ b/pkg/repository/config/aws_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" @@ -55,7 +55,7 @@ func TestGetS3ResticEnvVars(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual, err := getS3ResticEnvVars(tc.config) + actual, err := GetS3ResticEnvVars(tc.config) require.NoError(t, err) diff --git a/pkg/restic/azure.go b/pkg/repository/config/azure.go similarity index 88% rename from pkg/restic/azure.go rename to pkg/repository/config/azure.go index 20324b8e36..8c5871c527 100644 --- a/pkg/restic/azure.go +++ b/pkg/repository/config/azure.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "context" @@ -37,6 +37,7 @@ const ( storageAccountConfigKey = "storageAccount" storageAccountKeyEnvVarConfigKey = "storageAccountKeyEnvVar" subscriptionIDConfigKey = "subscriptionId" + storageDomainConfigKey = "storageDomain" ) // getSubscriptionID gets the subscription ID from the 'config' map if it contains @@ -131,10 +132,10 @@ func mapLookup(data map[string]string) func(string) string { } } -// getAzureResticEnvVars gets the environment variables that restic +// GetAzureResticEnvVars gets the environment variables that restic // relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based // on info in the provided object storage location config map. -func getAzureResticEnvVars(config map[string]string) (map[string]string, error) { +func GetAzureResticEnvVars(config map[string]string) (map[string]string, error) { storageAccountKey, _, err := getStorageAccountKey(config) if err != nil { return nil, err @@ -158,7 +159,7 @@ func credentialsFileFromEnv() string { // selectCredentialsFile selects the Azure credentials file to use, retrieving it // from the given config or falling back to retrieving it from the environment. func selectCredentialsFile(config map[string]string) string { - if credentialsFile, ok := config[credentialsFileKey]; ok { + if credentialsFile, ok := config[CredentialsFileKey]; ok { return credentialsFile } @@ -208,3 +209,22 @@ func getRequiredValues(getValue func(string) string, keys ...string) (map[string return results, nil } + +// GetAzureStorageDomain gets the Azure storage domain required by a Azure blob connection, +// if the provided config doean't have the value, get it from system's environment variables +func GetAzureStorageDomain(config map[string]string) string { + if domain, exist := config[storageDomainConfigKey]; exist { + return domain + } else { + return os.Getenv(cloudNameEnvVar) + } +} + +func GetAzureCredentials(config map[string]string) (string, string, error) { + storageAccountKey, _, err := getStorageAccountKey(config) + if err != nil { + return "", "", err + } + + return config[storageAccountConfigKey], storageAccountKey, nil +} diff --git a/pkg/restic/azure_test.go b/pkg/repository/config/azure_test.go similarity index 99% rename from pkg/restic/azure_test.go rename to pkg/repository/config/azure_test.go index acb2f25065..d20ac2e28b 100644 --- a/pkg/restic/azure_test.go +++ b/pkg/repository/config/azure_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "os" diff --git a/pkg/restic/config.go b/pkg/repository/config/config.go similarity index 74% rename from pkg/restic/config.go rename to pkg/repository/config/config.go index 1600f39fa8..24dc8d6b3d 100644 --- a/pkg/restic/config.go +++ b/pkg/repository/config/config.go @@ -14,17 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( - "context" "fmt" "path" "strings" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -37,11 +33,18 @@ const ( AWSBackend BackendType = "velero.io/aws" AzureBackend BackendType = "velero.io/azure" GCPBackend BackendType = "velero.io/gcp" + FSBackend BackendType = "velero.io/fs" +) + +const ( + // CredentialsFileKey is the key within a BSL config that is checked to see if + // the BSL is using its own credentials, rather than those in the environment + CredentialsFileKey = "credentialsFile" ) // this func is assigned to a package-level variable so it can be // replaced when unit-testing -var getAWSBucketRegion = getBucketRegion +var getAWSBucketRegion = GetAWSBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". @@ -55,7 +58,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) prefix = layout.GetResticDir() } - backendType := getBackendType(location.Spec.Provider) + backendType := GetBackendType(location.Spec.Provider) if repoPrefix := location.Spec.Config["resticRepoPrefix"]; repoPrefix != "" { return repoPrefix, nil @@ -89,7 +92,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) return "", errors.New("restic repository prefix (resticRepoPrefix) not specified in backup storage location's config") } -func getBackendType(provider string) BackendType { +func GetBackendType(provider string) BackendType { if !strings.Contains(provider, "/") { provider = "velero.io/" + provider } @@ -97,6 +100,10 @@ func getBackendType(provider string) BackendType { return BackendType(provider) } +func IsBackendTypeValid(backendType BackendType) bool { + return (backendType == AWSBackend || backendType == AzureBackend || backendType == GCPBackend || backendType == FSBackend) +} + // GetRepoIdentifier returns the string to be used as the value of the --repo flag in // restic commands for the given repository. func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) (string, error) { @@ -107,29 +114,3 @@ func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) return fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), name), nil } - -// getBucketRegion returns the AWS region that a bucket is in, or an error -// if the region cannot be determined. -func getBucketRegion(bucket string) (string, error) { - var region string - - sess, err := session.NewSession() - if err != nil { - return "", errors.WithStack(err) - } - - for _, partition := range endpoints.DefaultPartitions() { - for regionHint := range partition.Regions() { - region, _ = s3manager.GetBucketRegion(context.Background(), sess, bucket, regionHint) - - // we only need to try a single region hint per partition, so break after the first - break - } - - if region != "" { - return region, nil - } - } - - return "", errors.New("unable to determine bucket's region") -} diff --git a/pkg/restic/config_test.go b/pkg/repository/config/config_test.go similarity index 99% rename from pkg/restic/config_test.go rename to pkg/repository/config/config_test.go index 8418d68085..2fa26a1936 100644 --- a/pkg/restic/config_test.go +++ b/pkg/repository/config/config_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" diff --git a/pkg/restic/gcp.go b/pkg/repository/config/gcp.go similarity index 59% rename from pkg/restic/gcp.go rename to pkg/repository/config/gcp.go index 96d1edfe60..ed9e3ec6a8 100644 --- a/pkg/restic/gcp.go +++ b/pkg/repository/config/gcp.go @@ -14,21 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config + +import "os" const ( // GCP specific environment variable gcpCredentialsFileEnvVar = "GOOGLE_APPLICATION_CREDENTIALS" ) -// getGCPResticEnvVars gets the environment variables that restic relies +// GetGCPResticEnvVars gets the environment variables that restic relies // on based on info in the provided object storage location config map. -func getGCPResticEnvVars(config map[string]string) (map[string]string, error) { +func GetGCPResticEnvVars(config map[string]string) (map[string]string, error) { result := make(map[string]string) - if credentialsFile, ok := config[credentialsFileKey]; ok { + if credentialsFile, ok := config[CredentialsFileKey]; ok { result[gcpCredentialsFileEnvVar] = credentialsFile } return result, nil } + +// GetGCPCredentials gets the credential file required by a GCP bucket connection, +// if the provided config doean't have the value, get it from system's environment variables +func GetGCPCredentials(config map[string]string) string { + if credentialsFile, ok := config[CredentialsFileKey]; ok { + return credentialsFile + } else { + return os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") + } +} diff --git a/pkg/restic/gcp_test.go b/pkg/repository/config/gcp_test.go similarity index 95% rename from pkg/restic/gcp_test.go rename to pkg/repository/config/gcp_test.go index 37f2bf2c70..cd4411e3bc 100644 --- a/pkg/restic/gcp_test.go +++ b/pkg/repository/config/gcp_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" @@ -46,7 +46,7 @@ func TestGetGCPResticEnvVars(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual, err := getGCPResticEnvVars(tc.config) + actual, err := GetGCPResticEnvVars(tc.config) require.NoError(t, err) diff --git a/pkg/repository/provider/provider.go b/pkg/repository/provider/provider.go new file mode 100644 index 0000000000..36d69a594e --- /dev/null +++ b/pkg/repository/provider/provider.go @@ -0,0 +1,56 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// RepoParam includes the parameters to manipulate a backup repository +// SubDir is used to generate the path in the backup storage +type RepoParam struct { + SubDir string + BackupLocation *velerov1api.BackupStorageLocation +} + +type Provider interface { + //InitRepo is to initialize a repository from a new storage place + InitRepo(ctx context.Context, param RepoParam) error + + //ConnectToRepo is to establish the connection to a + //storage place that a repository is already initialized + ConnectToRepo(ctx context.Context, param RepoParam) error + + //PrepareRepo is a combination of InitRepo and ConnectToRepo, + //it may do initializing + connecting, connecting only if the repository + //is already initialized, or do nothing if the repository is already connected + PrepareRepo(ctx context.Context, param RepoParam) error + + //PruneRepo does a full prune/maintenance of the repository + PruneRepo(ctx context.Context, param RepoParam) error + + //PruneRepoQuick does a quick prune/maintenance of the repository if available + PruneRepoQuick(ctx context.Context, param RepoParam) error + + //EnsureUnlockRepo esures to remove any stale file locks in the storage + EnsureUnlockRepo(ctx context.Context, param RepoParam) error + + //Forget is to delete a snapshot from the repository + Forget(ctx context.Context, snapshotID string, param RepoParam) error +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go new file mode 100644 index 0000000000..49cef09ce4 --- /dev/null +++ b/pkg/repository/provider/unified_repo.go @@ -0,0 +1,292 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "fmt" + "path" + "strings" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/util/ownership" +) + +type unifiedRepoProvider struct { + credentialsFileStore credentials.FileStore + workPath string + repoService udmrepo.BackupRepoService + log logrus.FieldLogger +} + +// this func is assigned to a package-level variable so it can be +// replaced when unit-testing +var getAzureCredentials = repoconfig.GetAzureCredentials +var getS3Credentials = repoconfig.GetS3Credentials +var getGCPCredentials = repoconfig.GetGCPCredentials +var getS3BucketRegion = repoconfig.GetAWSBucketRegion +var getAzureStorageDomain = repoconfig.GetAzureStorageDomain + +// NewUnifiedRepoProvider creates the service provider for Unified Repo +// workPath is the path for Unified Repo to store some local information +// workPath could be empty, if so, the default path will be used +func NewUnifiedRepoProvider( + credentialFileStore credentials.FileStore, + workPath string, + log logrus.FieldLogger, +) (Provider, error) { + repo := unifiedRepoProvider{ + credentialsFileStore: credentialFileStore, + workPath: workPath, + log: log, + } + + repo.repoService = createRepoService(log) + + log.Debug("Finished create unified repo service") + + return &repo, nil +} + +func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) error { + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to init repo") + + repoOption, err := urp.getRepoOption(param) + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Init(ctx, repoOption, true) + if err != nil { + return errors.Wrap(err, "error to init backup repo") + } + + log.Debug("Init repo complete") + + return nil +} + +func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error { + return nil +} + +func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) getRepoPassword(param RepoParam) (string, error) { + ///TODO: get repo password + + return "", nil +} + +func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) { + repoOption := udmrepo.RepoOptions{ + StorageType: getStorageType(param.BackupLocation), + ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)), + Ownership: udmrepo.OwnershipOptions{ + Username: ownership.GetRepositoryOwner().Username, + DomainName: ownership.GetRepositoryOwner().DomainName, + }, + StorageOptions: make(map[string]string), + GeneralOptions: make(map[string]string), + } + + repoPassword, err := urp.getRepoPassword(param) + if err != nil { + return repoOption, errors.Wrap(err, "error to get repo password") + } + + repoOption.RepoPassword = repoPassword + + storeVar, err := getStorageVariables(param.BackupLocation, param.SubDir) + if err != nil { + return repoOption, errors.Wrap(err, "error to get storage variables") + } + + for k, v := range storeVar { + repoOption.StorageOptions[k] = v + } + + storeCred, err := getStorageCredentials(param.BackupLocation, urp.credentialsFileStore) + if err != nil { + return repoOption, errors.Wrap(err, "error to get repo credential env") + } + + for k, v := range storeCred { + repoOption.StorageOptions[k] = v + } + + return repoOption, nil +} + +func getStorageType(backupLocation *velerov1api.BackupStorageLocation) string { + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + + switch backendType { + case repoconfig.AWSBackend: + return udmrepo.StorageTypeS3 + case repoconfig.AzureBackend: + return udmrepo.StorageTypeAzure + case repoconfig.GCPBackend: + return udmrepo.StorageTypeGcs + case repoconfig.FSBackend: + return udmrepo.StorageTypeFs + default: + return "" + } +} + +func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, credentialsFileStore credentials.FileStore) (map[string]string, error) { + result := make(map[string]string) + var err error + + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + if !repoconfig.IsBackendTypeValid(backendType) { + return map[string]string{}, errors.New("invalid storage provider") + } + + config := backupLocation.Spec.Config + if config == nil { + config = map[string]string{} + } + + if backupLocation.Spec.Credential != nil { + config[repoconfig.CredentialsFileKey], err = credentialsFileStore.Path(backupLocation.Spec.Credential) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get credential file in bsl") + } + } + + switch backendType { + case repoconfig.AWSBackend: + credValue, err := getS3Credentials(config) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get s3 credentials") + } + result[udmrepo.StoreOptionS3KeyId] = credValue.AccessKeyID + result[udmrepo.StoreOptionS3Provider] = credValue.ProviderName + result[udmrepo.StoreOptionS3SecretKey] = credValue.SecretAccessKey + result[udmrepo.StoreOptionS3Token] = credValue.SessionToken + + case repoconfig.AzureBackend: + storageAccount, accountKey, err := getAzureCredentials(config) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get azure credentials") + } + result[udmrepo.StoreOptionAzureStorageAccount] = storageAccount + result[udmrepo.StoreOptionAzureKey] = accountKey + + case repoconfig.GCPBackend: + result[udmrepo.StoreOptionCredentialFile] = getGCPCredentials(config) + } + + return result, nil +} + +func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoName string) (map[string]string, error) { + result := make(map[string]string) + + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + if !repoconfig.IsBackendTypeValid(backendType) { + return map[string]string{}, errors.New("invalid storage provider") + } + + config := backupLocation.Spec.Config + if config == nil { + config = map[string]string{} + } + + bucket := strings.Trim(config["bucket"], "/") + prefix := strings.Trim(config["prefix"], "/") + if backupLocation.Spec.ObjectStorage != nil { + bucket = strings.Trim(backupLocation.Spec.ObjectStorage.Bucket, "/") + prefix = strings.Trim(backupLocation.Spec.ObjectStorage.Prefix, "/") + } + + prefix = path.Join(prefix, udmrepo.StoreOptionPrefixName, repoName) + "/" + + region := config["region"] + + if backendType == repoconfig.AWSBackend { + s3Url := config["s3Url"] + + var err error + if s3Url == "" { + region, err = getS3BucketRegion(bucket) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get s3 bucket region") + } + + s3Url = fmt.Sprintf("s3-%s.amazonaws.com", region) + } + + result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3Url, "/") + result[udmrepo.StoreOptionS3DisableTlsVerify] = config["insecureSkipTLSVerify"] + } else if backendType == repoconfig.AzureBackend { + result[udmrepo.StoreOptionAzureDomain] = getAzureStorageDomain(config) + } + + result[udmrepo.StoreOptionOssBucket] = bucket + result[udmrepo.StoreOptionPrefix] = prefix + result[udmrepo.StoreOptionOssRegion] = strings.Trim(region, "/") + result[udmrepo.StoreOptionFsPath] = config["fspath"] + + return result, nil +} + +func getRepoConfigFile(workPath string, repoID string) string { + ///TODO: call udmrepo to get config file + return "" +} + +func createRepoService(log logrus.FieldLogger) udmrepo.BackupRepoService { + ///TODO: call udmrepo create repo service + return nil +} diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go new file mode 100644 index 0000000000..ee78c7b5d2 --- /dev/null +++ b/pkg/repository/provider/unified_repo_test.go @@ -0,0 +1,414 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "errors" + "testing" + + awscredentials "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" + + filecredentials "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestGetStorageCredentials(t *testing.T) { + testCases := []struct { + name string + backupLocation velerov1api.BackupStorageLocation + credFileStore filecredentials.FileStore + getAzureCredentials func(map[string]string) (string, string, error) + getS3Credentials func(map[string]string) (awscredentials.Value, error) + getGCPCredentials func(map[string]string) string + expected map[string]string + expectedErr string + }{ + { + name: "invalid provider", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "invalid-provider", + }, + }, + expected: map[string]string{}, + expectedErr: "invalid storage provider", + }, + { + name: "credential section exists in BSL, file store fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("", errors.New("fake error")), + expected: map[string]string{}, + expectedErr: "error get credential file in bsl: fake error", + }, + { + name: "aws, Credential section not exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{ + AccessKeyID: "from: " + config["credentialsFile"], + }, nil + }, + + expected: map[string]string{ + "accessKeyID": "from: credentials-from-config-map", + "providerName": "", + "secretAccessKey": "", + "sessionToken": "", + }, + }, + { + name: "aws, Credential section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{ + AccessKeyID: "from: " + config["credentialsFile"], + }, nil + }, + + expected: map[string]string{ + "accessKeyID": "from: credentials-from-credential-key", + "providerName": "", + "secretAccessKey": "", + "sessionToken": "", + }, + }, + { + name: "aws, get credentials fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("", nil), + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{}, errors.New("fake error") + }, + expected: map[string]string{}, + expectedErr: "error get s3 credentials: fake error", + }, + { + name: "azure, Credential section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + getAzureCredentials: func(config map[string]string) (string, string, error) { + return "storage account from: " + config["credentialsFile"], "", nil + }, + + expected: map[string]string{ + "storageAccount": "storage account from: credentials-from-credential-key", + "storageKey": "", + }, + }, + { + name: "azure, get azure credentials fails", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getAzureCredentials: func(config map[string]string) (string, string, error) { + return "", "", errors.New("fake error") + }, + + expected: map[string]string{}, + expectedErr: "error get azure credentials: fake error", + }, + { + name: "gcp, Credential section not exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/gcp", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getGCPCredentials: func(config map[string]string) string { + return "credentials-from-config-map" + }, + + expected: map[string]string{ + "credFile": "credentials-from-config-map", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + getAzureCredentials = tc.getAzureCredentials + getS3Credentials = tc.getS3Credentials + getGCPCredentials = tc.getGCPCredentials + + actual, err := getStorageCredentials(&tc.backupLocation, tc.credFileStore) + + require.Equal(t, tc.expected, actual) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestGetStorageVariables(t *testing.T) { + testCases := []struct { + name string + backupLocation velerov1api.BackupStorageLocation + repoName string + getS3BucketRegion func(string) (string, error) + getAzureStorageDomain func(map[string]string) string + expected map[string]string + expectedErr string + }{ + { + name: "invalid provider", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "invalid-provider", + }, + }, + expected: map[string]string{}, + expectedErr: "invalid storage provider", + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region/", + "s3Url": "fake-url", + "insecureSkipTLSVerify": "true", + }, + }, + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/", + "region": "fake-region", + "fspath": "", + "endpoint": "fake-url", + "skipTLSVerify": "true", + }, + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url not exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "insecureSkipTLSVerify": "false", + }, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "region from bucket: " + bucket, nil + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/", + "region": "region from bucket: fake-bucket", + "fspath": "", + "endpoint": "s3-region from bucket: fake-bucket.amazonaws.com", + "skipTLSVerify": "false", + }, + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url not exist, get region fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{}, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "", errors.New("fake error") + }, + expected: map[string]string{}, + expectedErr: "error get s3 bucket region: fake error", + }, + { + name: "aws, ObjectStorage section exists in BSL, s3Url exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "s3Url": "fake-url", + "insecureSkipTLSVerify": "false", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + }, + }, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "region from bucket: " + bucket, nil + }, + expected: map[string]string{ + "bucket": "fake-bucket-object-store", + "prefix": "fake-prefix-object-store/unified-repo/", + "region": "fake-region", + "fspath": "", + "endpoint": "fake-url", + "skipTLSVerify": "false", + }, + }, + { + name: "azure, ObjectStorage section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + }, + }, + }, + }, + getAzureStorageDomain: func(config map[string]string) string { + return config["storageDomain"] + }, + expected: map[string]string{ + "bucket": "fake-bucket-object-store", + "prefix": "fake-prefix-object-store/unified-repo/", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + { + name: "azure, ObjectStorage section not exists in BSL, repo name exists", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + }, + repoName: "//fake-name//", + getAzureStorageDomain: func(config map[string]string) string { + return config["storageDomain"] + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/fake-name/", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + { + name: "fs", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/fs", + Config: map[string]string{ + "fspath": "fake-path", + "prefix": "fake-prefix", + }, + }, + }, + expected: map[string]string{ + "fspath": "fake-path", + "bucket": "", + "prefix": "fake-prefix/unified-repo/", + "region": "", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + getS3BucketRegion = tc.getS3BucketRegion + getAzureStorageDomain = tc.getAzureStorageDomain + + actual, err := getStorageVariables(&tc.backupLocation, tc.repoName) + + require.Equal(t, tc.expected, actual) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/repo-option-consts.go b/pkg/repository/udmrepo/repo-option-consts.go new file mode 100644 index 0000000000..7cf55d017c --- /dev/null +++ b/pkg/repository/udmrepo/repo-option-consts.go @@ -0,0 +1,58 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package udmrepo + +const ( + StorageTypeS3 = "s3" + StorageTypeAzure = "azure" + StorageTypeFs = "filesystem" + StorageTypeGcs = "gcs" + + GenOptionMaintainMode = "mode" + GenOptionMaintainFull = "full" + GenOptionMaintainQuick = "quick" + + StoreOptionS3KeyId = "accessKeyID" + StoreOptionS3Provider = "providerName" + StoreOptionS3SecretKey = "secretAccessKey" + StoreOptionS3Token = "sessionToken" + StoreOptionS3Endpoint = "endpoint" + StoreOptionS3DisableTls = "doNotUseTLS" + StoreOptionS3DisableTlsVerify = "skipTLSVerify" + + StoreOptionAzureKey = "storageKey" + StoreOptionAzureDomain = "storageDomain" + StoreOptionAzureStorageAccount = "storageAccount" + StoreOptionAzureToken = "sasToken" + + StoreOptionFsPath = "fspath" + + StoreOptionGcsReadonly = "readonly" + + StoreOptionOssBucket = "bucket" + StoreOptionOssRegion = "region" + + StoreOptionCredentialFile = "credFile" + StoreOptionPrefix = "prefix" + StoreOptionPrefixName = "unified-repo" + + ThrottleOptionReadOps = "readOPS" + ThrottleOptionWriteOps = "writeOPS" + ThrottleOptionListOps = "listOPS" + ThrottleOptionUploadBytes = "uploadBytes" + ThrottleOptionDownloadBytes = "downloadBytes" +) diff --git a/pkg/repository/udmrepo/repo.go b/pkg/repository/udmrepo/repo.go new file mode 100644 index 0000000000..be18a6d176 --- /dev/null +++ b/pkg/repository/udmrepo/repo.go @@ -0,0 +1,171 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package udmrepo + +import ( + "context" + "io" + "time" +) + +type ID string + +// ManifestEntryMetadata is the metadata describing one manifest data +type ManifestEntryMetadata struct { + ID ID // The ID of the manifest data + Length int32 // The data size of the manifest data + Labels map[string]string // Labels saved together with the manifest data + ModTime time.Time // Modified time of the manifest data +} + +type RepoManifest struct { + Payload interface{} // The user data of manifest + Metadata *ManifestEntryMetadata // The metadata data of manifest +} + +type ManifestFilter struct { + Labels map[string]string +} + +const ( + // Below consts descrbe the data type of one object. + // Metadata: This type describes how the data is organized. + // For a file system backup, the Metadata describes a Dir or File. + // For a block backup, the Metadata describes a Disk and its incremental link. + ObjectDataTypeUnknown int = 0 + ObjectDataTypeMetadata int = 1 + ObjectDataTypeData int = 2 + + // Below consts defines the access mode when creating an object for write + ObjectDataAccessModeUnknown int = 0 + ObjectDataAccessModeFile int = 1 + ObjectDataAccessModeBlock int = 2 + + ObjectDataBackupModeUnknown int = 0 + ObjectDataBackupModeFull int = 1 + ObjectDataBackupModeInc int = 2 +) + +// ObjectWriteOptions defines the options when creating an object for write +type ObjectWriteOptions struct { + FullPath string // Full logical path of the object + DataType int // OBJECT_DATA_TYPE_* + Description string // A description of the object, could be empty + Prefix ID // A prefix of the name used to save the object + AccessMode int // OBJECT_DATA_ACCESS_* + BackupMode int // OBJECT_DATA_BACKUP_* +} + +// OwnershipOptions is used to add some access control to the unified repository. +// For example, some privileged operations of the unified repository can be done by the +// repository owner only; the data of a backup may be manipulated by the backup owner +// who created it only. It is optional for a backup repository to support this ownership control. +type OwnershipOptions struct { + Username string + DomainName string + FullQualified string +} + +type RepoOptions struct { + // A repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + // Backup repository password, if any + RepoPassword string + // A custom path to save the repository's configuration, if any + ConfigFilePath string + // The ownership for the current repository operation + Ownership OwnershipOptions + // Other repository specific options + GeneralOptions map[string]string + // Storage specific options + StorageOptions map[string]string + + // Description of the backup repository + Description string +} + +// BackupRepoService is used to initialize, open or maintain a backup repository +type BackupRepoService interface { + // Create a backup repository or connect to an existing backup repository. + // repoOption: option to the backup repository and the underlying backup storage. + // createNew: indicates whether to create a new or connect to an existing backup repository. + Init(ctx context.Context, repoOption RepoOptions, createNew bool) error + + // Open an backup repository that has been created/connected. + // repoOption: options to open the backup repository and the underlying storage. + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) + + // Periodically called to maintain the backup repository to eliminate redundant data and improve performance. + // repoOption: options to maintain the backup repository. + Maintain(ctx context.Context, repoOption RepoOptions) error +} + +// BackupRepo provides the access to the backup repository +type BackupRepo interface { + // Open an existing object for read. + // id: the object's unified identifier. + OpenObject(ctx context.Context, id ID) (ObjectReader, error) + + // Get a manifest data. + GetManifest(ctx context.Context, id ID, mani *RepoManifest) error + + // Get one or more manifest data that match the given labels + FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) + + // Create a new object and return the object's writer interface. + // return: A unified identifier of the object on success. + NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter + + // Save a manifest object + PutManifest(ctx context.Context, mani RepoManifest) (ID, error) + + // Delete a manifest object + DeleteManifest(ctx context.Context, id ID) error + + // Flush all the backup repository data + Flush(ctx context.Context) error + + // Get the local time of the backup repository. It may be different from the time of the caller + Time() time.Time + + // Close the backup repository + Close(ctx context.Context) error +} + +type ObjectReader interface { + io.ReadCloser + io.Seeker + + // Length returns the logical size of the object + Length() int64 +} + +type ObjectWriter interface { + io.WriteCloser + + // For some cases, i.e. block incremental, the object is not written sequentially + io.Seeker + + // Periodically called to preserve the state of data written to the repo so far. + // Return a unified identifier that represent the current state. + // An empty ID could be returned on success if the backup repository doesn't support this. + Checkpoint() (ID, error) + + // Wait for the completion of the object write. + // Result returns the object's unified identifier after the write completes. + Result() (ID, error) +} diff --git a/pkg/restic/aws.go b/pkg/restic/aws.go deleted file mode 100644 index d97c5f0b77..0000000000 --- a/pkg/restic/aws.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -const ( - // AWS specific environment variable - awsProfileEnvVar = "AWS_PROFILE" - awsProfileKey = "profile" - awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" -) - -// getS3ResticEnvVars gets the environment variables that restic -// relies on (AWS_PROFILE) based on info in the provided object -// storage location config map. -func getS3ResticEnvVars(config map[string]string) (map[string]string, error) { - result := make(map[string]string) - - if credentialsFile, ok := config[credentialsFileKey]; ok { - result[awsCredentialsFileEnvVar] = credentialsFile - } - - if profile, ok := config[awsProfileKey]; ok { - result[awsProfileEnvVar] = profile - } - - return result, nil -} diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 23c09e558e..580bd27080 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -32,6 +32,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -63,10 +64,6 @@ const ( // should be excluded from restic backup. VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" - // credentialsFileKey is the key within a BSL config that is checked to see if - // the BSL is using its own credentials, rather than those in the environment - credentialsFileKey = "credentialsFile" - // Deprecated. // // TODO(2.0): remove @@ -322,24 +319,24 @@ func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileSto if err != nil { return []string{}, errors.WithStack(err) } - config[credentialsFileKey] = credsFile + config[repoconfig.CredentialsFileKey] = credsFile } - backendType := getBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) switch backendType { - case AWSBackend: - customEnv, err = getS3ResticEnvVars(config) + case repoconfig.AWSBackend: + customEnv, err = repoconfig.GetS3ResticEnvVars(config) if err != nil { return []string{}, err } - case AzureBackend: - customEnv, err = getAzureResticEnvVars(config) + case repoconfig.AzureBackend: + customEnv, err = repoconfig.GetAzureResticEnvVars(config) if err != nil { return []string{}, err } - case GCPBackend: - customEnv, err = getGCPResticEnvVars(config) + case repoconfig.GCPBackend: + customEnv, err = repoconfig.GetGCPResticEnvVars(config) if err != nil { return []string{}, err } diff --git a/pkg/util/ownership/backup_owner.go b/pkg/util/ownership/backup_owner.go new file mode 100644 index 0000000000..078c799dd9 --- /dev/null +++ b/pkg/util/ownership/backup_owner.go @@ -0,0 +1,42 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ownership + +import "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + +const ( + defaultOwnerUsername = "default" + defaultOwnerDomain = "default" +) + +// GetBackupOwner returns the owner used by uploaders when saving a snapshot or +// opening the unified repository. At present, use the default owner only +func GetBackupOwner() udmrepo.OwnershipOptions { + return udmrepo.OwnershipOptions{ + Username: defaultOwnerUsername, + DomainName: defaultOwnerDomain, + } +} + +// GetBackupOwner returns the owner used to create/connect the unified repository. +//At present, use the default owner only +func GetRepositoryOwner() udmrepo.OwnershipOptions { + return udmrepo.OwnershipOptions{ + Username: defaultOwnerUsername, + DomainName: defaultOwnerDomain, + } +} From 108c81d84cf6e10212a7f478c94d4691bf7b6a46 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 2 Aug 2022 15:47:57 +0800 Subject: [PATCH 237/366] Skip registering "crd-remap-version" plugin when feature flag (#5165) "EnableAPIGroupVersions" is set The crd-remap-version plugin will always backup v1b1 resource for some CRD. It impacts the feature flag `EnableAPIGroupVersions` which means to backup all versions, and make migration fail. In this commit the featureSet was removed from plugin server struct b/c it blocks the parm `--features` to be populated correctly. This change should not have negative impact b/c the attribute in server struct is never used. Fixes #5146 Signed-off-by: Daniel Jiang --- changelogs/unreleased/5165-reasonerjt | 1 + pkg/cmd/server/plugin/plugin.go | 17 ++++++++++------- pkg/plugin/framework/server.go | 6 +----- 3 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/5165-reasonerjt diff --git a/changelogs/unreleased/5165-reasonerjt b/changelogs/unreleased/5165-reasonerjt new file mode 100644 index 0000000000..c33f179b3f --- /dev/null +++ b/changelogs/unreleased/5165-reasonerjt @@ -0,0 +1 @@ +Skip registering "crd-remap-version" plugin when feature flag "EnableAPIGroupVersions" is set \ No newline at end of file diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 5c833d76a1..5dec03856b 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -19,9 +19,11 @@ package plugin import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/features" + "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/client" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" @@ -36,11 +38,10 @@ func NewCommand(f client.Factory) *cobra.Command { Hidden: true, Short: "INTERNAL COMMAND ONLY - not intended to be run directly by users", Run: func(c *cobra.Command, args []string) { - pluginServer. + pluginServer = pluginServer. RegisterBackupItemAction("velero.io/pv", newPVBackupItemAction). RegisterBackupItemAction("velero.io/pod", newPodBackupItemAction). RegisterBackupItemAction("velero.io/service-account", newServiceAccountBackupItemAction(f)). - RegisterBackupItemAction("velero.io/crd-remap-version", newRemapCRDVersionAction(f)). RegisterRestoreItemAction("velero.io/job", newJobRestoreItemAction). RegisterRestoreItemAction("velero.io/pod", newPodRestoreItemAction). RegisterRestoreItemAction("velero.io/restic", newResticRestoreItemAction(f)). @@ -55,13 +56,15 @@ func NewCommand(f client.Factory) *cobra.Command { RegisterRestoreItemAction("velero.io/crd-preserve-fields", newCRDV1PreserveUnknownFieldsItemAction). RegisterRestoreItemAction("velero.io/change-pvc-node-selector", newChangePVCNodeSelectorItemAction(f)). RegisterRestoreItemAction("velero.io/apiservice", newAPIServiceRestoreItemAction). - RegisterRestoreItemAction("velero.io/admission-webhook-configuration", newAdmissionWebhookConfigurationAction). - Serve() + RegisterRestoreItemAction("velero.io/admission-webhook-configuration", newAdmissionWebhookConfigurationAction) + if !features.IsEnabled(velerov1api.APIGroupVersionsFeatureFlag) { + // Do not register crd-remap-version BIA if the API Group feature flag is enabled, so that the v1 CRD can be backed up + pluginServer = pluginServer.RegisterBackupItemAction("velero.io/crd-remap-version", newRemapCRDVersionAction(f)) + } + pluginServer.Serve() }, } - pluginServer.BindFlags(c.Flags()) - return c } diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index 066a44db62..b25ef341de 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -25,7 +25,6 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/pflag" - veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -78,6 +77,7 @@ type Server interface { // RegisterItemSnapshotters registers multiple Item Snapshotters RegisterItemSnapshotters(map[string]HandlerInitializer) Server + // Server runs the plugin server. Serve() } @@ -87,7 +87,6 @@ type server struct { log *logrus.Logger logLevelFlag *logging.LevelFlag flagSet *pflag.FlagSet - featureSet *veleroflag.StringArray backupItemAction *BackupItemActionPlugin volumeSnapshotter *VolumeSnapshotterPlugin objectStore *ObjectStorePlugin @@ -99,12 +98,10 @@ type server struct { // NewServer returns a new Server func NewServer() Server { log := newLogger() - features := veleroflag.NewStringArray() return &server{ log: log, logLevelFlag: logging.LogLevelFlag(log.Level), - featureSet: &features, backupItemAction: NewBackupItemActionPlugin(serverLogger(log)), volumeSnapshotter: NewVolumeSnapshotterPlugin(serverLogger(log)), objectStore: NewObjectStorePlugin(serverLogger(log)), @@ -116,7 +113,6 @@ func NewServer() Server { func (s *server) BindFlags(flags *pflag.FlagSet) Server { flags.Var(s.logLevelFlag, "log-level", fmt.Sprintf("The level at which to log. Valid values are %s.", strings.Join(s.logLevelFlag.AllowedValues(), ", "))) - flags.Var(s.featureSet, "features", "List of feature flags for this plugin") s.flagSet = flags s.flagSet.ParseErrorsWhitelist.UnknownFlags = true From 828e28aa43b08de948d565f902a7d5c41826f220 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 2 Aug 2022 10:20:53 +0000 Subject: [PATCH 238/366] Fix restic backups to multiple backup storage locations bug Signed-off-by: Ming --- changelogs/unreleased/5172-qiuming-best | 1 + pkg/controller/pod_volume_backup_controller.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/5172-qiuming-best diff --git a/changelogs/unreleased/5172-qiuming-best b/changelogs/unreleased/5172-qiuming-best new file mode 100644 index 0000000000..fa57d3d30e --- /dev/null +++ b/changelogs/unreleased/5172-qiuming-best @@ -0,0 +1 @@ +Fix restic backups to multiple backup storage locations bug diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 9cb6a3ae26..8bce92ed7c 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -241,7 +241,7 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l // Go through all the podvolumebackups for the PVC and look for the most // recent completed one to use as the parent. - var mostRecentPVB *velerov1api.PodVolumeBackup + var mostRecentPVB velerov1api.PodVolumeBackup for _, pvb := range pvbList.Items { if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseCompleted { continue @@ -258,12 +258,12 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l continue } - if mostRecentPVB == nil || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) { - mostRecentPVB = &pvb + if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) { + mostRecentPVB = pvb } } - if mostRecentPVB == nil { + if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) { log.Info("No completed PodVolumeBackup found for PVC") return "" } From e8da5df57af1ad590eea10e31cd0c4c9b3c31bed Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 1 Jul 2022 07:18:58 +0000 Subject: [PATCH 239/366] Reduce CRD size. 1. Make the Restore hook.InitConatianer server side field pruing disable. 2. Remove restore patch in update-generate-crd-code.sh. 3. Modify related testcases. 4. Add Container fields validation in Restore Init hook. Signed-off-by: Xun Jiang --- changelogs/unreleased/5174-jxun | 1 + config/crd/v1/bases/velero.io_restores.yaml | 1421 +---------------- config/crd/v1/crds/crds.go | 2 +- hack/restore-crd-patch-v1.json | 3 - hack/restore-crd-patch-v1beta1.json | 3 - hack/update-generated-crd-code.sh | 11 +- internal/hook/item_hook_handler.go | 55 +- internal/hook/item_hook_handler_test.go | 207 ++- pkg/apis/velero/v1/restore.go | 5 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 2 +- pkg/builder/container_builder.go | 13 + pkg/controller/restore_controller.go | 17 + .../init_restorehook_pod_action_test.go | 10 +- 13 files changed, 207 insertions(+), 1543 deletions(-) create mode 100644 changelogs/unreleased/5174-jxun delete mode 100644 hack/restore-crd-patch-v1.json delete mode 100644 hack/restore-crd-patch-v1beta1.json diff --git a/changelogs/unreleased/5174-jxun b/changelogs/unreleased/5174-jxun new file mode 100644 index 0000000000..4c3991b28f --- /dev/null +++ b/changelogs/unreleased/5174-jxun @@ -0,0 +1 @@ +Reduce CRD size. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 4bc98b3a41..181a4e5faa 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -1,3 +1,5 @@ + +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -200,1426 +202,9 @@ spec: description: InitContainers is list of init containers to be added to a pod during its restore. items: - description: A single application container - that you want to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. - The docker image''s CMD is used if this - is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the - reference in the input string will be - unchanged. Double $$ are reduced to a - single $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, - regardless of whether the variable exists - or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed - within a shell. The docker image''s ENTRYPOINT - is used if this is not provided. Variable - references $(VAR_NAME) are expanded using - the container''s environment. If a variable - cannot be resolved, the reference in the - input string will be unchanged. Double - $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of - whether the variable exists or not. Cannot - be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables - to set in the container. Cannot be updated. - items: - description: EnvVar represents an environment - variable present in a Container. - properties: - name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references - $(VAR_NAME) are expanded using the - previously defined environment variables - in the container and any service - environment variables. If a variable - cannot be resolved, the reference - in the input string will be unchanged. - Double $$ are reduced to a single - $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal - "$(VAR_NAME)". Escaped references - will never be expanded, regardless - of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment - variable's value. Cannot be used - if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of - a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the - referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field - of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is - written in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory - and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the - output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of - a secret in the pod's namespace - properties: - key: - description: The key of the - secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the - referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate - environment variables in the container. - The keys defined within a source must - be a C_IDENTIFIER. All invalid keys will - be reported as an event when the container - is starting. When a key exists in multiple - sources, the value associated with the - last source will take precedence. Values - defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents - the source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier - to prepend to each key in the ConfigMap. - Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - Secret must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: - https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher - level config management to default or - override container images in workload - controllers like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of - Always, Never, IfNotPresent. Defaults - to Always if :latest tag is specified, - or IfNotPresent otherwise. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management - system should take in response to container - lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the - handler fails, the container is terminated - and restarted according to its restart - policy. Other management of the container - blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of - the following should be specified. - Exec specifies the action to take. - properties: - command: - description: Command is the - command line to execute inside - the container, the working - directory for the command is - root ('/') in the container's - filesystem. The command is - simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, - you need to explicitly call - out to that shell. Exit status - of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers - to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access - on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host - name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately - before a container is terminated due - to an API request or management event - such as liveness/startup probe failure, - preemption, resource contention, etc. - The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s - termination grace period countdown - begins before the PreStop hooked is - executed. Regardless of the outcome - of the handler, the container will - eventually terminate within the Pod''s - termination grace period. Other management - of the container blocks until the - hook completes or until the termination - grace period is reached. More info: - https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of - the following should be specified. - Exec specifies the action to take. - properties: - command: - description: Command is the - command line to execute inside - the container, the working - directory for the command is - root ('/') in the container's - filesystem. The command is - simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, - you need to explicitly call - out to that shell. Exit status - of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers - to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access - on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host - name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container - liveness. Container will be restarted - if the probe fails. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified - as a DNS_LABEL. Each container in a pod - must have a unique name (DNS_LABEL). Cannot - be updated. - type: string - ports: - description: List of ports to expose from - the container. Exposing a port here gives - the system additional information about - the network connections a container uses, - but is primarily informational. Not specifying - a port here DOES NOT prevent that port - from being exposed. Any port which is - listening on the default "0.0.0.0" address - inside a container will be accessible - from the network. Cannot be updated. - items: - description: ContainerPort represents - a network port in a single container. - properties: - containerPort: - description: Number of port to expose - on the pod's IP address. This must - be a valid port number, 0 < x < - 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind - the external port to. - type: string - hostPort: - description: Number of port to expose - on the host. If specified, this - must be a valid port number, 0 < - x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most - containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must - be an IANA_SVC_NAME and unique within - the pod. Each named port in a pod - must have a unique name. Name for - the port that can be referred to - by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must - be UDP, TCP, or SCTP. Defaults to - "TCP". - type: string - required: - - containerPort - - protocol - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container - service readiness. Container will be removed - from service endpoints if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required - by this container. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the - minimum amount of compute resources - required. If Requests is omitted for - a container, it defaults to Limits - if that is explicitly specified, otherwise - to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the - security options the container should - be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation - controls whether a process can gain - more privileges than its parent process. - This bool directly controls if the - no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation - is true always when the container - is: 1) run as Privileged 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop - when running containers. Defaults - to the default set of capabilities - granted by the container runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged - mode. Processes in privileged containers - are essentially equivalent to root - on the host. Defaults to false. - type: boolean - procMount: - description: procMount denotes the type - of proc mount to use for the containers. - The default is DefaultProcMount which - uses the container runtime defaults - for readonly paths and masked paths. - This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container - has a read-only root filesystem. Default - is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint - of the container process. Uses runtime - default if unset. May also be set - in PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container - must run as a non-root user. If true, - the Kubelet will validate the image - at runtime to ensure that it does - not run as UID 0 (root) and fail to - start the container if it does. If - unset or false, no such validation - will be performed. May also be set - in PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint - of the container process. Defaults - to user specified in image metadata - if unspecified. May also be set in - PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to - be applied to the container. If unspecified, - the container runtime will allocate - a random SELinux context for each - container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - properties: - level: - description: Level is SELinux level - label that applies to the container. - type: string - role: - description: Role is a SELinux role - label that applies to the container. - type: string - type: - description: Type is a SELinux type - label that applies to the container. - type: string - user: - description: User is a SELinux user - label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to - use by this container. If seccomp - options are provided at both the pod - & container level, the container options - override the pod options. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on - the node should be used. The profile - must be preconfigured on the node - to work. Must be a descending - path, relative to the kubelet's - configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which - kind of seccomp profile will be - applied. Valid options are: \n - Localhost - a profile defined - in a file on the node should be - used. RuntimeDefault - the container - runtime default profile should - be used. Unconfined - no profile - should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings - applied to all containers. If unspecified, - the options from the PodSecurityContext - will be used. If set in both SecurityContext - and PodSecurityContext, the value - specified in SecurityContext takes - precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec - is where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA - credential spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName - is the name of the GMSA credential - spec to use. - type: string - hostProcess: - description: HostProcess determines - if a container should be run as - a 'Host Process' container. This - field is alpha-level and will - only be honored by components - that enable the WindowsHostProcessContainers - feature flag. Setting this field - without the feature flag will - result in errors when validating - the Pod. All of a Pod's containers - must have the same effective HostProcess - value (it is not allowed to have - a mix of HostProcess containers - and non-HostProcess containers). In - addition, if HostProcess is true - then HostNetwork must also be - set to true. - type: boolean - runAsUserName: - description: The UserName in Windows - to run the entrypoint of the container - process. Defaults to the user - specified in image metadata if - unspecified. May also be set in - PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that - the Pod has successfully initialized. - If specified, no other probes are executed - until this completes successfully. If - this probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This - can be used to provide different probe - parameters at the beginning of a Pod''s - lifecycle, when it might take a long time - to load data or warm a cache, than during - steady-state operation. This cannot be - updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should - allocate a buffer for stdin in the container - runtime. If this is not set, reads from - stdin in the container will always result - in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime - should close the stdin channel after it - has been opened by a single attach. When - stdin is true the stdin stream will remain - open across multiple attach sessions. - If stdinOnce is set to true, stdin is - opened on container start, is empty until - the first client attaches to stdin, and - then remains open and accepts data until - the client disconnects, at which time - stdin is closed and remains closed until - the container is restarted. If this flag - is false, a container processes that reads - from stdin will never receive an EOF. - Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the - file to which the container''s termination - message will be written is mounted into - the container''s filesystem. Message written - is intended to be brief final status, - such as an assertion failure message. - Will be truncated by the node if greater - than 4096 bytes. The total message length - across all containers will be limited - to 12kb. Defaults to /dev/termination-log. - Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination - message should be populated. File will - use the contents of terminationMessagePath - to populate the container status message - on both success and failure. FallbackToLogsOnError - will use the last chunk of container log - output if the termination message file - is empty and the container exited with - an error. The log output is limited to - 2048 bytes or 80 lines, whichever is smaller. - Defaults to File. Cannot be updated. - type: string - tty: - description: Whether this container should - allocate a TTY for itself, also requires - 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of - block devices to be used by the container. - items: - description: volumeDevice describes a - mapping of a raw block device within - a container. - properties: - devicePath: - description: devicePath is the path - inside of the container that the - device will be mapped to. - type: string - name: - description: name must match the name - of a persistentVolumeClaim in the - pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the - container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting - of a Volume within a container. - properties: - mountPath: - description: Path within the container - at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines - how mounts are propagated from the - host to container and the other - way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name - of a Volume. - type: string - readOnly: - description: Mounted read-only if - true, read-write otherwise (false - or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume - from which the container's volume - should be mounted. Defaults to "" - (volume's root). - type: string - subPathExpr: - description: Expanded path within - the volume from which the container's - volume should be mounted. Behaves - similarly to SubPath but environment - variable references $(VAR_NAME) - are expanded using the container's - environment. Defaults to "" (volume's - root). SubPathExpr and SubPath are - mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. - If not specified, the container runtime's - default will be used, which might be configured - in the container image. Cannot be updated. - type: string - required: - - name type: object type: array + x-kubernetes-preserve-unknown-fields: true timeout: description: Timeout defines the maximum amount of time Velero should wait for the initContainers diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 2acede9be5..fd9b62843f 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -36,7 +36,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), diff --git a/hack/restore-crd-patch-v1.json b/hack/restore-crd-patch-v1.json deleted file mode 100644 index b8b729d0d8..0000000000 --- a/hack/restore-crd-patch-v1.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - { "op": "replace", "path": "/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] } -] diff --git a/hack/restore-crd-patch-v1beta1.json b/hack/restore-crd-patch-v1beta1.json deleted file mode 100644 index cbaa90a02b..0000000000 --- a/hack/restore-crd-patch-v1beta1.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - { "op": "replace", "path": "/spec/validation/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] } -] diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index 70b9a942bd..fb7be399eb 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -47,7 +47,7 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ # Generate apiextensions.k8s.io/v1 # Generate manifests e.g. CRD, RBAC etc. controller-gen \ - crd:crdVersions=v1\ + crd:crdVersions=v1 \ paths=./pkg/apis/velero/v1/... \ rbac:roleName=velero-perms \ paths=./pkg/controller/... \ @@ -55,13 +55,4 @@ controller-gen \ object \ paths=./pkg/apis/velero/v1/... -# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395 -# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked -# as required or have default values to ensure merging of list map items work as expected. -# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not -# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395. -# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed. -kubectl patch -f config/crd/v1/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch-v1.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched -mv /tmp/velero.io_restores-yaml.patched config/crd/v1/bases/velero.io_restores.yaml - go generate ./config/crd/v1/crds diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 96cb18bc66..83c756bd53 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -131,10 +131,10 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( pod.Spec.InitContainers = pod.Spec.InitContainers[1:] } - hooksFromAnnotations := getInitRestoreHookFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log) - if hooksFromAnnotations != nil { + initContainerFromAnnotations := getInitContainerFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log) + if initContainerFromAnnotations != nil { log.Infof("Handling InitRestoreHooks from pod annotations") - initContainers = append(initContainers, hooksFromAnnotations.InitContainers...) + initContainers = append(initContainers, *initContainerFromAnnotations) } else { log.Infof("Handling InitRestoreHooks from RestoreSpec") // pod did not have the annotations appropriate for restore hooks @@ -155,7 +155,22 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( } for _, hook := range rh.RestoreHooks { if hook.Init != nil { - initContainers = append(initContainers, hook.Init.InitContainers...) + containers := make([]corev1api.Container, 0) + for _, raw := range hook.Init.InitContainers { + container := corev1api.Container{} + err := ValidateContainer(raw.Raw) + if err != nil { + log.Errorf("invalid Restore Init hook: %s", err.Error()) + return nil, err + } + err = json.Unmarshal(raw.Raw, &container) + if err != nil { + log.Errorf("fail to Unmarshal hook Init into container: %s", err.Error()) + return nil, errors.WithStack(err) + } + containers = append(containers, container) + } + initContainers = append(initContainers, containers...) } } } @@ -350,7 +365,7 @@ type ResourceRestoreHook struct { RestoreHooks []velerov1api.RestoreResourceHook } -func getInitRestoreHookFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *velerov1api.InitRestoreHook { +func getInitContainerFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *corev1api.Container { containerImage := annotations[podRestoreHookInitContainerImageAnnotationKey] containerName := annotations[podRestoreHookInitContainerNameAnnotationKey] command := annotations[podRestoreHookInitContainerCommandAnnotationKey] @@ -373,15 +388,13 @@ func getInitRestoreHookFromAnnotation(podName string, annotations map[string]str log.Infof("Pod %s has no %s annotation, using generated name %s for initContainer", podName, podRestoreHookInitContainerNameAnnotationKey, containerName) } - return &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - { - Image: containerImage, - Name: containerName, - Command: parseStringToCommand(command), - }, - }, + initContainer := corev1api.Container{ + Image: containerImage, + Name: containerName, + Command: parseStringToCommand(command), } + + return &initContainer } // GetRestoreHooksFromSpec returns a list of ResourceRestoreHooks from the restore Spec. @@ -406,7 +419,7 @@ func GetRestoreHooksFromSpec(hooksSpec *velerov1api.RestoreHooks) ([]ResourceRes if rs.LabelSelector != nil { ls, err := metav1.LabelSelectorAsSelector(rs.LabelSelector) if err != nil { - return nil, errors.WithStack(err) + return []ResourceRestoreHook{}, errors.WithStack(err) } rh.Selector.LabelSelector = ls } @@ -526,3 +539,17 @@ func GroupRestoreExecHooks( return byContainer, nil } + +// ValidateContainer validate whether a map contains mandatory k8s Container fields. +// mandatory fields include name, image and commands. +func ValidateContainer(raw []byte) error { + container := corev1api.Container{} + err := json.Unmarshal(raw, &container) + if err != nil { + return err + } + if len(container.Command) <= 0 || len(container.Name) <= 0 || len(container.Image) <= 0 { + return fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field") + } + return nil +} diff --git a/internal/hook/item_hook_handler_test.go b/internal/hook/item_hook_handler_test.go index 267c413a11..9f8267808c 100644 --- a/internal/hook/item_hook_handler_test.go +++ b/internal/hook/item_hook_handler_test.go @@ -1191,11 +1191,11 @@ func TestGroupRestoreExecHooks(t *testing.T) { } } -func TestGetInitRestoreHookFromAnnotations(t *testing.T) { +func TestGetInitContainerFromAnnotations(t *testing.T) { testCases := []struct { name string inputAnnotations map[string]string - expected velerov1api.InitRestoreHook + expected *corev1api.Container expectNil bool }{ { @@ -1223,12 +1223,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { podRestoreHookInitContainerNameAnnotationKey: "", podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full", }, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), }, { name: "should generate container name when container name is missing", @@ -1237,22 +1233,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full", }, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), }, { name: "should return expected init container when all annotations are specified", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1262,12 +1250,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { { name: "should return expected init container when all annotations are specified with command as a JSON array", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"a", "b", "c"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"a", "b", "c"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1277,12 +1261,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { { name: "should return expected init container when all annotations are specified with command as malformed a JSON array", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"[foobarbaz"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"[foobarbaz"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1293,15 +1273,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := getInitRestoreHookFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger()) + actualInitContainer := getInitContainerFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger()) if tc.expectNil { - assert.Nil(t, actual) + assert.Nil(t, actualInitContainer) return } - assert.NotEmpty(t, actual.InitContainers[0].Name) - assert.Equal(t, len(tc.expected.InitContainers), len(actual.InitContainers)) - assert.Equal(t, tc.expected.InitContainers[0].Image, actual.InitContainers[0].Image) - assert.Equal(t, tc.expected.InitContainers[0].Command, actual.InitContainers[0].Command) + assert.NotEmpty(t, actualInitContainer.Name) + assert.Equal(t, tc.expected.Image, actualInitContainer.Image) + assert.Equal(t, tc.expected.Command, actualInitContainer.Command) }) } } @@ -1347,11 +1326,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { PostHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, @@ -1369,11 +1348,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, @@ -1539,9 +1518,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("should-not exist", "does-not-matter"). - Command([]string{""}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("should-not exist", "does-not-matter"). + Command([]string{""}).ResultRawExtension(), }, }, }, @@ -1556,6 +1535,9 @@ func TestHandleRestoreHooks(t *testing.T) { Name: "app1", Namespace: "default", }, + Spec: corev1api.PodSpec{ + InitContainers: []corev1api.Container{}, + }, }, expectedError: nil, expectedPod: &corev1api.Pod{ @@ -1582,11 +1564,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1643,11 +1625,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1680,11 +1662,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1733,11 +1715,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1795,11 +1777,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1868,9 +1850,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1911,9 +1893,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1922,6 +1904,37 @@ func TestHandleRestoreHooks(t *testing.T) { }, namespaceMapping: map[string]string{"default": "new"}, }, + { + name: "Invalid InitContainer in Restore hook should return nil as pod, and error.", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "new", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field"), + expectedPod: nil, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("new"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + ResultRawExtension(), + }, + }, + }, + }, + }, + }, + }, } for _, tc := range testCases { @@ -1931,10 +1944,32 @@ func TestHandleRestoreHooks(t *testing.T) { assert.NoError(t, err) actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks, tc.namespaceMapping) assert.Equal(t, tc.expectedError, err) - actualPod := new(corev1api.Pod) - err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) - assert.NoError(t, err) - assert.Equal(t, tc.expectedPod, actualPod) + if actual != nil { + actualPod := new(corev1api.Pod) + err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) + assert.NoError(t, err) + assert.Equal(t, tc.expectedPod, actualPod) + } }) } } + +func TestValidateContainer(t *testing.T) { + valid := `{"name": "test", "image": "busybox", "command": ["pwd"]}` + noName := `{"image": "busybox", "command": ["pwd"]}` + noImage := `{"name": "test", "command": ["pwd"]}` + noCommand := `{"name": "test", "image": "busybox"}` + expectedError := fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field") + + // valid string should return nil as result. + assert.Equal(t, nil, ValidateContainer([]byte(valid))) + + // noName string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noName))) + + // noImage string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noImage))) + + // noCommand string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noCommand))) +} diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index 251f1a0453..f36e7348ce 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -17,8 +17,8 @@ limitations under the License. package v1 import ( - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) // RestoreSpec defines the specification for a Velero restore. @@ -208,9 +208,10 @@ type ExecRestoreHook struct { // InitRestoreHook is a hook that adds an init container to a PodSpec to run commands before the // workload pod is able to start. type InitRestoreHook struct { + // +kubebuilder:pruning:PreserveUnknownFields // InitContainers is list of init containers to be added to a pod during its restore. // +optional - InitContainers []v1.Container `json:"initContainers"` + InitContainers []runtime.RawExtension `json:"initContainers"` // Timeout defines the maximum amount of time Velero should wait for the initContainers to complete. // +optional diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index a35e5e1ef5..3d806d2bb8 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -764,7 +764,7 @@ func (in *InitRestoreHook) DeepCopyInto(out *InitRestoreHook) { *out = *in if in.InitContainers != nil { in, out := &in.InitContainers, &out.InitContainers - *out = make([]corev1.Container, len(*in)) + *out = make([]runtime.RawExtension, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/builder/container_builder.go b/pkg/builder/container_builder.go index 80c99955d7..da6215637f 100644 --- a/pkg/builder/container_builder.go +++ b/pkg/builder/container_builder.go @@ -17,9 +17,11 @@ limitations under the License. package builder import ( + "encoding/json" "strings" corev1api "k8s.io/api/core/v1" + apimachineryRuntime "k8s.io/apimachinery/pkg/runtime" ) // ContainerBuilder builds Container objects @@ -89,6 +91,17 @@ func (b *ContainerBuilder) Result() *corev1api.Container { return b.object } +// ResultRawExtension returns the Container as runtime.RawExtension. +func (b *ContainerBuilder) ResultRawExtension() apimachineryRuntime.RawExtension { + result, err := json.Marshal(b.object) + if err != nil { + return apimachineryRuntime.RawExtension{} + } + return apimachineryRuntime.RawExtension{ + Raw: result, + } +} + // Args sets the container's Args. func (b *ContainerBuilder) Args(args ...string) *ContainerBuilder { b.object.Args = append(b.object.Args, args...) diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 3799259fad..559611fe09 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -38,6 +38,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" + hook "github.com/vmware-tanzu/velero/internal/hook" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" @@ -328,6 +329,22 @@ func (c *restoreController) validateAndComplete(restore *api.Restore, pluginMana return backupInfo{} } + // validate Restore Init Hook's InitContainers + restoreHooks, err := hook.GetRestoreHooksFromSpec(&restore.Spec.Hooks) + if err != nil { + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error()) + } + for _, resource := range restoreHooks { + for _, h := range resource.RestoreHooks { + for _, container := range h.Init.InitContainers { + err = hook.ValidateContainer(container.Raw) + if err != nil { + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error()) + } + } + } + } + // if ScheduleName is specified, fill in BackupName with the most recent successful backup from // the schedule if restore.Spec.ScheduleName != "" { diff --git a/pkg/restore/init_restorehook_pod_action_test.go b/pkg/restore/init_restorehook_pod_action_test.go index f4f8f4dfde..c69d3c23f9 100644 --- a/pkg/restore/init_restorehook_pod_action_test.go +++ b/pkg/restore/init_restorehook_pod_action_test.go @@ -90,11 +90,11 @@ func TestInitContainerRestoreHookPodActionExecute(t *testing.T) { PostHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, From 088eb9b83cd00ba5e8cc3c5c59aa678973dd9f1f Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Thu, 4 Aug 2022 15:20:02 +0800 Subject: [PATCH 240/366] repo credentials (#5167) Signed-off-by: Lyndon-Li --- changelogs/unreleased/5167-lyndon | 1 + internal/credentials/getter.go | 24 +++ internal/credentials/mocks/FileStore.go | 49 +++++ internal/credentials/mocks/SecretStore.go | 49 +++++ internal/credentials/secret_store.go | 56 ++++++ pkg/cmd/server/server.go | 3 +- .../pod_volume_backup_controller.go | 3 +- .../pod_volume_restore_controller.go | 3 +- pkg/repository/keys/keys.go | 75 ++++++++ pkg/repository/keys/keys_test.go | 30 +++ pkg/repository/provider/unified_repo.go | 79 +++++--- pkg/repository/provider/unified_repo_test.go | 179 ++++++++++++++++-- pkg/restic/repository_manager.go | 3 +- 13 files changed, 503 insertions(+), 51 deletions(-) create mode 100644 changelogs/unreleased/5167-lyndon create mode 100644 internal/credentials/getter.go create mode 100644 internal/credentials/mocks/FileStore.go create mode 100644 internal/credentials/mocks/SecretStore.go create mode 100644 internal/credentials/secret_store.go create mode 100644 pkg/repository/keys/keys.go create mode 100644 pkg/repository/keys/keys_test.go diff --git a/changelogs/unreleased/5167-lyndon b/changelogs/unreleased/5167-lyndon new file mode 100644 index 0000000000..7bef82dccb --- /dev/null +++ b/changelogs/unreleased/5167-lyndon @@ -0,0 +1 @@ +Add changes for Kopia Integration: Unified Repository Provider - Repo Password \ No newline at end of file diff --git a/internal/credentials/getter.go b/internal/credentials/getter.go new file mode 100644 index 0000000000..c890c05666 --- /dev/null +++ b/internal/credentials/getter.go @@ -0,0 +1,24 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package credentials + +// CredentialGetter is a collection of interfaces for interacting with credentials +// that are stored in different targets +type CredentialGetter struct { + FromFile FileStore + FromSecret SecretStore +} diff --git a/internal/credentials/mocks/FileStore.go b/internal/credentials/mocks/FileStore.go new file mode 100644 index 0000000000..3fce9e843d --- /dev/null +++ b/internal/credentials/mocks/FileStore.go @@ -0,0 +1,49 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + v1 "k8s.io/api/core/v1" +) + +// FileStore is an autogenerated mock type for the FileStore type +type FileStore struct { + mock.Mock +} + +// Path provides a mock function with given fields: selector +func (_m *FileStore) Path(selector *v1.SecretKeySelector) (string, error) { + ret := _m.Called(selector) + + var r0 string + if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok { + r0 = rf(selector) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok { + r1 = rf(selector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewFileStore interface { + mock.TestingT + Cleanup(func()) +} + +// NewFileStore creates a new instance of FileStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFileStore(t mockConstructorTestingTNewFileStore) *FileStore { + mock := &FileStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/credentials/mocks/SecretStore.go b/internal/credentials/mocks/SecretStore.go new file mode 100644 index 0000000000..5494511c9e --- /dev/null +++ b/internal/credentials/mocks/SecretStore.go @@ -0,0 +1,49 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + v1 "k8s.io/api/core/v1" +) + +// SecretStore is an autogenerated mock type for the SecretStore type +type SecretStore struct { + mock.Mock +} + +// Get provides a mock function with given fields: selector +func (_m *SecretStore) Get(selector *v1.SecretKeySelector) (string, error) { + ret := _m.Called(selector) + + var r0 string + if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok { + r0 = rf(selector) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok { + r1 = rf(selector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewSecretStore interface { + mock.TestingT + Cleanup(func()) +} + +// NewSecretStore creates a new instance of SecretStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSecretStore(t mockConstructorTestingTNewSecretStore) *SecretStore { + mock := &SecretStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/credentials/secret_store.go b/internal/credentials/secret_store.go new file mode 100644 index 0000000000..f4d2111a57 --- /dev/null +++ b/internal/credentials/secret_store.go @@ -0,0 +1,56 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package credentials + +import ( + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/util/kube" +) + +// SecretStore defines operations for interacting with credentials +// that are stored in Secret. +type SecretStore interface { + // Get returns the secret key defined by the given selector + Get(selector *corev1api.SecretKeySelector) (string, error) +} + +type namespacedSecretStore struct { + client kbclient.Client + namespace string +} + +// NewNamespacedSecretStore returns a SecretStore which can interact with credentials +// for the given namespace. +func NewNamespacedSecretStore(client kbclient.Client, namespace string) (SecretStore, error) { + return &namespacedSecretStore{ + client: client, + namespace: namespace, + }, nil +} + +// Buffer returns the secret key defined by the given selector. +func (n *namespacedSecretStore) Get(selector *corev1api.SecretKeySelector) (string, error) { + creds, err := kube.GetSecretKey(n.client, n.namespace, selector) + if err != nil { + return "", errors.Wrap(err, "unable to get key for secret") + } + + return string(creds), nil +} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f257c96e72..642e1baf5b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -80,6 +80,7 @@ import ( "github.com/vmware-tanzu/velero/internal/storage" "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" ) const ( @@ -519,7 +520,7 @@ func (s *server) initRestic() error { } // ensure the repo key secret is set up - if err := restic.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil { + if err := repokey.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil { return err } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 8bce92ed7c..d7fab35482 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/metrics" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -324,7 +325,7 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log log.WithField("path", path).Debugf("Found path matching glob") // Temporary credentials. - details.credsFile, err = r.CredsFileStore.Path(restic.RepoKeySelector()) + details.credsFile, err = r.CredsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return nil, errors.Wrap(err, "creating temporary Restic credentials file") } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3315dae3ba..e52de6fdbc 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -39,6 +39,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -241,7 +242,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error identifying path of volume") } - credsFile, err := c.credentialsFileStore.Path(restic.RepoKeySelector()) + credsFile, err := c.credentialsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return errors.Wrap(err, "error creating temp restic credentials file") } diff --git a/pkg/repository/keys/keys.go b/pkg/repository/keys/keys.go new file mode 100644 index 0000000000..3da876e28f --- /dev/null +++ b/pkg/repository/keys/keys.go @@ -0,0 +1,75 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package keys + +import ( + "context" + + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + + "github.com/vmware-tanzu/velero/pkg/builder" +) + +const ( + credentialsSecretName = "velero-restic-credentials" + credentialsKey = "repository-password" + + encryptionKey = "static-passw0rd" +) + +func EnsureCommonRepositoryKey(secretClient corev1client.SecretsGetter, namespace string) error { + _, err := secretClient.Secrets(namespace).Get(context.TODO(), credentialsSecretName, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return errors.WithStack(err) + } + if err == nil { + return nil + } + + // if we got here, we got an IsNotFound error, so we need to create the key + + secret := &corev1api.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: credentialsSecretName, + }, + Type: corev1api.SecretTypeOpaque, + Data: map[string][]byte{ + credentialsKey: []byte(encryptionKey), + }, + } + + if _, err = secretClient.Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { + return errors.Wrapf(err, "error creating %s secret", credentialsSecretName) + } + + return nil +} + +// RepoKeySelector returns the SecretKeySelector which can be used to fetch +// the restic repository key. +func RepoKeySelector() *corev1api.SecretKeySelector { + // For now, all restic repos share the same key so we don't need the repoName to fetch it. + // When we move to full-backup encryption, we'll likely have a separate key per restic repo + // (all within the Velero server's namespace) so RepoKeySelector will need to select the key + // for that repo. + return builder.ForSecretKeySelector(credentialsSecretName, credentialsKey).Result() +} diff --git a/pkg/repository/keys/keys_test.go b/pkg/repository/keys/keys_test.go new file mode 100644 index 0000000000..3102b58c85 --- /dev/null +++ b/pkg/repository/keys/keys_test.go @@ -0,0 +1,30 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package keys + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRepoKeySelector(t *testing.T) { + selector := RepoKeySelector() + + require.Equal(t, credentialsSecretName, selector.Name) + require.Equal(t, credentialsKey, selector.Key) +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 49cef09ce4..8ddff57999 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -28,15 +28,16 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/util/ownership" ) type unifiedRepoProvider struct { - credentialsFileStore credentials.FileStore - workPath string - repoService udmrepo.BackupRepoService - log logrus.FieldLogger + credentialGetter credentials.CredentialGetter + workPath string + repoService udmrepo.BackupRepoService + log logrus.FieldLogger } // this func is assigned to a package-level variable so it can be @@ -47,18 +48,30 @@ var getGCPCredentials = repoconfig.GetGCPCredentials var getS3BucketRegion = repoconfig.GetAWSBucketRegion var getAzureStorageDomain = repoconfig.GetAzureStorageDomain +type localFuncTable struct { + getRepoPassword func(credentials.SecretStore, RepoParam) (string, error) + getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) + getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error) +} + +var funcTable = localFuncTable{ + getRepoPassword: getRepoPassword, + getStorageVariables: getStorageVariables, + getStorageCredentials: getStorageCredentials, +} + // NewUnifiedRepoProvider creates the service provider for Unified Repo // workPath is the path for Unified Repo to store some local information // workPath could be empty, if so, the default path will be used func NewUnifiedRepoProvider( - credentialFileStore credentials.FileStore, + credentialGetter credentials.CredentialGetter, workPath string, log logrus.FieldLogger, ) (Provider, error) { repo := unifiedRepoProvider{ - credentialsFileStore: credentialFileStore, - workPath: workPath, - log: log, + credentialGetter: credentialGetter, + workPath: workPath, + log: log, } repo.repoService = createRepoService(log) @@ -120,15 +133,38 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p return nil } -func (urp *unifiedRepoProvider) getRepoPassword(param RepoParam) (string, error) { - ///TODO: get repo password +func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) { + if secretStore == nil { + return "", errors.New("invalid credentials interface") + } + + buf, err := secretStore.Get(repokey.RepoKeySelector()) + if err != nil { + return "", errors.Wrap(err, "error to get password buffer") + } - return "", nil + return strings.TrimSpace(string(buf)), nil } func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) { + repoPassword, err := funcTable.getRepoPassword(urp.credentialGetter.FromSecret, param) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo password") + } + + storeVar, err := funcTable.getStorageVariables(param.BackupLocation, param.SubDir) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get storage variables") + } + + storeCred, err := funcTable.getStorageCredentials(param.BackupLocation, urp.credentialGetter.FromFile) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo credentials") + } + repoOption := udmrepo.RepoOptions{ StorageType: getStorageType(param.BackupLocation), + RepoPassword: repoPassword, ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)), Ownership: udmrepo.OwnershipOptions{ Username: ownership.GetRepositoryOwner().Username, @@ -138,27 +174,10 @@ func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOpti GeneralOptions: make(map[string]string), } - repoPassword, err := urp.getRepoPassword(param) - if err != nil { - return repoOption, errors.Wrap(err, "error to get repo password") - } - - repoOption.RepoPassword = repoPassword - - storeVar, err := getStorageVariables(param.BackupLocation, param.SubDir) - if err != nil { - return repoOption, errors.Wrap(err, "error to get storage variables") - } - for k, v := range storeVar { repoOption.StorageOptions[k] = v } - storeCred, err := getStorageCredentials(param.BackupLocation, urp.credentialsFileStore) - if err != nil { - return repoOption, errors.Wrap(err, "error to get repo credential env") - } - for k, v := range storeCred { repoOption.StorageOptions[k] = v } @@ -187,6 +206,10 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr result := make(map[string]string) var err error + if credentialsFileStore == nil { + return map[string]string{}, errors.New("invalid credentials interface") + } + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) if !repoconfig.IsBackendTypeValid(backendType) { return map[string]string{}, errors.New("invalid storage provider") diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index ee78c7b5d2..f2cccb8e5d 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -22,25 +22,35 @@ import ( awscredentials "github.com/aws/aws-sdk-go/aws/credentials" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" - filecredentials "github.com/vmware-tanzu/velero/internal/credentials" + velerocredentials "github.com/vmware-tanzu/velero/internal/credentials" + credmock "github.com/vmware-tanzu/velero/internal/credentials/mocks" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" ) func TestGetStorageCredentials(t *testing.T) { testCases := []struct { name string backupLocation velerov1api.BackupStorageLocation - credFileStore filecredentials.FileStore + credFileStore *credmock.FileStore + credStoreError error + credStorePath string getAzureCredentials func(map[string]string) (string, string, error) getS3Credentials func(map[string]string) (awscredentials.Value, error) getGCPCredentials func(map[string]string) string expected map[string]string expectedErr string }{ + { + name: "invalid credentials file store interface", + expected: map[string]string{}, + expectedErr: "invalid credentials interface", + }, { name: "invalid provider", backupLocation: velerov1api.BackupStorageLocation{ @@ -48,8 +58,9 @@ func TestGetStorageCredentials(t *testing.T) { Provider: "invalid-provider", }, }, - expected: map[string]string{}, - expectedErr: "invalid storage provider", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "invalid storage provider", }, { name: "credential section exists in BSL, file store fail", @@ -59,9 +70,10 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("", errors.New("fake error")), - expected: map[string]string{}, - expectedErr: "error get credential file in bsl: fake error", + credFileStore: new(credmock.FileStore), + credStoreError: errors.New("fake error"), + expected: map[string]string{}, + expectedErr: "error get credential file in bsl: fake error", }, { name: "aws, Credential section not exists in BSL", @@ -78,7 +90,7 @@ func TestGetStorageCredentials(t *testing.T) { AccessKeyID: "from: " + config["credentialsFile"], }, nil }, - + credFileStore: new(credmock.FileStore), expected: map[string]string{ "accessKeyID": "from: credentials-from-config-map", "providerName": "", @@ -97,7 +109,8 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + credFileStore: new(credmock.FileStore), + credStorePath: "credentials-from-credential-key", getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { return awscredentials.Value{ AccessKeyID: "from: " + config["credentialsFile"], @@ -121,12 +134,12 @@ func TestGetStorageCredentials(t *testing.T) { }, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("", nil), getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { return awscredentials.Value{}, errors.New("fake error") }, - expected: map[string]string{}, - expectedErr: "error get s3 credentials: fake error", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "error get s3 credentials: fake error", }, { name: "azure, Credential section exists in BSL", @@ -139,7 +152,8 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + credFileStore: new(credmock.FileStore), + credStorePath: "credentials-from-credential-key", getAzureCredentials: func(config map[string]string) (string, string, error) { return "storage account from: " + config["credentialsFile"], "", nil }, @@ -162,9 +176,9 @@ func TestGetStorageCredentials(t *testing.T) { getAzureCredentials: func(config map[string]string) (string, string, error) { return "", "", errors.New("fake error") }, - - expected: map[string]string{}, - expectedErr: "error get azure credentials: fake error", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "error get azure credentials: fake error", }, { name: "gcp, Credential section not exists in BSL", @@ -179,7 +193,7 @@ func TestGetStorageCredentials(t *testing.T) { getGCPCredentials: func(config map[string]string) string { return "credentials-from-config-map" }, - + credFileStore: new(credmock.FileStore), expected: map[string]string{ "credFile": "credentials-from-config-map", }, @@ -192,7 +206,13 @@ func TestGetStorageCredentials(t *testing.T) { getS3Credentials = tc.getS3Credentials getGCPCredentials = tc.getGCPCredentials - actual, err := getStorageCredentials(&tc.backupLocation, tc.credFileStore) + var fileStore velerocredentials.FileStore + if tc.credFileStore != nil { + tc.credFileStore.On("Path", mock.Anything, mock.Anything).Return(tc.credStorePath, tc.credStoreError) + fileStore = tc.credFileStore + } + + actual, err := getStorageCredentials(&tc.backupLocation, fileStore) require.Equal(t, tc.expected, actual) @@ -412,3 +432,124 @@ func TestGetStorageVariables(t *testing.T) { }) } } + +func TestGetRepoPassword(t *testing.T) { + testCases := []struct { + name string + getter *credmock.SecretStore + credStoreReturn string + credStoreError error + cached string + expected string + expectedErr string + }{ + { + name: "invalid secret interface", + expectedErr: "invalid credentials interface", + }, + { + name: "error from secret interface", + getter: new(credmock.SecretStore), + credStoreError: errors.New("fake error"), + expectedErr: "error to get password buffer: fake error", + }, + { + name: "secret with whitespace", + getter: new(credmock.SecretStore), + credStoreReturn: " fake-passwor d ", + expected: "fake-passwor d", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var secretStore velerocredentials.SecretStore + if tc.getter != nil { + tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError) + secretStore = tc.getter + } + + urp := unifiedRepoProvider{ + credentialGetter: velerocredentials.CredentialGetter{ + FromSecret: secretStore, + }, + } + + password, err := getRepoPassword(urp.credentialGetter.FromSecret, RepoParam{}) + + require.Equal(t, tc.expected, password) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestGetRepoOption(t *testing.T) { + testCases := []struct { + name string + funcTable localFuncTable + getRepoPassword func(velerocredentials.SecretStore, RepoParam) (string, error) + getStorageCredentials func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) + getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) + expected udmrepo.RepoOptions + expectedErr string + }{ + { + name: "get repo password fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "", errors.New("fake-error-1") + }, + }, + expectedErr: "error to get repo password: fake-error-1", + }, + { + name: "get storage variable fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "fake-password", nil + }, + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, errors.New("fake-error-2") + }, + }, + expectedErr: "error to get storage variables: fake-error-2", + }, + { + name: "get storage credentials fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "fake-password", nil + }, + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, errors.New("fake-error-3") + }, + }, + expectedErr: "error to get repo credentials: fake-error-3", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + funcTable = tc.funcTable + urp := unifiedRepoProvider{} + + password, err := urp.getRepoOption(RepoParam{}) + + require.Equal(t, tc.expected, password) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index f0ab633868..69927b851c 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -36,6 +36,7 @@ import ( velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -242,7 +243,7 @@ func (rm *repositoryManager) Forget(ctx context.Context, snapshot SnapshotIdenti } func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { - file, err := rm.credentialsFileStore.Path(RepoKeySelector()) + file, err := rm.credentialsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return err } From eb08bdeb62dc53611dfb008f87f9cf4791e3c4a6 Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Wed, 3 Aug 2022 20:00:49 +0800 Subject: [PATCH 241/366] fix issue#2413 by treating namespaces with exclude label as excludedNamespaces Signed-off-by: allenxu404 --- changelogs/unreleased/5178-allenxu404 | 2 ++ pkg/controller/backup_controller.go | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5178-allenxu404 diff --git a/changelogs/unreleased/5178-allenxu404 b/changelogs/unreleased/5178-allenxu404 new file mode 100644 index 0000000000..2c1b4b0e04 --- /dev/null +++ b/changelogs/unreleased/5178-allenxu404 @@ -0,0 +1,2 @@ +Treat namespaces with exclude label as excludedNamespaces +Related issue: #2413 diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 3f54e8f4f3..387e922bd6 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -70,6 +70,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" + corev1api "k8s.io/api/core/v1" kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -257,7 +258,6 @@ func (c *backupController) processBackup(key string) error { log.Debug("Preparing backup request") request := c.prepareBackupRequest(original) - if len(request.Status.ValidationErrors) > 0 { request.Status.Phase = velerov1api.BackupPhaseFailedValidation } else { @@ -436,6 +436,15 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Annotations[velerov1api.SourceClusterK8sMajorVersionAnnotation] = c.discoveryHelper.ServerVersion().Major request.Annotations[velerov1api.SourceClusterK8sMinorVersionAnnotation] = c.discoveryHelper.ServerVersion().Minor + // Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces + // Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces + namespaces, excludeLabel := corev1api.NamespaceList{}, "velero.io/exclude-from-backup" + if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{excludeLabel: "true"}); err == nil { + for _, ns := range namespaces.Items { + request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, ns.Name) + } + } + // validate the included/excluded resources for _, err := range collections.ValidateIncludesExcludes(request.Spec.IncludedResources, request.Spec.ExcludedResources) { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded resource lists: %v", err)) From 092fc01e8de1ef053459f24a5163487a355bd03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 5 Aug 2022 17:15:38 +0800 Subject: [PATCH 242/366] Splic pkg/restic package (#5143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit splits the pkg/restic package into several packages to support Kopia integration works Fixes #5055 Signed-off-by: Wenkai Yin(尹文开) --- Tiltfile | 2 +- changelogs/unreleased/5143-ywk253100 | 1 + pkg/backup/backup.go | 8 +- pkg/backup/backup_test.go | 4 +- pkg/backup/item_backupper.go | 6 +- .../restic_repository_controller.go | 1 - pkg/{restic => podvolume}/backupper.go | 38 +- pkg/podvolume/backupper_factory.go | 88 +++ pkg/{restic => podvolume}/backupper_test.go | 2 +- pkg/{restic => podvolume}/mocks/restorer.go | 10 +- pkg/{restic => podvolume}/restorer.go | 33 +- pkg/podvolume/restorer_factory.go | 85 +++ pkg/podvolume/util.go | 198 ++++++ pkg/podvolume/util_test.go | 563 ++++++++++++++++ pkg/repository/config/config.go | 2 - .../ensurer.go} | 16 +- .../repo_locker.go => repository/locker.go} | 21 +- pkg/restic/common.go | 172 ----- pkg/restic/common_test.go | 610 ------------------ pkg/restic/mocks/repository_manager.go | 17 +- pkg/restic/repository_manager.go | 79 +-- pkg/restore/restic_restore_action.go | 3 +- pkg/restore/restore.go | 16 +- pkg/restore/restore_test.go | 12 +- 24 files changed, 1051 insertions(+), 936 deletions(-) create mode 100644 changelogs/unreleased/5143-ywk253100 rename pkg/{restic => podvolume}/backupper.go (90%) create mode 100644 pkg/podvolume/backupper_factory.go rename pkg/{restic => podvolume}/backupper_test.go (99%) rename pkg/{restic => podvolume}/mocks/restorer.go (60%) rename pkg/{restic => podvolume}/restorer.go (87%) create mode 100644 pkg/podvolume/restorer_factory.go create mode 100644 pkg/podvolume/util.go create mode 100644 pkg/podvolume/util_test.go rename pkg/{restic/repository_ensurer.go => repository/ensurer.go} (93%) rename pkg/{restic/repo_locker.go => repository/locker.go} (79%) diff --git a/Tiltfile b/Tiltfile index 02da1df56d..0d9a642632 100644 --- a/Tiltfile +++ b/Tiltfile @@ -7,7 +7,7 @@ k8s_yaml([ 'config/crd/v1/bases/velero.io_downloadrequests.yaml', 'config/crd/v1/bases/velero.io_podvolumebackups.yaml', 'config/crd/v1/bases/velero.io_podvolumerestores.yaml', - 'config/crd/v1/bases/velero.io_resticrepositories.yaml', + 'config/crd/v1/bases/velero.io_backuprepositories.yaml', 'config/crd/v1/bases/velero.io_restores.yaml', 'config/crd/v1/bases/velero.io_schedules.yaml', 'config/crd/v1/bases/velero.io_serverstatusrequests.yaml', diff --git a/changelogs/unreleased/5143-ywk253100 b/changelogs/unreleased/5143-ywk253100 new file mode 100644 index 0000000000..cf52136456 --- /dev/null +++ b/changelogs/unreleased/5143-ywk253100 @@ -0,0 +1 @@ +This commit splits the pkg/restic package into several packages to support Kopia integration works \ No newline at end of file diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 697be85009..d026b09cff 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -46,7 +46,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -74,7 +74,7 @@ type kubernetesBackupper struct { dynamicFactory client.DynamicFactory discoveryHelper discovery.Helper podCommandExecutor podexec.PodCommandExecutor - resticBackupperFactory restic.BackupperFactory + resticBackupperFactory podvolume.BackupperFactory resticTimeout time.Duration defaultVolumesToRestic bool clientPageSize int @@ -100,7 +100,7 @@ func NewKubernetesBackupper( discoveryHelper discovery.Helper, dynamicFactory client.DynamicFactory, podCommandExecutor podexec.PodCommandExecutor, - resticBackupperFactory restic.BackupperFactory, + resticBackupperFactory podvolume.BackupperFactory, resticTimeout time.Duration, defaultVolumesToRestic bool, clientPageSize int, @@ -234,7 +234,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, ctx, cancelFunc := context.WithTimeout(context.Background(), podVolumeTimeout) defer cancelFunc() - var resticBackupper restic.Backupper + var resticBackupper podvolume.Backupper if kb.resticBackupperFactory != nil { resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup) if err != nil { diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 53554e8b38..cf6a4269f8 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -47,7 +47,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -2595,7 +2595,7 @@ func TestBackupWithHooks(t *testing.T) { type fakeResticBackupperFactory struct{} -func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup) (restic.Backupper, error) { +func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup) (podvolume.Backupper, error) { return &fakeResticBackupper{}, nil } diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index bd40c3bdba..df5d48cd26 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -42,7 +42,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/volume" ) @@ -53,7 +53,7 @@ type itemBackupper struct { tarWriter tarWriter dynamicFactory client.DynamicFactory discoveryHelper discovery.Helper - resticBackupper restic.Backupper + resticBackupper podvolume.Backupper resticSnapshotTracker *pvcSnapshotTracker volumeSnapshotterGetter VolumeSnapshotterGetter @@ -149,7 +149,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. - for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { + for _, volume := range podvolume.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 3f27de36fc..c3ca1505ac 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -26,7 +26,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/pkg/restic/backupper.go b/pkg/podvolume/backupper.go similarity index 90% rename from pkg/restic/backupper.go rename to pkg/podvolume/backupper.go index 589b396f39..116a5c4e77 100644 --- a/pkg/restic/backupper.go +++ b/pkg/podvolume/backupper.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" @@ -30,7 +30,9 @@ import ( "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -41,11 +43,12 @@ type Backupper interface { } type backupper struct { - ctx context.Context - repoManager *repositoryManager - repoEnsurer *repositoryEnsurer - pvcClient corev1client.PersistentVolumeClaimsGetter - pvClient corev1client.PersistentVolumesGetter + ctx context.Context + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + pvClient corev1client.PersistentVolumesGetter results map[string]chan *velerov1api.PodVolumeBackup resultsLock sync.Mutex @@ -53,19 +56,21 @@ type backupper struct { func newBackupper( ctx context.Context, - repoManager *repositoryManager, - repoEnsurer *repositoryEnsurer, + repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, podVolumeBackupInformer cache.SharedIndexInformer, + veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, log logrus.FieldLogger, ) *backupper { b := &backupper{ - ctx: ctx, - repoManager: repoManager, - repoEnsurer: repoEnsurer, - pvcClient: pvcClient, - pvClient: pvClient, + ctx: ctx, + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + pvClient: pvClient, results: make(map[string]chan *velerov1api.PodVolumeBackup), } @@ -109,8 +114,8 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. // get a single non-exclusive lock since we'll wait for all individual // backups to be complete before releasing it. - b.repoManager.repoLocker.Lock(repo.Name) - defer b.repoManager.repoLocker.Unlock(repo.Name) + b.repoLocker.Lock(repo.Name) + defer b.repoLocker.Unlock(repo.Name) resultsChan := make(chan *velerov1api.PodVolumeBackup) @@ -176,9 +181,10 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. log.Warnf("Volume %s is declared in pod %s/%s but not mounted by any container, skipping", volumeName, pod.Namespace, pod.Name) continue } + // TODO: Remove the hard-coded uploader type before v1.10 FC volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, "restic", pvc) - if volumeBackup, err = b.repoManager.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { + if volumeBackup, err = b.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { errs = append(errs, err) continue } diff --git a/pkg/podvolume/backupper_factory.go b/pkg/podvolume/backupper_factory.go new file mode 100644 index 0000000000..aaaa5e2ac1 --- /dev/null +++ b/pkg/podvolume/backupper_factory.go @@ -0,0 +1,88 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podvolume + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" +) + +// BackupperFactory can construct pod volumes backuppers. +type BackupperFactory interface { + // NewBackupper returns a pod volumes backupper for use during a single Velero backup. + NewBackupper(context.Context, *velerov1api.Backup) (Backupper, error) +} + +func NewBackupperFactory(repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, + veleroClient clientset.Interface, + pvcClient corev1client.PersistentVolumeClaimsGetter, + pvClient corev1client.PersistentVolumesGetter, + repoInformerSynced cache.InformerSynced, + log logrus.FieldLogger) BackupperFactory { + return &backupperFactory{ + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + pvClient: pvClient, + repoInformerSynced: repoInformerSynced, + log: log, + } +} + +type backupperFactory struct { + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + pvClient corev1client.PersistentVolumesGetter + repoInformerSynced cache.InformerSynced + log logrus.FieldLogger +} + +func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1api.Backup) (Backupper, error) { + informer := velerov1informers.NewFilteredPodVolumeBackupInformer( + bf.veleroClient, + backup.Namespace, + 0, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + func(opts *metav1.ListOptions) { + opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.BackupUIDLabel, backup.UID) + }, + ) + + b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, bf.log) + + go informer.Run(ctx.Done()) + if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, bf.repoInformerSynced) { + return nil, errors.New("timed out waiting for caches to sync") + } + + return b, nil +} diff --git a/pkg/restic/backupper_test.go b/pkg/podvolume/backupper_test.go similarity index 99% rename from pkg/restic/backupper_test.go rename to pkg/podvolume/backupper_test.go index 8969f6efaf..fb0cacd1a5 100644 --- a/pkg/restic/backupper_test.go +++ b/pkg/podvolume/backupper_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" diff --git a/pkg/restic/mocks/restorer.go b/pkg/podvolume/mocks/restorer.go similarity index 60% rename from pkg/restic/mocks/restorer.go rename to pkg/podvolume/mocks/restorer.go index 7f4f5c1d82..fd210aa00f 100644 --- a/pkg/restic/mocks/restorer.go +++ b/pkg/podvolume/mocks/restorer.go @@ -2,8 +2,10 @@ package mocks -import mock "github.com/stretchr/testify/mock" -import restic "github.com/vmware-tanzu/velero/pkg/restic" +import ( + mock "github.com/stretchr/testify/mock" + "github.com/vmware-tanzu/velero/pkg/podvolume" +) // Restorer is an autogenerated mock type for the Restorer type type Restorer struct { @@ -11,11 +13,11 @@ type Restorer struct { } // RestorePodVolumes provides a mock function with given fields: _a0 -func (_m *Restorer) RestorePodVolumes(_a0 restic.RestoreData) []error { +func (_m *Restorer) RestorePodVolumes(_a0 podvolume.RestoreData) []error { ret := _m.Called(_a0) var r0 []error - if rf, ok := ret.Get(0).(func(restic.RestoreData) []error); ok { + if rf, ok := ret.Get(0).(func(podvolume.RestoreData) []error); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { diff --git a/pkg/restic/restorer.go b/pkg/podvolume/restorer.go similarity index 87% rename from pkg/restic/restorer.go rename to pkg/podvolume/restorer.go index e747b0b6d5..daa3a630d7 100644 --- a/pkg/restic/restorer.go +++ b/pkg/podvolume/restorer.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" @@ -28,7 +28,9 @@ import ( "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -46,10 +48,11 @@ type Restorer interface { } type restorer struct { - ctx context.Context - repoManager *repositoryManager - repoEnsurer *repositoryEnsurer - pvcClient corev1client.PersistentVolumeClaimsGetter + ctx context.Context + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter resultsLock sync.Mutex results map[string]chan *velerov1api.PodVolumeRestore @@ -57,17 +60,19 @@ type restorer struct { func newRestorer( ctx context.Context, - rm *repositoryManager, - repoEnsurer *repositoryEnsurer, + repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, podVolumeRestoreInformer cache.SharedIndexInformer, + veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, log logrus.FieldLogger, ) *restorer { r := &restorer{ - ctx: ctx, - repoManager: rm, - repoEnsurer: repoEnsurer, - pvcClient: pvcClient, + ctx: ctx, + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, results: make(map[string]chan *velerov1api.PodVolumeRestore), } @@ -108,8 +113,8 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { // get a single non-exclusive lock since we'll wait for all individual // restores to be complete before releasing it. - r.repoManager.repoLocker.Lock(repo.Name) - defer r.repoManager.repoLocker.Unlock(repo.Name) + r.repoLocker.Lock(repo.Name) + defer r.repoLocker.Unlock(repo.Name) resultsChan := make(chan *velerov1api.PodVolumeRestore) @@ -142,7 +147,7 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { // TODO: Remove the hard-coded uploader type before v1.10 FC volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, "restic", pvc) - if err := errorOnly(r.repoManager.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { + if err := errorOnly(r.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) continue } diff --git a/pkg/podvolume/restorer_factory.go b/pkg/podvolume/restorer_factory.go new file mode 100644 index 0000000000..3432f4be3b --- /dev/null +++ b/pkg/podvolume/restorer_factory.go @@ -0,0 +1,85 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podvolume + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" +) + +// RestorerFactory can construct pod volumes restorers. +type RestorerFactory interface { + // NewRestorer returns a pod volumes restorer for use during a single Velero restore. + NewRestorer(context.Context, *velerov1api.Restore) (Restorer, error) +} + +func NewRestorerFactory(repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, + veleroClient clientset.Interface, + pvcClient corev1client.PersistentVolumeClaimsGetter, + repoInformerSynced cache.InformerSynced, + log logrus.FieldLogger) RestorerFactory { + return &restorerFactory{ + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + repoInformerSynced: repoInformerSynced, + log: log, + } +} + +type restorerFactory struct { + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + repoInformerSynced cache.InformerSynced + log logrus.FieldLogger +} + +func (rf *restorerFactory) NewRestorer(ctx context.Context, restore *velerov1api.Restore) (Restorer, error) { + informer := velerov1informers.NewFilteredPodVolumeRestoreInformer( + rf.veleroClient, + restore.Namespace, + 0, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + func(opts *metav1.ListOptions) { + opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.RestoreUIDLabel, restore.UID) + }, + ) + + r := newRestorer(ctx, rf.repoLocker, rf.repoEnsurer, informer, rf.veleroClient, rf.pvcClient, rf.log) + + go informer.Run(ctx.Done()) + if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rf.repoInformerSynced) { + return nil, errors.New("timed out waiting for cache to sync") + } + + return r, nil +} diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go new file mode 100644 index 0000000000..57baacc106 --- /dev/null +++ b/pkg/podvolume/util.go @@ -0,0 +1,198 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podvolume + +import ( + "strings" + + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +const ( + // PVCNameAnnotation is the key for the annotation added to + // pod volume backups when they're for a PVC. + PVCNameAnnotation = "velero.io/pvc-name" + + // Deprecated. + // + // TODO(2.0): remove + podAnnotationPrefix = "snapshot.velero.io/" + + // VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes + // need to be backed up using restic. + VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" + + // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes + // should be excluded from restic backup. + VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" +) + +// GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, +// of the PodVolumeBackups that exist for the provided pod. +func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string { + volumes := make(map[string]string) + + for _, pvb := range podVolumeBackups { + if !isPVBMatchPod(pvb, pod.GetName(), sourcePodNs) { + continue + } + + // skip PVBs without a snapshot ID since there's nothing + // to restore (they could be failed, or for empty volumes). + if pvb.Status.SnapshotID == "" { + continue + } + + // If the volume came from a projected or DownwardAPI source, skip its restore. + // This allows backups affected by https://github.com/vmware-tanzu/velero/issues/3863 + // or https://github.com/vmware-tanzu/velero/issues/4053 to be restored successfully. + if volumeHasNonRestorableSource(pvb.Spec.Volume, pod.Spec.Volumes) { + continue + } + + volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID + } + + if len(volumes) > 0 { + return volumes + } + + return getPodSnapshotAnnotations(pod) +} + +func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool { + return podName == pvb.Spec.Pod.Name && namespace == pvb.Spec.Pod.Namespace +} + +// volumeHasNonRestorableSource checks if the given volume exists in the list of podVolumes +// and returns true if the volume's source is not restorable. This is true for volumes with +// a Projected or DownwardAPI source. +func volumeHasNonRestorableSource(volumeName string, podVolumes []corev1api.Volume) bool { + var volume corev1api.Volume + for _, v := range podVolumes { + if v.Name == volumeName { + volume = v + break + } + } + return volume.Projected != nil || volume.DownwardAPI != nil +} + +// getPodSnapshotAnnotations returns a map, of volume name -> snapshot id, +// of all snapshots for this pod. +// TODO(2.0) to remove +// Deprecated: we will stop using pod annotations to record restic snapshot IDs after they're taken, +// therefore we won't need to check if these annotations exist. +func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { + var res map[string]string + + insertSafe := func(k, v string) { + if res == nil { + res = make(map[string]string) + } + res[k] = v + } + + for k, v := range obj.GetAnnotations() { + if strings.HasPrefix(k, podAnnotationPrefix) { + insertSafe(k[len(podAnnotationPrefix):], v) + } + } + + return res +} + +// GetVolumesToBackup returns a list of volume names to backup for +// the provided pod. +// Deprecated: Use GetPodVolumesUsingRestic instead. +func GetVolumesToBackup(obj metav1.Object) []string { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + + backupsValue := annotations[VolumesToBackupAnnotation] + if backupsValue == "" { + return nil + } + + return strings.Split(backupsValue, ",") +} + +func getVolumesToExclude(obj metav1.Object) []string { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + + return strings.Split(annotations[VolumesToExcludeAnnotation], ",") +} + +func contains(list []string, k string) bool { + for _, i := range list { + if i == k { + return true + } + } + return false +} + +// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. +func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { + if !defaultVolumesToRestic { + return GetVolumesToBackup(pod) + } + + volsToExclude := getVolumesToExclude(pod) + podVolumes := []string{} + for _, pv := range pod.Spec.Volumes { + // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods + // and therefore not accessible to the restic daemon set. + if pv.HostPath != nil { + continue + } + // don't backup volumes mounting secrets. Secrets will be backed up separately. + if pv.Secret != nil { + continue + } + // don't backup volumes mounting config maps. Config maps will be backed up separately. + if pv.ConfigMap != nil { + continue + } + // don't backup volumes mounted as projected volumes, all data in those come from kube state. + if pv.Projected != nil { + continue + } + // don't backup DownwardAPI volumes, all data in those come from kube state. + if pv.DownwardAPI != nil { + continue + } + // don't backup volumes that are included in the exclude list. + if contains(volsToExclude, pv.Name) { + continue + } + // don't include volumes that mount the default service account token. + if strings.HasPrefix(pv.Name, "default-token") { + continue + } + podVolumes = append(podVolumes, pv.Name) + } + return podVolumes +} diff --git a/pkg/podvolume/util_test.go b/pkg/podvolume/util_test.go new file mode 100644 index 0000000000..88b5746685 --- /dev/null +++ b/pkg/podvolume/util_test.go @@ -0,0 +1,563 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podvolume + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" +) + +func TestGetVolumeBackupsForPod(t *testing.T) { + tests := []struct { + name string + podVolumeBackups []*velerov1api.PodVolumeBackup + podVolumes []corev1api.Volume + podAnnotations map[string]string + podName string + sourcePodNs string + expected map[string]string + }{ + { + name: "nil annotations results in no volume backups returned", + podAnnotations: nil, + expected: nil, + }, + { + name: "empty annotations results in no volume backups returned", + podAnnotations: make(map[string]string), + expected: nil, + }, + { + name: "pod annotations with no snapshot annotation prefix results in no volume backups returned", + podAnnotations: map[string]string{"foo": "bar"}, + expected: nil, + }, + { + name: "pod annotation with only snapshot annotation prefix, results in volume backup with empty volume key", + podAnnotations: map[string]string{podAnnotationPrefix: "snapshotID"}, + expected: map[string]string{"": "snapshotID"}, + }, + { + name: "pod annotation with snapshot annotation prefix results in volume backup with volume name and snapshot ID", + podAnnotations: map[string]string{podAnnotationPrefix + "volume": "snapshotID"}, + expected: map[string]string{"volume": "snapshotID"}, + }, + { + name: "only pod annotations with snapshot annotation prefix are considered", + podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "volume1": "snapshot1", podAnnotationPrefix + "volume2": "snapshot2"}, + expected: map[string]string{"volume1": "snapshot1", "volume2": "snapshot2"}, + }, + { + name: "pod annotations are not considered if PVBs are provided", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"}, + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "volume backups are returned even if no pod annotations are present", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs with snapshot IDs are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest3-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-4").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest4-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs for the given pod are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestAnotherPod").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs which match the pod name and source pod namespace are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestAnotherPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestAnotherNS").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1"}, + }, + { + name: "volumes from PVBs that correspond to a pod volume from a projected source are not returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-projected").Result(), + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-projected").Result(), + }, + podVolumes: []corev1api.Volume{ + { + Name: "pvb-non-projected", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "pvb-projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvb-non-projected": "snapshot1"}, + }, + { + name: "volumes from PVBs that correspond to a pod volume from a DownwardAPI source are not returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-downwardapi").Result(), + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-downwardapi").Result(), + }, + podVolumes: []corev1api.Volume{ + { + Name: "pvb-non-downwardapi", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "pvb-downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvb-non-downwardapi": "snapshot1"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := &corev1api.Pod{} + pod.Annotations = test.podAnnotations + pod.Name = test.podName + pod.Spec.Volumes = test.podVolumes + + res := GetVolumeBackupsForPod(test.podVolumeBackups, pod, test.sourcePodNs) + assert.Equal(t, test.expected, res) + }) + } +} + +func TestVolumeHasNonRestorableSource(t *testing.T) { + testCases := []struct { + name string + volumeName string + podVolumes []corev1api.Volume + expected bool + }{ + { + name: "volume name not in list of volumes", + volumeName: "missing-volume", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: false, + }, + { + name: "volume name in list of volumes but not projected or DownwardAPI", + volumeName: "restorable", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: false, + }, + { + name: "volume name in list of volumes and projected", + volumeName: "projected", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: true, + }, + { + name: "volume name in list of volumes and is a DownwardAPI volume", + volumeName: "downwardapi", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := volumeHasNonRestorableSource(tc.volumeName, tc.podVolumes) + assert.Equal(t, tc.expected, actual) + }) + + } +} + +func TestGetVolumesToBackup(t *testing.T) { + tests := []struct { + name string + annotations map[string]string + expected []string + }{ + { + name: "nil annotations", + annotations: nil, + expected: nil, + }, + { + name: "no volumes to backup", + annotations: map[string]string{"foo": "bar"}, + expected: nil, + }, + { + name: "one volume to backup", + annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1"}, + expected: []string{"volume-1"}, + }, + { + name: "multiple volumes to backup", + annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1,volume-2,volume-3"}, + expected: []string{"volume-1", "volume-2", "volume-3"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := &corev1api.Pod{} + pod.Annotations = test.annotations + + res := GetVolumesToBackup(pod) + + // sort to ensure good compare of slices + sort.Strings(test.expected) + sort.Strings(res) + + assert.Equal(t, test.expected, res) + }) + } +} + +func TestGetPodVolumesUsingRestic(t *testing.T) { + testCases := []struct { + name string + pod *corev1api.Pod + expected []string + defaultVolumesToRestic bool + }{ + { + name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", + defaultVolumesToRestic: false, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude default service account token from restic backup", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic because colume mounting default service account token + {Name: "default-token-5xq45"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude host path volumes from restic backups", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude volumes mounting secrets", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude volumes mounting config maps", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude projected volumes", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{ + Sources: []corev1api.VolumeProjection{{ + Secret: &corev1api.SecretProjection{ + LocalObjectReference: corev1api.LocalObjectReference{}, + Items: nil, + Optional: nil, + }, + DownwardAPI: nil, + ConfigMap: nil, + ServiceAccountToken: nil, + }}, + DefaultMode: nil, + }, + }, + }, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude DownwardAPI volumes", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + { + Name: "downwardAPI", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{ + Items: []corev1api.DownwardAPIVolumeFile{ + { + Path: "labels", + FieldRef: &corev1api.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.labels", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) + + sort.Strings(tc.expected) + sort.Strings(actual) + assert.Equal(t, tc.expected, actual) + }) + } +} diff --git a/pkg/repository/config/config.go b/pkg/repository/config/config.go index 24dc8d6b3d..d7ed99b69e 100644 --- a/pkg/repository/config/config.go +++ b/pkg/repository/config/config.go @@ -34,9 +34,7 @@ const ( AzureBackend BackendType = "velero.io/azure" GCPBackend BackendType = "velero.io/gcp" FSBackend BackendType = "velero.io/fs" -) -const ( // CredentialsFileKey is the key within a BSL config that is checked to see if // the BSL is using its own credentials, rather than those in the environment CredentialsFileKey = "credentialsFile" diff --git a/pkg/restic/repository_ensurer.go b/pkg/repository/ensurer.go similarity index 93% rename from pkg/restic/repository_ensurer.go rename to pkg/repository/ensurer.go index d764a49c7d..15aa107014 100644 --- a/pkg/restic/repository_ensurer.go +++ b/pkg/repository/ensurer.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package repository import ( "context" @@ -35,8 +35,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" ) -// repositoryEnsurer ensures that Velero restic repositories are created and ready. -type repositoryEnsurer struct { +// RepositoryEnsurer ensures that backup repositories are created and ready. +type RepositoryEnsurer struct { log logrus.FieldLogger repoLister velerov1listers.BackupRepositoryLister repoClient velerov1client.BackupRepositoriesGetter @@ -55,8 +55,8 @@ type repoKey struct { backupLocation string } -func newRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { - r := &repositoryEnsurer{ +func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *RepositoryEnsurer { + r := &RepositoryEnsurer{ log: log, repoLister: repoInformer.Lister(), repoClient: repoClient, @@ -105,7 +105,7 @@ func repoLabels(volumeNamespace, backupLocation string) labels.Set { } } -func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { +func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation) // It's only safe to have one instance of this method executing concurrently for a @@ -190,7 +190,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam } } -func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { +func (r *RepositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { r.repoChansLock.Lock() defer r.repoChansLock.Unlock() @@ -198,7 +198,7 @@ func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRep return r.repoChans[name] } -func (r *repositoryEnsurer) repoLock(volumeNamespace, backupLocation string) *sync.Mutex { +func (r *RepositoryEnsurer) repoLock(volumeNamespace, backupLocation string) *sync.Mutex { r.repoLocksMu.Lock() defer r.repoLocksMu.Unlock() diff --git a/pkg/restic/repo_locker.go b/pkg/repository/locker.go similarity index 79% rename from pkg/restic/repo_locker.go rename to pkg/repository/locker.go index 29434753e9..20eea96359 100644 --- a/pkg/restic/repo_locker.go +++ b/pkg/repository/locker.go @@ -13,23 +13,24 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package restic + +package repository import "sync" -// repoLocker manages exclusive/non-exclusive locks for +// RepoLocker manages exclusive/non-exclusive locks for // operations against restic repositories. The semantics // of exclusive/non-exclusive locks are the same as for // a sync.RWMutex, where a non-exclusive lock is equivalent // to a read lock, and an exclusive lock is equivalent to // a write lock. -type repoLocker struct { +type RepoLocker struct { mu sync.Mutex locks map[string]*sync.RWMutex } -func newRepoLocker() *repoLocker { - return &repoLocker{ +func NewRepoLocker() *RepoLocker { + return &RepoLocker{ locks: make(map[string]*sync.RWMutex), } } @@ -37,28 +38,28 @@ func newRepoLocker() *repoLocker { // LockExclusive acquires an exclusive lock for the specified // repository. This function blocks until no other locks exist // for the repo. -func (rl *repoLocker) LockExclusive(name string) { +func (rl *RepoLocker) LockExclusive(name string) { rl.ensureLock(name).Lock() } // Lock acquires a non-exclusive lock for the specified // repository. This function blocks until no exclusive // locks exist for the repo. -func (rl *repoLocker) Lock(name string) { +func (rl *RepoLocker) Lock(name string) { rl.ensureLock(name).RLock() } // UnlockExclusive releases an exclusive lock for the repo. -func (rl *repoLocker) UnlockExclusive(name string) { +func (rl *RepoLocker) UnlockExclusive(name string) { rl.ensureLock(name).Unlock() } // Unlock releases a non-exclusive lock for the repo. -func (rl *repoLocker) Unlock(name string) { +func (rl *RepoLocker) Unlock(name string) { rl.ensureLock(name).RUnlock() } -func (rl *repoLocker) ensureLock(name string) *sync.RWMutex { +func (rl *RepoLocker) ensureLock(name string) *sync.RWMutex { rl.mu.Lock() defer rl.mu.Unlock() diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 580bd27080..6e2671625d 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -20,11 +20,9 @@ import ( "context" "fmt" "os" - "strings" "time" "github.com/pkg/errors" - corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" @@ -51,178 +49,8 @@ const ( // DefaultVolumesToRestic specifies whether restic should be used, by default, to // take backup of all pod volumes. DefaultVolumesToRestic = false - - // PVCNameAnnotation is the key for the annotation added to - // pod volume backups when they're for a PVC. - PVCNameAnnotation = "velero.io/pvc-name" - - // VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes - // need to be backed up using restic. - VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" - - // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes - // should be excluded from restic backup. - VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" - - // Deprecated. - // - // TODO(2.0): remove - podAnnotationPrefix = "snapshot.velero.io/" ) -// getPodSnapshotAnnotations returns a map, of volume name -> snapshot id, -// of all restic snapshots for this pod. -// TODO(2.0) to remove -// Deprecated: we will stop using pod annotations to record restic snapshot IDs after they're taken, -// therefore we won't need to check if these annotations exist. -func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { - var res map[string]string - - insertSafe := func(k, v string) { - if res == nil { - res = make(map[string]string) - } - res[k] = v - } - - for k, v := range obj.GetAnnotations() { - if strings.HasPrefix(k, podAnnotationPrefix) { - insertSafe(k[len(podAnnotationPrefix):], v) - } - } - - return res -} - -func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool { - return podName == pvb.Spec.Pod.Name && namespace == pvb.Spec.Pod.Namespace -} - -// volumeHasNonRestorableSource checks if the given volume exists in the list of podVolumes -// and returns true if the volume's source is not restorable. This is true for volumes with -// a Projected or DownwardAPI source. -func volumeHasNonRestorableSource(volumeName string, podVolumes []corev1api.Volume) bool { - var volume corev1api.Volume - for _, v := range podVolumes { - if v.Name == volumeName { - volume = v - break - } - } - return volume.Projected != nil || volume.DownwardAPI != nil -} - -// GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, -// of the PodVolumeBackups that exist for the provided pod. -func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string { - volumes := make(map[string]string) - - for _, pvb := range podVolumeBackups { - if !isPVBMatchPod(pvb, pod.GetName(), sourcePodNs) { - continue - } - - // skip PVBs without a snapshot ID since there's nothing - // to restore (they could be failed, or for empty volumes). - if pvb.Status.SnapshotID == "" { - continue - } - - // If the volume came from a projected or DownwardAPI source, skip its restore. - // This allows backups affected by https://github.com/vmware-tanzu/velero/issues/3863 - // or https://github.com/vmware-tanzu/velero/issues/4053 to be restored successfully. - if volumeHasNonRestorableSource(pvb.Spec.Volume, pod.Spec.Volumes) { - continue - } - - volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID - } - - if len(volumes) > 0 { - return volumes - } - - return getPodSnapshotAnnotations(pod) -} - -// GetVolumesToBackup returns a list of volume names to backup for -// the provided pod. -// Deprecated: Use GetPodVolumesUsingRestic instead. -func GetVolumesToBackup(obj metav1.Object) []string { - annotations := obj.GetAnnotations() - if annotations == nil { - return nil - } - - backupsValue := annotations[VolumesToBackupAnnotation] - if backupsValue == "" { - return nil - } - - return strings.Split(backupsValue, ",") -} - -func getVolumesToExclude(obj metav1.Object) []string { - annotations := obj.GetAnnotations() - if annotations == nil { - return nil - } - - return strings.Split(annotations[VolumesToExcludeAnnotation], ",") -} - -func contains(list []string, k string) bool { - for _, i := range list { - if i == k { - return true - } - } - return false -} - -// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. -func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { - if !defaultVolumesToRestic { - return GetVolumesToBackup(pod) - } - - volsToExclude := getVolumesToExclude(pod) - podVolumes := []string{} - for _, pv := range pod.Spec.Volumes { - // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods - // and therefore not accessible to the restic daemon set. - if pv.HostPath != nil { - continue - } - // don't backup volumes mounting secrets. Secrets will be backed up separately. - if pv.Secret != nil { - continue - } - // don't backup volumes mounting config maps. Config maps will be backed up separately. - if pv.ConfigMap != nil { - continue - } - // don't backup volumes mounted as projected volumes, all data in those come from kube state. - if pv.Projected != nil { - continue - } - // don't backup DownwardAPI volumes, all data in those come from kube state. - if pv.DownwardAPI != nil { - continue - } - // don't backup volumes that are included in the exclude list. - if contains(volsToExclude, pv.Name) { - continue - } - // don't include volumes that mount the default service account token. - if strings.HasPrefix(pv.Name, "default-token") { - continue - } - podVolumes = append(podVolumes, pv.Name) - } - return podVolumes -} - // SnapshotIdentifier uniquely identifies a restic snapshot // taken by Velero. type SnapshotIdentifier struct { diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 7f3e0c5032..fac82f9011 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -28,212 +28,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/builder" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestGetVolumeBackupsForPod(t *testing.T) { - tests := []struct { - name string - podVolumeBackups []*velerov1api.PodVolumeBackup - podVolumes []corev1api.Volume - podAnnotations map[string]string - podName string - sourcePodNs string - expected map[string]string - }{ - { - name: "nil annotations results in no volume backups returned", - podAnnotations: nil, - expected: nil, - }, - { - name: "empty annotations results in no volume backups returned", - podAnnotations: make(map[string]string), - expected: nil, - }, - { - name: "pod annotations with no snapshot annotation prefix results in no volume backups returned", - podAnnotations: map[string]string{"foo": "bar"}, - expected: nil, - }, - { - name: "pod annotation with only snapshot annotation prefix, results in volume backup with empty volume key", - podAnnotations: map[string]string{podAnnotationPrefix: "snapshotID"}, - expected: map[string]string{"": "snapshotID"}, - }, - { - name: "pod annotation with snapshot annotation prefix results in volume backup with volume name and snapshot ID", - podAnnotations: map[string]string{podAnnotationPrefix + "volume": "snapshotID"}, - expected: map[string]string{"volume": "snapshotID"}, - }, - { - name: "only pod annotations with snapshot annotation prefix are considered", - podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "volume1": "snapshot1", podAnnotationPrefix + "volume2": "snapshot2"}, - expected: map[string]string{"volume1": "snapshot1", "volume2": "snapshot2"}, - }, - { - name: "pod annotations are not considered if PVBs are provided", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"}, - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "volume backups are returned even if no pod annotations are present", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs with snapshot IDs are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest3-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-4").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest4-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs for the given pod are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestAnotherPod").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs which match the pod name and source pod namespace are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestAnotherPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestAnotherNS").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1"}, - }, - { - name: "volumes from PVBs that correspond to a pod volume from a projected source are not returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-projected").Result(), - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-projected").Result(), - }, - podVolumes: []corev1api.Volume{ - { - Name: "pvb-non-projected", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "pvb-projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvb-non-projected": "snapshot1"}, - }, - { - name: "volumes from PVBs that correspond to a pod volume from a DownwardAPI source are not returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-downwardapi").Result(), - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-downwardapi").Result(), - }, - podVolumes: []corev1api.Volume{ - { - Name: "pvb-non-downwardapi", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "pvb-downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvb-non-downwardapi": "snapshot1"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pod := &corev1api.Pod{} - pod.Annotations = test.podAnnotations - pod.Name = test.podName - pod.Spec.Volumes = test.podVolumes - - res := GetVolumeBackupsForPod(test.podVolumeBackups, pod, test.sourcePodNs) - assert.Equal(t, test.expected, res) - }) - } -} - -func TestGetVolumesToBackup(t *testing.T) { - tests := []struct { - name string - annotations map[string]string - expected []string - }{ - { - name: "nil annotations", - annotations: nil, - expected: nil, - }, - { - name: "no volumes to backup", - annotations: map[string]string{"foo": "bar"}, - expected: nil, - }, - { - name: "one volume to backup", - annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1"}, - expected: []string{"volume-1"}, - }, - { - name: "multiple volumes to backup", - annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1,volume-2,volume-3"}, - expected: []string{"volume-1", "volume-2", "volume-3"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pod := &corev1api.Pod{} - pod.Annotations = test.annotations - - res := GetVolumesToBackup(pod) - - // sort to ensure good compare of slices - sort.Strings(test.expected) - sort.Strings(res) - - assert.Equal(t, test.expected, res) - }) - } -} - func TestGetSnapshotsInBackup(t *testing.T) { tests := []struct { name string @@ -419,410 +216,3 @@ func TestTempCACertFile(t *testing.T) { os.Remove(fileName) } - -func TestGetPodVolumesUsingRestic(t *testing.T) { - testCases := []struct { - name string - pod *corev1api.Pod - expected []string - defaultVolumesToRestic bool - }{ - { - name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", - defaultVolumesToRestic: false, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude default service account token from restic backup", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic because colume mounting default service account token - {Name: "default-token-5xq45"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude host path volumes from restic backups", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude volumes mounting secrets", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude volumes mounting config maps", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude projected volumes", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{ - Sources: []corev1api.VolumeProjection{{ - Secret: &corev1api.SecretProjection{ - LocalObjectReference: corev1api.LocalObjectReference{}, - Items: nil, - Optional: nil, - }, - DownwardAPI: nil, - ConfigMap: nil, - ServiceAccountToken: nil, - }}, - DefaultMode: nil, - }, - }, - }, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude DownwardAPI volumes", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - { - Name: "downwardAPI", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{ - Items: []corev1api.DownwardAPIVolumeFile{ - { - Path: "labels", - FieldRef: &corev1api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels", - }, - }, - }, - }, - }, - }, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) - - sort.Strings(tc.expected) - sort.Strings(actual) - assert.Equal(t, tc.expected, actual) - }) - } -} - -func TestIsPVBMatchPod(t *testing.T) { - testCases := []struct { - name string - pvb velerov1api.PodVolumeBackup - podName string - sourcePodNs string - expected bool - }{ - { - name: "should match PVB and pod", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "matching-pod", - sourcePodNs: "matching-namespace", - expected: true, - }, - { - name: "should not match PVB and pod, pod name mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "not-matching-pod", - sourcePodNs: "matching-namespace", - expected: false, - }, - { - name: "should not match PVB and pod, pod namespace mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "matching-pod", - sourcePodNs: "not-matching-namespace", - expected: false, - }, - { - name: "should not match PVB and pod, pod name and namespace mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "not-matching-pod", - sourcePodNs: "not-matching-namespace", - expected: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := isPVBMatchPod(&tc.pvb, tc.podName, tc.sourcePodNs) - assert.Equal(t, tc.expected, actual) - }) - - } -} - -func TestVolumeHasNonRestorableSource(t *testing.T) { - testCases := []struct { - name string - volumeName string - podVolumes []corev1api.Volume - expected bool - }{ - { - name: "volume name not in list of volumes", - volumeName: "missing-volume", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: false, - }, - { - name: "volume name in list of volumes but not projected or DownwardAPI", - volumeName: "restorable", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: false, - }, - { - name: "volume name in list of volumes and projected", - volumeName: "projected", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: true, - }, - { - name: "volume name in list of volumes and is a DownwardAPI volume", - volumeName: "downwardapi", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := volumeHasNonRestorableSource(tc.volumeName, tc.podVolumes) - assert.Equal(t, tc.expected, actual) - }) - - } -} diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/restic/mocks/repository_manager.go index de8770c375..5533706741 100644 --- a/pkg/restic/mocks/repository_manager.go +++ b/pkg/restic/mocks/repository_manager.go @@ -23,6 +23,7 @@ import ( restic "github.com/vmware-tanzu/velero/pkg/restic" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" ) // RepositoryManager is an autogenerated mock type for the RepositoryManager type @@ -73,15 +74,15 @@ func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { } // NewBackupper provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (restic.Backupper, error) { +func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (podvolume.Backupper, error) { ret := _m.Called(_a0, _a1) - var r0 restic.Backupper - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) restic.Backupper); ok { + var r0 podvolume.Backupper + if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) podvolume.Backupper); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(restic.Backupper) + r0 = ret.Get(0).(podvolume.Backupper) } } @@ -96,15 +97,15 @@ func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) ( } // NewRestorer provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (restic.Restorer, error) { +func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (podvolume.Restorer, error) { ret := _m.Called(_a0, _a1) - var r0 restic.Restorer - if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) restic.Restorer); ok { + var r0 podvolume.Restorer + if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) podvolume.Restorer); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(restic.Restorer) + r0 = ret.Get(0).(podvolume.Restorer) } } diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 69927b851c..39961fc02c 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -18,16 +18,13 @@ package restic import ( "context" - "fmt" "os" "strconv" "github.com/pkg/errors" "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/internal/credentials" @@ -36,6 +33,8 @@ import ( velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -62,23 +61,9 @@ type RepositoryManager interface { // available snapshots in a repo. Forget(context.Context, SnapshotIdentifier) error - BackupperFactory - - RestorerFactory -} + podvolume.BackupperFactory -// BackupperFactory can construct restic backuppers. -type BackupperFactory interface { - // NewBackupper returns a restic backupper for use during a single - // Velero backup. - NewBackupper(context.Context, *velerov1api.Backup) (Backupper, error) -} - -// RestorerFactory can construct restic restorers. -type RestorerFactory interface { - // NewRestorer returns a restic restorer for use during a single - // Velero restore. - NewRestorer(context.Context, *velerov1api.Restore) (Restorer, error) + podvolume.RestorerFactory } type repositoryManager struct { @@ -88,13 +73,15 @@ type repositoryManager struct { repoInformerSynced cache.InformerSynced kbClient kbclient.Client log logrus.FieldLogger - repoLocker *repoLocker - repoEnsurer *repositoryEnsurer + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer fileSystem filesystem.Interface ctx context.Context pvcClient corev1client.PersistentVolumeClaimsGetter pvClient corev1client.PersistentVolumesGetter credentialsFileStore credentials.FileStore + podvolume.BackupperFactory + podvolume.RestorerFactory } const ( @@ -132,56 +119,18 @@ func NewRepositoryManager( log: log, ctx: ctx, - repoLocker: newRepoLocker(), - repoEnsurer: newRepositoryEnsurer(repoInformer, repoClient, log), + repoLocker: repository.NewRepoLocker(), + repoEnsurer: repository.NewRepositoryEnsurer(repoInformer, repoClient, log), fileSystem: filesystem.NewFileSystem(), } + rm.BackupperFactory = podvolume.NewBackupperFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, + rm.pvClient, rm.repoInformerSynced, rm.log) + rm.RestorerFactory = podvolume.NewRestorerFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, + rm.repoInformerSynced, rm.log) return rm, nil } -func (rm *repositoryManager) NewBackupper(ctx context.Context, backup *velerov1api.Backup) (Backupper, error) { - informer := velerov1informers.NewFilteredPodVolumeBackupInformer( - rm.veleroClient, - backup.Namespace, - 0, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(opts *metav1.ListOptions) { - opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.BackupUIDLabel, backup.UID) - }, - ) - - b := newBackupper(ctx, rm, rm.repoEnsurer, informer, rm.pvcClient, rm.pvClient, rm.log) - - go informer.Run(ctx.Done()) - if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rm.repoInformerSynced) { - return nil, errors.New("timed out waiting for caches to sync") - } - - return b, nil -} - -func (rm *repositoryManager) NewRestorer(ctx context.Context, restore *velerov1api.Restore) (Restorer, error) { - informer := velerov1informers.NewFilteredPodVolumeRestoreInformer( - rm.veleroClient, - restore.Namespace, - 0, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(opts *metav1.ListOptions) { - opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.RestoreUIDLabel, restore.UID) - }, - ) - - r := newRestorer(ctx, rm, rm.repoEnsurer, informer, rm.pvcClient, rm.log) - - go informer.Run(ctx.Done()) - if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rm.repoInformerSynced) { - return nil, errors.New("timed out waiting for cache to sync") - } - - return r, nil -} - func (rm *repositoryManager) InitRepo(repo *velerov1api.BackupRepository) error { // restic init requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index 91b4a6761c..ba9f9cb0a3 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -96,7 +97,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu for i := range podVolumeBackupList.Items { podVolumeBackups = append(podVolumeBackups, &podVolumeBackupList.Items[i]) } - volumeSnapshots := restic.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) + volumeSnapshots := podvolume.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) if len(volumeSnapshots) == 0 { log.Debug("No restic backups found for pod") return velero.NewRestoreItemActionExecuteOutput(input.Item), nil diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 691027c7c6..11008b55e8 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -59,7 +59,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -104,7 +104,7 @@ type kubernetesRestorer struct { discoveryHelper discovery.Helper dynamicFactory client.DynamicFactory namespaceClient corev1.NamespaceInterface - resticRestorerFactory restic.RestorerFactory + resticRestorerFactory podvolume.RestorerFactory resticTimeout time.Duration resourceTerminatingTimeout time.Duration resourcePriorities []string @@ -122,7 +122,7 @@ func NewKubernetesRestorer( dynamicFactory client.DynamicFactory, resourcePriorities []string, namespaceClient corev1.NamespaceInterface, - resticRestorerFactory restic.RestorerFactory, + resticRestorerFactory podvolume.RestorerFactory, resticTimeout time.Duration, resourceTerminatingTimeout time.Duration, logger logrus.FieldLogger, @@ -248,7 +248,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( ctx, cancelFunc := go_context.WithTimeout(go_context.Background(), podVolumeTimeout) defer cancelFunc() - var resticRestorer restic.Restorer + var resticRestorer podvolume.Restorer if kr.resticRestorerFactory != nil { resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, req.Restore) if err != nil { @@ -338,7 +338,7 @@ type restoreContext struct { restoreItemActions []framework.RestoreItemResolvedAction itemSnapshotterActions []framework.ItemSnapshotterResolvedAction volumeSnapshotterGetter VolumeSnapshotterGetter - resticRestorer restic.Restorer + resticRestorer podvolume.Restorer resticWaitGroup sync.WaitGroup resticErrs chan error pvsToProvision sets.String @@ -1394,7 +1394,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // Do not create podvolumerestore when current restore excludes pv/pvc if ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumeClaims.String()) && ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumes.String()) && - len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { + len(podvolume.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { restorePodVolumeBackups(ctx, createdObj, originalNamespace) } } @@ -1549,7 +1549,7 @@ func restorePodVolumeBackups(ctx *restoreContext, createdObj *unstructured.Unstr return } - data := restic.RestoreData{ + data := podvolume.RestoreData{ Restore: ctx.restore, Pod: pod, PodVolumeBackups: ctx.podVolumeBackups, @@ -1631,7 +1631,7 @@ func hasResticBackup(unstructuredPV *unstructured.Unstructured, ctx *restoreCont var found bool for _, pvb := range ctx.podVolumeBackups { - if pvb.Spec.Pod.Namespace == pv.Spec.ClaimRef.Namespace && pvb.GetAnnotations()[restic.PVCNameAnnotation] == pv.Spec.ClaimRef.Name { + if pvb.Spec.Pod.Namespace == pv.Spec.ClaimRef.Namespace && pvb.GetAnnotations()[podvolume.PVCNameAnnotation] == pv.Spec.ClaimRef.Name { found = true break } diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 7655253d10..404d45e1a5 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -48,8 +48,8 @@ import ( velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restic" - resticmocks "github.com/vmware-tanzu/velero/pkg/restic/mocks" + "github.com/vmware-tanzu/velero/pkg/podvolume" + uploadermocks "github.com/vmware-tanzu/velero/pkg/podvolume/mocks" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -2681,10 +2681,10 @@ func TestRestorePersistentVolumes(t *testing.T) { } type fakeResticRestorerFactory struct { - restorer *resticmocks.Restorer + restorer *uploadermocks.Restorer } -func (f *fakeResticRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (restic.Restorer, error) { +func (f *fakeResticRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (podvolume.Restorer, error) { return f.restorer, nil } @@ -2749,7 +2749,7 @@ func TestRestoreWithRestic(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { h := newHarness(t) - restorer := new(resticmocks.Restorer) + restorer := new(uploadermocks.Restorer) defer restorer.AssertExpectations(t) h.restorer.resticRestorerFactory = &fakeResticRestorerFactory{ restorer: restorer, @@ -2773,7 +2773,7 @@ func TestRestoreWithRestic(t *testing.T) { // the restore process adds these labels before restoring, so we must add them here too otherwise they won't match pod.Labels = map[string]string{"velero.io/backup-name": tc.backup.Name, "velero.io/restore-name": tc.restore.Name} - expectedArgs := restic.RestoreData{ + expectedArgs := podvolume.RestoreData{ Restore: tc.restore, Pod: pod, PodVolumeBackups: tc.podVolumeBackups, From 7af1e23614c97dc1d0e78e9be086d0d69818871e Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 4 Aug 2022 17:19:31 +0800 Subject: [PATCH 243/366] Add annotation "pv.kubernetes.io/migrated-to" for CSI checking. 1. Also checking annotation "pv.kubernetes.io/migrated-to" to find out whether volume is provisioned by CSI. 2. Add UT cases. Signed-off-by: Xun Jiang --- changelogs/unreleased/5181-jxun | 1 + .../pod_volume_backup_controller.go | 15 +------- .../pod_volume_restore_controller.go | 17 ++------- pkg/util/kube/utils.go | 36 +++++++++++++++---- pkg/util/kube/utils_test.go | 17 +++++++++ 5 files changed, 52 insertions(+), 34 deletions(-) create mode 100644 changelogs/unreleased/5181-jxun diff --git a/changelogs/unreleased/5181-jxun b/changelogs/unreleased/5181-jxun new file mode 100644 index 0000000000..4333691d26 --- /dev/null +++ b/changelogs/unreleased/5181-jxun @@ -0,0 +1 @@ +Add annotation "pv.kubernetes.io/migrated-to" for CSI checking. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 8bce92ed7c..420e3ca9cf 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -208,19 +208,6 @@ func (r *PodVolumeBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *PodVolumeBackupReconciler) singlePathMatch(path string) (string, error) { - matches, err := r.FileSystem.Glob(path) - if err != nil { - return "", errors.WithStack(err) - } - - if len(matches) != 1 { - return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) - } - - return matches[0], nil -} - // getParentSnapshot finds the most recent completed PodVolumeBackup for the // specified PVC and returns its Restic snapshot ID. Any errors encountered are // logged but not returned since they do not prevent a backup from proceeding. @@ -317,7 +304,7 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(pvb.Spec.Pod.UID), volDir) log.WithField("pathGlob", pathGlob).Debug("Looking for path matching glob") - path, err := r.singlePathMatch(pathGlob) + path, err := kube.SinglePathMatch(pathGlob, r.FileSystem, log) if err != nil { return nil, errors.Wrap(err, "identifying unique volume path on host") } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3315dae3ba..14671834e7 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -215,19 +215,6 @@ func getResticInitContainerIndex(pod *corev1api.Pod) int { return -1 } -func singlePathMatch(path string) (string, error) { - matches, err := filepath.Glob(path) - if err != nil { - return "", errors.WithStack(err) - } - - if len(matches) != 1 { - return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) - } - - return matches[0], nil -} - func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *velerov1api.PodVolumeRestore, pod *corev1api.Pod, log logrus.FieldLogger) error { volumeDir, err := kube.GetVolumeDirectory(ctx, log, pod, req.Spec.Volume, c.Client) if err != nil { @@ -236,7 +223,9 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve // Get the full path of the new volume's directory as mounted in the daemonset pod, which // will look like: /host_pods//volumes// - volumePath, err := singlePathMatch(fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir)) + volumePath, err := kube.SinglePathMatch( + fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir), + c.fileSystem, log) if err != nil { return errors.Wrap(err, "error identifying path of volume") } diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 24b2ef6c70..bf7ac0011c 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -34,15 +34,20 @@ import ( "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) // These annotations are taken from the Kubernetes persistent volume/persistent volume claim controller. // They cannot be directly importing because they are part of the kubernetes/kubernetes package, and importing that package is unsupported. // Their values are well-known and slow changing. They're duplicated here as constants to provide compile-time checking. // Originals can be found in kubernetes/kubernetes/pkg/controller/volume/persistentvolume/util/util.go. -const KubeAnnBindCompleted = "pv.kubernetes.io/bind-completed" -const KubeAnnBoundByController = "pv.kubernetes.io/bound-by-controller" -const KubeAnnDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by" +const ( + KubeAnnBindCompleted = "pv.kubernetes.io/bind-completed" + KubeAnnBoundByController = "pv.kubernetes.io/bound-by-controller" + KubeAnnDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by" + KubeAnnMigratedTo = "pv.kubernetes.io/migrated-to" +) // NamespaceAndName returns a string in the format / func NamespaceAndName(objMeta metav1.Object) string { @@ -163,6 +168,9 @@ func GetVolumeDirectory(ctx context.Context, log logrus.FieldLogger, pod *corev1 return pvc.Spec.VolumeName, nil } +// isProvisionedByCSI function checks whether this is a CSI PV by annotation. +// Either "pv.kubernetes.io/provisioned-by" or "pv.kubernetes.io/migrated-to" indicates +// PV is provisioned by CSI. func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, kbClient client.Client) (bool, error) { if pv.Spec.CSI != nil { return true, nil @@ -171,14 +179,15 @@ func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, // Refer to https://github.com/vmware-tanzu/velero/issues/4496 for more details if pv.Annotations != nil { driverName := pv.Annotations[KubeAnnDynamicallyProvisioned] - if len(driverName) > 0 { + migratedDriver := pv.Annotations[KubeAnnMigratedTo] + if len(driverName) > 0 || len(migratedDriver) > 0 { list := &storagev1api.CSIDriverList{} if err := kbClient.List(context.TODO(), list); err != nil { return false, err } for _, driver := range list.Items { - if driverName == driver.Name { - log.Debugf("the annotation %s=%s indicates the volume is provisioned by a CSI driver", KubeAnnDynamicallyProvisioned, driverName) + if driverName == driver.Name || migratedDriver == driver.Name { + log.Debugf("the annotation %s or %s equals to %s indicates the volume is provisioned by a CSI driver", KubeAnnDynamicallyProvisioned, KubeAnnMigratedTo, driverName) return true, nil } } @@ -187,6 +196,21 @@ func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, return false, nil } +// SinglePathMatch function will be called by PVB and PVR controller to check whether pass-in volume path is valid. +// Check whether there is only one match by the path's pattern (/host_pods/%s/volumes/*/volume_name/[mount|]). +func SinglePathMatch(path string, fs filesystem.Interface, log logrus.FieldLogger) (string, error) { + matches, err := fs.Glob(path) + if err != nil { + return "", errors.WithStack(err) + } + if len(matches) != 1 { + return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) + } + + log.Debugf("This is a valid volume path: %s.", matches[0]) + return matches[0], nil +} + // IsV1CRDReady checks a v1 CRD to see if it's ready, with both the Established and NamesAccepted conditions. func IsV1CRDReady(crd *apiextv1.CustomResourceDefinition) bool { var isEstablished, namesAccepted bool diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index 4a6db60695..178fa425fc 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -197,6 +197,13 @@ func TestGetVolumeDirectorySuccess(t *testing.T) { pv: builder.ForPersistentVolume("a-pv").ObjectMeta(builder.WithAnnotations(KubeAnnDynamicallyProvisioned, "csi.test.com")).Result(), want: "a-pv/mount", }, + { + name: "Volume with CSI annotation 'pv.kubernetes.io/migrated-to' appends '/mount' to the volume name", + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(), + pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(), + pv: builder.ForPersistentVolume("a-pv").ObjectMeta(builder.WithAnnotations(KubeAnnMigratedTo, "csi.test.com")).Result(), + want: "a-pv/mount", + }, } csiDriver := storagev1api.CSIDriver{ @@ -425,3 +432,13 @@ func TestIsCRDReady(t *testing.T) { _, err = IsCRDReady(obj) assert.NotNil(t, err) } + +func TestSinglePathMatch(t *testing.T) { + fakeFS := velerotest.NewFakeFileSystem() + fakeFS.MkdirAll("testDir1/subpath", 0755) + fakeFS.MkdirAll("testDir2/subpath", 0755) + + _, err := SinglePathMatch("./*/subpath", fakeFS, logrus.StandardLogger()) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "expected one matching path") +} From e5d828a2a4e77e28301454638f312883659dcfcf Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Mon, 8 Aug 2022 12:05:08 +0800 Subject: [PATCH 244/366] modify variables defination and expose err Signed-off-by: allenxu404 --- pkg/controller/backup_controller.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 387e922bd6..5363e2baff 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -438,11 +438,13 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces // Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces - namespaces, excludeLabel := corev1api.NamespaceList{}, "velero.io/exclude-from-backup" - if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{excludeLabel: "true"}); err == nil { + namespaces := corev1api.NamespaceList{} + if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{"velero.io/exclude-from-backup": "true"}); err == nil { for _, ns := range namespaces.Items { request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, ns.Name) } + } else { + request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("error getting namespace list: %v", err)) } // validate the included/excluded resources From f8d9cfdb84723e0dc97ade3d6738156464b331df Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 6 Jul 2022 15:51:42 +0800 Subject: [PATCH 245/366] Make CSI snapshot creation timeout configurable for backup and schedule. Signed-off-by: Xun Jiang --- changelogs/unreleased/5104-jxun | 1 + config/crd/v1/bases/velero.io_backups.yaml | 5 +++++ config/crd/v1/bases/velero.io_schedules.yaml | 5 +++++ config/crd/v1/crds/crds.go | 14 +++++++------- pkg/apis/velero/v1/backup.go | 6 ++++++ pkg/apis/velero/v1/zz_generated.deepcopy.go | 1 + pkg/builder/backup_builder.go | 6 ++++++ pkg/cmd/cli/backup/create.go | 5 ++++- pkg/cmd/cli/backup/create_test.go | 3 +++ pkg/cmd/cli/schedule/create.go | 1 + pkg/cmd/server/server.go | 6 +++++- pkg/controller/backup_controller.go | 15 ++++++++++++--- site/content/docs/main/api-types/backup.md | 4 ++++ site/content/docs/main/api-types/schedule.md | 4 ++++ site/content/docs/main/examples.md | 4 ++-- site/content/docs/v1.9/api-types/backup.md | 5 +++++ site/content/docs/v1.9/api-types/schedule.md | 5 +++++ 17 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/5104-jxun diff --git a/changelogs/unreleased/5104-jxun b/changelogs/unreleased/5104-jxun new file mode 100644 index 0000000000..56c6c4f1e9 --- /dev/null +++ b/changelogs/unreleased/5104-jxun @@ -0,0 +1 @@ +Make CSI snapshot creation timeout configurable. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index 03bfed1ffb..eebfedf705 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -37,6 +37,11 @@ spec: spec: description: BackupSpec defines the specification for a Velero backup. properties: + csiSnapshotTimeout: + description: CSISnapshotTimeout specifies the time used to wait for + CSI VolumeSnapshot status turns to ReadyToUse during creation, before + returning error as timeout. The default value is 10 minute. + type: string defaultVolumesToRestic: description: DefaultVolumesToRestic specifies whether restic should be used to take a backup of all pod volumes by default. diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index c391b1f5f1..c3e0a69fa4 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -61,6 +61,11 @@ spec: description: Template is the definition of the Backup to be run on the provided schedule properties: + csiSnapshotTimeout: + description: CSISnapshotTimeout specifies the time used to wait + for CSI VolumeSnapshot status turns to ReadyToUse during creation, + before returning error as timeout. The default value is 10 minute. + type: string defaultVolumesToRestic: description: DefaultVolumesToRestic specifies whether restic should be used to take a backup of all pod volumes by default. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 4b1197a47f..b7199e7421 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,17 +29,17 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Msܸrw\xfd\x8a.\xe5\xe0\xf7\xaa4\xa3u吔n^\xd9[Q\xedƫ\xb2\xf4\x9cC*\a\f\xd93\x83\x15\t\xf0\x01\xe0ȓT\xfe{\xaa\x1b\xe0\xe7\x80$F+\xbd\xb7/e\\\xec!\x81F\xa3\xd1\xe8/4[\x17\xab\xd5\xeaBT\xf2+\x1a+\xb5\xba\x01QI\xfc\xe6P\xd1/\xbb~\xfaW\xbb\x96\xfa\xfa\xf0\xfe\xe2I\xaa\xfc\x06nk\xebt\xf9\x05\xad\xaeM\x86\x1fq+\x95tR\xab\x8b\x12\x9dȅ\x137\x17\x00B)\xed\x04=\xb6\xf4\x13 \xd3\xca\x19]\x14hV;T\xeb\xa7z\x83\x9bZ\x169\x1a\x06\xdeL}\xf8a\xfd/\xeb\x1f.\x002\x83<\xfcQ\x96h\x9d(\xab\x1bPuQ\\\x00(Q\xe2\rlD\xf6TWv}\xc0\x02\x8d^K}a+\xcch\xae\x9d\xd1uu\x03\xdd\v?$\xe0\xe1\xd7\xf0#\x8f\xe6\a\x85\xb4\xee\xe7\xde\xc3_\xa4u\xfc\xa2*j#\x8av&~f\xa5\xdaՅ0\xcd\xd3\v\x00\x9b\xe9\no\xe03MQ\x89\f\xf3\v\x80\xb0\x1c\x9er\x15\x10>\xbc\xf7\x10\xb2=\x96\xc2\xe3\x02\xa0+T\x1f\xee\xef\xbe\xfe\xf3\xc3\xe01@\x8e63\xb2rL\x14\x8f\x18H\v\x02\xbe\xf2\xb2\xc0\x04\xf2\x83\xdb\v\a\x06+\x83\x16\x95\xb3\xe0\xf6\b\x99\xa8\\m\x10\xf4\x16~\xae7h\x14:\xb4-h\x80\xac\xa8\xadC\x03\xd6\t\x87 \x1c\b\xa8\xb4T\x0e\xa4\x02'K\x84?}\xb8\xbf\x03\xbd\xf9\r3gA\xa8\x1c\x84\xb5:\x93\xc2a\x0e\a]\xd4%\xfa\xb1\u007f^\xb7P+\xa3+4N6t\xf6\xad\xc7U\xbd\xa7\xa3\xe5\xbd#\n\xf8^\x90\x13;\xa1_F\xa0\"\xe6\x81h\xb4\x1e\xb7\x97\xb6[.s\xc8\x000P'\xa1\x02\xf2kx@C`\xc0\xeeu]\xe4ą\a4D\xb0L\xef\x94\xfc\xef\x16\xb6\x05\xa7y\xd2B8\f\f\xd05\xa9\x1c\x1a%\n8\x88\xa2\xc6+&I)\x8e`\x90f\x81Z\xf5\xe0q\x17\xbb\x86\u007f\xd7\x06A\xaa\xad\xbe\x81\xbds\x95\xbd\xb9\xbe\xdeIל\xa6L\x97e\xad\xa4;^\xf3\xc1\x90\x9b\xdaic\xafs<`qm\xe5n%L\xb6\x97\x0e3\xda\xc8kQ\xc9\x15\xa3\xae\xf8D\xad\xcb\xfc\x9f\x1a\x06\xb0\xef\x06\xb8\xba#1\xa3uF\xaa]\xef\x05s\xfd\xcc\x0e\xd0\x01\xf0\xfc\xe5\x87\xfaUt\x84\xa6GD\x9d/\x9f\x1e\x1e\xfb\xbc'\xed\x98\xfaL\xf7\x1eCv[@\x04\x93j\x8b\xc6o\xe2\xd6\xe8\x92a\xa2\xca=\xf71\xeb\x16\x12\u0558\xfc\xb6ޔ\xd2Ѿ\xff\xb5FKL\xae\xd7p\xcb\"\x066\bu\x95\x13g\xae\xe1N\xc1\xad(\xb1\xb8\x15\x16\xdf|\x03\x88\xd2vE\x84Mۂ\xbet\x1cw\xf6T\xeb\xbdhd\xd9\xc4~y\x81\xf0Pa6804Jne\xc6\xc7\x02\xb6\xdat\xf2\u008b\xab\xf5\x00d\xfc\xc8Rˬ|P\xa2\xb2{\xedH\xfe\xeaڍ{\x8c\x10\xba}\xb8\x1b\rh\x90\t\xa8\xb1X\xa9-\xe6tΞ\x85t\x84\xde\tL @\xf0\x95%L\x03\x8f%Mm\xc1\xd5F\xf1)\xfd\x82\"?>\xea\xbfX\x84\xbcffmt\xc5\x15lp\xab\rF\xe0\x1a\xa4\xf1\xd4\x19\x8d!\xc2XFI\xd7n\r\x8f{$2\x8a\xbap\x81聾\xf7?@)U\xedp}\x02mb\x83=Q\x18\x8c_\x81}\xd4_\xd0:\x99-\x10\xefctP\x8f\x80\xcf{t{4t\xf0\xf8\x05˲\xc8\"7\x1d\x89\x9dxB\x10a\xdbY&\x16\x05T\xba\x11\xdf\x166\xc7\x06٩\x05n\xb4.P\x8c\xc5+~ˊ:Ǽ\xd5w'\xcc3Zݧ\x93\x01l\v\b\xa9Hܐ\xf6%\xf4T\xf7\x964Zdq\xc2 Ё\x97\xca\xc3ce\xb5\xc7(gS\x93\x0e\xcb\bn\xb3\xdb\alc\x88M\x817\xe0L}\xcaH~\xac0F\x1c'\xe8\xd2\xd8E\xa9di\xfb\a\xf1[Ȍ\x15w+d\x992^͋(k\xff\x81\x89\xb2\xd7\xfai\x89\x10\xffF}:\x85\x01\x19\x9b\x97\xb0\xc1\xbd8Hm\xc2҃\xfe\xde \xe07\xccj\x871\xfe\x17\x0er\xb9ݢ!8\xd5^X\xb4\xdef\x98&ȴ\f\x04\x96\x1a\x93\x9by\xb2\x8en#\x89Sy\xe5S\xa8Ӂ\x1e\x9f\xab\xa6\x11\xa2$\xa6\xc8\xdeS\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf1:]\x0f\xccm\xf2\t\xce^\x8f4\x98\xd3N\ft\x8aV\b\xda@I\x8a\xf4\xb4\xebX\xf5wmj\xd9\x1bA\xd2I{\x165u\x816L\x95\xb3\xb2\xead\xc0\xd5$\xe8vG\xbc\x11V\x88\r\x16`\xb1\xc0\xcci\x13'\xc7\xd2&\xfb\x96\"\xd7&\xa8\x18\x91pC\xe5\xd7-l\x06$\xb0f\xdc\xcbl\xef\xed#\xe2 \x86\x03\xb9F˧\\TUq\x9cZ$,\xed|\x98d\xee\xa0wm\xe1ȏ\xe1\xc5\x0e\u007f\xd7\x12dc\xd7\x16\xa4䐲-;\x80ӳ\xcb\xfe\xffI\xd8F쿀i\xefN\x86\xbe.\xd3\x12I%\xf9Aw[\xc0\xb2r\xc7+\x90\xaey\xba\x04\x91\x8c\x95n\xfe\u007f\xe0\x8d9\x9f\xe3\xef\xc6#_\x95\xe3gwe\t\"\xedJ;\xfd?র\xb2x\b\xba\"yC~鏺\x02\xb9m7$\xbf\x82\xad,\x1c\x9a\xd1\xce\xfc\xae\xf3\xf2\x1a\xc4H\xd1w\xd4J\xe1\xb2\xfd\xa7ody\xd9.R\x97H\x97\xf1`o\xbf6\xf6\xfcP1/\xc0\x05\xf6\xec\xa5\xc1\xd2G\f\x1e\x99\x9a\xdd\x13\xb6\xa8>|\xfe\x88\xf9\x1cy \x8d\xf3N\x16\xf2a\x84l\u007f\xea`\x94\xa7.#\x98>\xad\u007f\xe3cAW \xe0\t\x8f\xdeb\x11\nhs\x04M4\xe1\xe9\x9c\x12\x87\x83R\xccdOxd0!ʴ8:\x95\x15|{\xc2cJ\xb7\x11\x01\t'iC\xf4\x8c(I\x0f\x98\x10\x1c\x94H'\x1epİ\x91Eˋ\x83tAҴ\x86\xf6/Xf\xbbm\xbdh+o\xec;뷈N\xc1^V\x89\v%5\a\x16\xf9\xb441ï\xa2\x90y;\x91\xe7\xfb;5m\r\x0f\xdbg\xed\xee\xd4\x15|\xfa&m\b\xdb~\xd4h?k\xc7Oބ\x9c\x1e\xf1\x17\x10\xd3\x0f\xe4㥼\xd8&:\xf4\x83\x8f\t\xcc\xed\u06dd\xf7\xf0\xda\xed\x91\x16\xee\x14\xf9-\x81\x1e\x1cJ\xf6\xd3\xcd\xeb\x87a+k\xcb\xd1E\xa5ՊU\xe5:6\x93'v\"Hm\x06;r\x8aZ;\xa9\x9f0\x11\xec#i\x12?\xde\a\xc7\v\x91a\xde\x04\xc78\xa4+\x1c\xeed\x06%\x9aݜ\xe2跊\xe4{\x1a\n\x89R\u05f739,M\xb57-\x88\xee|\x19\x99\x15\x9d܄^\xcdf/v\x9d\x88\xe4Nw]^\x11\xabX\xb6?\x16\xa9+\xf2\x9c/\xe1Dq\u007f\x86\xc4?c/Nu\xbfG\xcck\xc8Rp\x90\xf1\u007fH\xcd1C\xff/TB\x9a\x843\xfc\x81\xef\xd4\n\x1c\x8c\rQ\xac\xfe44\x83\xb4@\xfb{\x10\xc5\xe9\x1dAdq\x9ad\v\x16^\x91\xeb\xed\x89\xc5r\x05\xcf{m\xbdN\xddJ\x8c\x86T\x87MZ\xb8|\xc2\xe3\xe5Չ\x1c\xb8\xbcS\x97^\xc1\x9f-nZkA\xab\xe2\b\x97<\xf6\xf2\xf7\x18A\x89\x9c\x98ԍ\xef.SMe\xf2%\x1bK\x80\x06\xb6\x17vd\xe6\xcea\x9dć\x95\xb6\x91k\x88\tT\xee\xb5u>\xb280Kωb\x81\xe7\xa1\x10\xbd\x02\xb1\xf5W\xa6\xda4\x97a$\xf6F\x01W\xda5;/ai\x1bۈ\x98\aJ\x8e\xd5ew\x82\xbd<\xbd\xf47d<\x89\xc8ظX\x84[\x19\x9d\xa1\xb5\xf3,\x92 \xad\x17\x82\x84m\x80Px\a\xc6\xdf4\xcd\a%\x9b\x96n\x90\x12\x91\xce4\xe5?}\xebE/\xe9\xf0\xd3\xef%\xe6;\x17/\xe03[\x96b|\xa5\x9a\x84\xe2\xad\x1f\xd9\x1c\x93\x00Ȼ\x06fW\xf3QO\xb7 \x03#\xfd\x11\xd4t)\xd5\x1dO\x00\xef_]\xad\xb7B\x12_b\xb8\xdf6c;\xa2\xb7\x0f\xf8\xf4\xa6ZD\x9a#\xf7\x06\a;w\x1a\xe7&C1\x11\xa4Ү\x1fN \xb8\x95\xce\xdfY\xd8Jc]\x1f\xd1T\xa6\xa8\x17N\u007f\xd7\xce\xf5\x9c\xd4'c^\xe48\xfd\xeaG\xf6\x02Y{\xfd\xdc\\LO^f\xc6\x1a_\n!\xc8-H\a\xa82]+\x0e\xbf\xd0Q\xe7)\xfc\x16x\x01\x9dL\xb24\x01A\rU]\xa6\x11`\xc5\\'\xd5l\x9c\xa6\xdf\xfd'!\x8b\xb7\xd867u\u007f\x1fk\x83mk.\xf2\xfb\x19\x06\xa5\xf8&˺\x04Q\x12\xe9Sݞ\xad\xbf\xfe\x1f\xecx\x9b\x04\xc0pY\x8d8M\x87\xaa*Х\x9eH\u007f\xddO\xc7\xc4\xca\x1c[\xc5\x1c\xb8@+\x10\xb0\x15\xb2\xa8M\xa2\x84<\x8b\xb6\xe7\xf8\x1aAX\xbc\x9e\x13\x916\xf9\x8aI\x91\x10\x88M4\x16\xe7\xa5ue\xd2M\xc5{\x83i\xe6\xd9RP\xba1\xcf*#\x89\x97\xf4k[h\x81ń:~7\xd1N\xdaw\x13m\xa1}7\xd1&\xdbw\x13m\xb9}7\xd1B\xfbn\xa25\xed\xbb\x89\xf6\xddD\x9b\xeb6'\xad\x970\xf2\x9f*L\xbc\\\xc4\"\xe1zz\x0e\xc5\x19\xf8!\x9b\xe2\xd6\u007f\xb6\x90\x9aay\x17\x1f\x15ɫ\r\xdfC\xac\xf8S\x8e\x18\atI\x17\x9d*iS.\xe9\x804\xec\xed3\xaf\x17\x920\x93\xd2)\xe3ٷ)\t?Ki>\xc3<\xd36ͦI4\xd5\xcd$\x11:4\x9f\x84\x90\xd9\xdb\xcf!\x19\xe6밝\xdb`\xfaw\xcfAMH\xc5YH\xc0\x99O̝\xa3\xd7\xc8\xf5\x18\x12\xcc\f\x12F\xff0\xf4ZȒ\x99\u038d\t7A\xe8\xc4\xe1\xfdz\xf8\xc6\xe9\x90)\x03\xcf\xd2\xed#Kyޣ\xe2;,\xb5맽6\xfc\x16\xbe\xcd\x19\xd3\x11\xb4\x01%\v&\xe7\f\xb7\x0e\xc8\v\xbfVޅ;\xfb\\λ\x1fi\xb94/Π\x19f\xc8L\x88\xe8s\xaf\x8c\xd2\x13\x85\xd3sd\xe6\x93Z\xceɌ\x19\xe7\xbdL\x02]·I\xf1\x1c\x17r_^\x90\xf1\x92\x98\xed\xf8\xbb/\xc6RrZ^\x94ɲ\x98\x10\x98\x98\xbf2\xccL\x99\ayF\xd6J\x12q\x963T\xce\xceK\ty \xb3\xebH\xceF\x89\xe4\x99\xcc\x02\x9e\xccA\x99\xcb.Y\x88J\x9df\x9e\xa4\xe7\x94̂\xe6|\x93\xe5L\x92\xd7\xcb\x17}\r\x1bxZ\xd4,f\x83,\xda\xc8\xf3\xf8-\xe6{\x9c\x93\xe5\xb1H\xb1\x17ft\xb4\x19\x1b\x13\xf3\x9e\x9b\xc71\xccӘ\x00\x9a\x92\xbd1\x91\x9d1\x01q6g#5'c\x02\xf6\x82ڝ咙\x97\xf1/HaQ\xbf\x15\u007f+\x8ez\xe9´\x19\x98\x8bK\x16\xfa\xaf\xa3\ued17\x8d\xd54o~\xc6,O\xe9\xf6盟e]8Y\x15\x1c\xce?\xc8<\xea4\xba=\x1e\xe1Y\x16\x05\x89\xd5\xdf4\u007f\xe6\xb492\xa4_\xbf\xb4\xec\xb9\x1e\x19\xd1\xc2\xc23\x16\x05\x88\x18s\x9d\xac<\xf3\x1fAgz\x85$\xf3\xe9\xc0\x85O>÷\xd2W\x9e\x83\xf9K\xaeX\xc4\xd3\xed\xb1$(ͷ\xa3g\xb8\x1f\xf3\x06\xa2\xb7e\xf9\xd9_k4G\xd0\a4\x9dŰ\xf0\x1d\x81?h\xb6.\xbaĭ ?\xfc\xa7\xf7#ù;p\xf0Ay\x15\x16\x05;\u0091\xe1Й/ڽ&\xf1F~\xc0D\xd7x\xe0C\xb7\xa3#\xef\x97l\xcf\xd4$\xfc\xb7u\x1d\xcew\x1e\x16\xd5\xf6\x9b8\x10/w!f@\xa6&է]@-&ѿ\x95+\xb1\xe4L$[QiI\xf2o\x91\x1c\u007fFR\xfc\x19N\xc5ynE2\x99R\x92\xdf\xdfĹxC\xf7\xe2-\x1c\x8c\x97\xb9\x18\v GI\xed)\xe9\xeaI\x97\xab\xc9\xf7\v)\x97\xa3\xcbW\x00\xf3i\xe8\t\xe9\xe7\t\x97\x03K\x98&\xa4\x99\x9f\x97^\x9e@\xc37r>\xde\xc8\xfdx\v\a\xe4m]\x90E'd\x91sf_\xbf8\xba\xacM\x8ef6\x18\x9f\xcaj\xb3L6\xf2\x17\x86s\x8e\xbe\xa8mj\xa4P\xaf\x81i\x1a\v)\xb7_\u007ff\xf0\xb3T\xb9\xdf\x0fb\xaa\x9e\x1e\xe7bJ\x9c\xff\xde\x1a\x15\x9d}\x16\a:\xbaT\xb0X\t\xc3ն6G\u007f1i\xd7\xf0Id\xfbaG\xd8\v\v[mʨ\xc1t\xd9\xde\xc8\\7\xa3\xe8\xc9\xe5\x1a\xe0'\xdd^z\xf5+*XYVő\xfc\x00\xb8\x1c\x0ey\x19\x03D\x99dž\xba>\xa1\xdc͂\xaf\xf70\xec\x1d\xb9\xbck\x8a\xddd\x85\xae\xf3\x16\xfa\xc4\xe6\tu\x84\xfb\xafl\x93p\x99\x90\xac+\x99\x12\xac\x8e\xc6\xe7\x1bWT\xf9\xf1\xf5/\xf3\xac\xd3F\xec\xf0\x17\xed\v6-Qb\xd8{P\xad+Ȋ\xe6r\xbd\xf9\xf6\"\xa6CC\xe9\xa8\x11\xb0.g&\x9c\x86\ue793\xb0\x8c\t\x91\x99\xf3\xe7\\\xb1\xb0\x98\xc7\xc7_\xfc\x02\x9c,q\xfd\xb1\xf6\x17\xa7\xabJ\x18\x8bD\xcdfa~І\xfe\xbb\xd7ϱ؆\x0ek\xfeq\x8c\xb7A\xce\xcb\xe1\xfbٳ\xb0?\f\xcaO5$Zbԯ\xf1Q=Ǭ\xb7I\xfe\x94G\x1d\xf2)8\xbd\n|\x1c\xb2\xe0\xefj^\xb7\xccϔԞ\xaaQ\xc6u\xb9\x96\xab\x94\xf9\xf2]\xa1&a\xc8\xee\xaa\r\xd7\xe8\t\xa5\xbd\xb8\xa6\xcd\xcb\n\x95\xf9d\x94A\x9d\xc8\xf9}\xba=\x1d\xc1\xd5\x00M\xde+T\xd6\x16\xcez\x16\xb6Mx\x89*\xd2\x0e\x9c\x1fɖ,A\xc3\x1c\xf0\x80\n\xb4\xe2\xfc\x16\xae~\xe3+V\x8e\xc7D\xa0\xf6\xa1\x84\x04\x9a\xba*\xb4ț\x13\xde\xe8\xacP\xe5\xf0\x91\xe5\x979\xa0ygg`rq\xb0\xad61\"\x9c\nL\xafXn \x17\x0eWQ\xa0I\xb2/\xcal\x99\x95CF\xb7\x1f\x9c#\xbf f+\x8f+\xcdM\x8dl\xf4\xaf\xd3N\x14\xa0\xear\xe3\x15\xbah:\xc4\xf6\xef\xa4ޜ\r\x19O3\xc7\xcb/L*\x87\xbb\x93\x98\xe2\xe9\xcan\x1b\xfe9{e\xedȩ\x95\xd9:\xcb\xd0\xdam]\x141Ӿ\xe5\xdc\xd7_&\xe7\xf2-\xd68\xe3N^\x04r\"`S\x88\xceg\x02\x96h\xad\xd85\xc5͞I\x03\xedP!\x1b>\xb1x\xa3w\f\xbḇai/\x1f\xc1\x12\x99\xabE\x98\xa0\xb9\xf9\xef\xf5z\x17\xb3\v\n\xbd\x83\xad,\xb8k\xa8_\x19T\xf3\x994\xf9VI\x93\xa2\xca?\xb5\x1d\x896\x1c|\xe6\x8d\xe8\xea\xbcb!w\x92\xf4 m\xd2N\x98\x8d\xd8\xe1*\xd3E\x81\x9cf~\x8a\xd7[\x1e\u0590\x9f\xf7\x05\x85]\\\xdaO\xfd\xbe!\xd2\xe1w\xdbW\xc6\x10\xbe@!\x97\xfdt\xd2`WG\xf7\x04!\xcd\x13\x9f\xa5\xba=\x15\xa2\x15gO1\xed\xf7m\x0eX\x90\xab\x1eNS\x80\xf6*\x18\x83qo\xb6\x14\xbfis\x05\xa5T\xf4\x0fY\xfc\x1c\x8ah\x06\x9f\x85?\u05ec[\xc0\xfb\x9e\xfa\xb4i\xd2=E\x8á\x982U㩱+\xf8\x8c\xa7\x96\x95\xcfvŜ\x83o\xb12\xbb\xd4\xe5N\xdd\x1b\xbd#\u007f8\xf2\xb2\x15^\x91w\xf7\xc28)\x8a\xe2\xe8'\x99\x9c=\xf2\xe2#\x92⚴^\xe2d\rX.Q6t\xeb\\o\xa9<'p\x9e\xeaF\xd7n J:Q\x14\x0f\xfb3\xb05|\xd6\x0e\x9b\x88\xae\x1c\xc2$\xe1\x8b֭p\xbb\xd5\xc6yO\u007f\xb5\x02\xb9\r\xd6P\x04.\x9d\t\xbe\x91\xf2UoA\xba\xeeR\xbe\xe3^vt\f\x1fB\xae\xf0T\x8a\xa3\xcfY\x14YF\xc66^['\x8a\x88|\xfb]9Plv\x12\xf7a\xfe\x97\x88\x1dvB\xf0\xbb~\xff\xf6\xc3\xf1V\xbb18O9\xce)\xf7\xb2=\xaa\xe9\x803\x8dQ\xc1\xb3\x91Α<\xed_ف#\tZ\x14`I\xa6L\x94\t\x9c\x93\xec\xfc\x9et\xef\xddt\bq\xe8ߴ\x9d\xa7TwX\x9c\xa6m\xd90\t&\x96\xe5\xbfY\x92\xb6\x19K[\x99\xed\x85\xda\x11S\x19]\xef\xf6\r_NhƩ\b\\MHAU\xd4;b\xf5p]\xe2j\xa3z!\x98p\x81\x92\xf7\xd0\x15\xd9\xd3$\xa6!$\xdcT^\xbf\x0e\x85\xffV[\xa3\xcbU\xd8\v\xbe\xe5\xb8\n\xa1\x11#5\xd9\xff\xe4\xc8O\x00\xed*l1\x1bT\x15*\x106\xe0\x93\xf0A\xd5\xfc\xb6\xce\xc5)\x9c0.իx\x18t^p(\x18r\x1c߇\x10\xf8\xf1\x1f\x96ݎk\xe0_\x81\x95\xaa)\xfa\xee\x03K\x9e\x15,\xf9\x19\x06\xd9W\x8f^`\x9dx\b\x03\u007f`\x88\xfe\xdf\xd6\x158\xb4\x1a\xe6S\x8aM\xf9u\xd4}\x94\x9dK\xa7\xbc\x83\x18\xec\xc0\b=\xfe$\xb7\xfeN-#\xac\xff\xfcwϺ=$\xd9,\xeff\xcd\x15\xb6DZ\xbb\x03>be0\x13Q\xc7\x03\xe0\xbe@\xb2#,\xe2\xd0\x12zw\x96\xc9{x\x99\x13\xf7\x9a\x1e\\\xf3\xf7\b^ǯ9\xbc\xccw{3\xc7\xeduW\xf7,\xb8\x06\xfa\xd2\x19\xfb\x8f\xd0-\xe2\xb9\x05\b\x11\xdf-\xb2\x8c֛[\xf4\xddz\xae[\x83\xe3D\xb5\xeb\x91;\xf7J\xce[T\x0f\x9c\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z\xdds\x1b\xb7\x11\u007f\xe7_\xb1\xe3<\xa8\x991\x8f\x89\xdbi;|s\xa4\xa6\xa36\x915\x96\xad\x17\x8f\x1f\xc0\xc3\xf2\x0e\xd1\x1d\x80\x028\xcal&\xff{g\xf1q\xbc\x0f\x90\x944ur/6\xf1\xb1\xf8a\xbfw\xa1\xc5r\xb9\\0-\xee\xd1X\xa1\xe4\x1a\x98\x16\xf8š\xa4_\xb6x\xf8\xbb-\x84Z\xed\xbe_<\b\xc9\xd7p\xd9Y\xa7\xda\xf7hUgJ\xbc\u00ad\x90\xc2\t%\x17-:ƙc\xeb\x05\x00\x93R9FÖ~\x02\x94J:\xa3\x9a\x06ͲBY\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb8N\x1e\x137\x8b;\xdf\xdfV\xe5\\\x9b\x1f;\xd5\x1f\xb86\xf6S\x99W\x8a\xe5\xad\xf1l\xad\xe6bW\xe5L5\xf5g\x00:\x91%^\xc1G\x1a\xaad\t\xa6g\x00~bv\xe8\x15\xb04\xb5\xa4b\xf9gŅAu-\xf3\xaa\b$ZA\x8a:Q\xbc4\x96\x14w\x86\x99J\x83܂ɰ=\x0e\x95\x9f\xb5\x14\x9f\x99ɮ`\xadm\xbbu\x991\x1d\xbe:\x129\x00\xbe\xca\x1c\b7m\x14\x17\xbb\xa1\xd1\xde\xc1\xb5\x92\x02\xf0k\xa9P\x13ʐZΊ\x1dCș6`x\x81\xc0\xfc\x80\xf0Ĵ\xc5a+\x15\x98\x8c\xeby\x9a\x10\x90\x0e\xb6\x0e\x9d\x0f\xfdj\x87P\xca\fztZ\xa0\x82T\xaf\x8f$\xb2\x03\xf3\xdd\x0e\x87\x81\xb9\xcf\xfb\xb7Nn\x92\f\vv\xe5[\xca\x12ŻϷ\x0f\u007f\xbd\xebTCO\x0e\xfc,\x81k`\xf0`\x85\x1a\x94_~`2f@!q\r\x85\xa1\x16\xa5\xc2U\xa0LZ\x83\x04\x90\nJT\\\xa6<\t\x14\xb5\x9du&\xab<\x85\r\x12q\xd7u\x87R\xc9\x12\x95\xe1aٸ\xd2R\x13\xad\xda\x1e\xc6ohR\xae\x95\x93\"\xd4Vp\xfcb\xc0\xd4\xd3\xc1\xc96\xd7\r\xfe\x96\xc0\x1d\xc0@\x8d\x98\x00\xb9\xf9\x19\x13\xb3\x86;T\x04&`\x9dH\xb1GE\x14H\xe4N\xf0\xff\u05305I\xac\xb1\x82dЯ\xe5\xa6\xd8\xc5'X\x0e{\x96Wx\x01L\xa4P\xb0\x03(\xa4Q\xa0\x12-x\xb6\x89^\xc3?\xa5B\xe0b+\xaf 3\xa6\xd4W\x97\x97;n\x82zLdQT\x82\x9bå\xd5t|S\x19\xa9\xf4e\x8a{\xcc/5߭\x98J2n01\x95\xc2KV\xf2\x95E]X\x15\xb9.\xd2?\x05\x8e\xea7\x1d\\\x8f֊+V\x89Mp\x80\xb4\x99\x13\x18\xd7\xd5͢!4U\x11u\xbe\xdc\xdcݷ\x85\x89\xeb>\xf5-\xdd[\x12ְ\x80\b\xc6\xc5\x16\xfdj\xdc*YX\x98(\xd2Rra\xec\x8f$\xe7(\xfa\xe4\xd7զ\xe0\x86\xf8\xfeK\x85\xda\x10\xaf\xd6pmm\x06\xc9aU\xd2\xeaI\xd7p+\xe0\x9a\x15\x98_3\x8d/\xce\x00\xa2\xb4^\x11a\xe3X\xd06w\xfdƎj\xad\x0f\xc14\x8d\xf0+\xac\xf1\xbb\x12\x93Β\xa1~|\xcb\x13\xbb0\xac\xe6\xabU@O\xfb\xb92\xbcj\xc1\xab\x1ejޯ\x9f\xd46\xb16\xe1\b&x\x15\xb3>\xfa2BM\xfb\t\x8b\x92\x96\xeb\f\x8a\xf7\xbe\x19\xa1H4Jk\x17$\x18ˠޤ\xd7jp\xa4T\xecp\x19\x12\xbd\xf6<\xf5Z㈚\xd3\x14\xa5\x92h~'X\xa93i\xc8.\xc8\xca\f\xb5\xeaM\xe0\xfa\xee\xb6\xd7)\xf0\xd9s\xddڽJcJSxb\xbc\xbf~B!y\xb8\xbe\xbb\x85\ar#0\xc0\x04g\xfd\xc0TJX5\xf8\x05Yz\xb8\x97?i\x84\xb4\xb2\xda ز\x8b\x11\xc0\x1b\xdc\xd2bSH0\xa8\x03*E\xb2\xa7-j\xb22kk\xa4Sܲ*7^\xb9p\ro\xbf\x83\x82\x8b\xca\xe01\xdfa\x9a\xf7\x8eH\x16\x9c\x9b\x8d\xbe\x97_P\x1b\x9eD\x10\xf4\xfd`\xc7\x16Q\x9f24\x19*\xd2t\xf6\x835\x1e\xa3s\xafIo\xd8#\xf9\x1f\x1b'Nd\x88\xf2\x1cJ\x99\xc2ލ\x04\x9bC@zj\xc2\x1b)sdC\"\x88_\x93\xbcJ1\xad}\xc6A!\xeb\xcd\xf6樓\xf5\xae\x19\x17\xb4dɗ%TE\xfdud\x9e\xd6\xf83\x85@Z\x97\v\a\x13\xb8\xf3\xf16#\xab\x97\n7X\x8c\xe09\xcbb\xb0^<\xdb\xe4x\x05FUC\x8a#\xc0`J\xb1\xc3\x04\xcdB\x04\xb2\x84du\x1fo\x1bs\x9e \x11\xab\xb6\x80\x96j\x964#\xf3\xfb?$X&\xe5c\f\x91\xfeA\xed\x1aK\x0f\x89\r\xf4`\x83\x19\xdbs\xa9t\xdf]į\x98T\x06\xc7\xd6\x113\x90\xf2\xed\x16\x15\xc1\xb2\xd1I\x1d\xccL\x11kZ\xdfRQӌ?\x9aW\xc3tb\x9e\xa5\xc6\xd8T\xac]\x1b\x85\n\x16qR\x87U\t\\\xa4|\xcfӊ\xe5\xc0\x856L$n~\xac\xc6ox~0'\x10G\xf8;k\x16fA\\\xea\xb8\tR \xf9\xf6\x85T\xc3\xc2\x11\xca1\x98q2l\x18i@9fۛ\xa2($\xf6\xa8\xa4\xd6\x1e5z\xe7\xa2\xe1\x94\xf3\xb0s\xb6\xc1\x1c4\xe6\x98\x18\xa9\xc6\xc9\x13#\x04\xae\xc4\xea\xcf\x11\xca\x0ehҮ!\x9eU\xa2M!K\x9d\xf1$s\xce0I\x99\x85\x05\xa9Dm5\x06+\xcb\xfc05i\x88\x91\f?\u061c\xd2hJ\x84\xfa\xe8\xc3\x1dS$M\x89\xd4\xc1M\x99\xd1\xc6]\xaa\xd7b\xf3J\xf4\x0e\x9a⛄\xfd\xf6\xa8\xfb\xf3\v;\x91\x9bS\xb0|\xbb\x05,Js\xb8\x00nBm\fTr\xb0\x1a<\xfe`\x8c;m\xb5\xdc\xf6{?\xfbjy\x16\xae\xd5h\xfcA\x98f\x8d՝\xb7U\x8b\x18\xf6\xa1\xdd\xf3\x02\xf8\xb6fXz\x01[\x9e\x1b\xb4\xbe\xd4\x1c\xa2-Gg\x96s\xcfI\xa0X\xdbK\xa5`&\xc9n\xea\xfd\x81\x88\x1e=Z\xf5\x018\xbf<\xc40\x96\a\x11 \xa1v*\xec\x96\x12WX\xb8\xad\xaa{\xbb>\x9a\x1a\xeb\x01\xbe\xfb\xf8\x1e\xd39\x92A\xbc\xa4\x1eM\xea]\xcf\xd3i\xa3`'\x18\x05\xb25)\xeb\xa6\xd51\x9eې\xbc\x00\x06\x8fxp\x9e\xd5`p9T\x88\xb5\xac\x06\xa9\xd0\xee\x8eZ5\xf2\x88\a\v\xcaowF\xc1[\"*\xae<\xe2!\xb6i\x8f\xa8\x84\x9f\xdf\xf0qԥ\n;\x8b\x98\xa5Ԕ\x9a\xa8~퀑q\x93\x85eJ)\x94@\xf1\x13\xa7]3\xac\xb3\xc7\xff\x88\x877ڱ\x8fVM\xc6\xcb\x05\x14 \x85\r\x1a\xed\n\v\x9b\xdb\x0f,\xe7i=\x98]'\v ފ\v\xf8(\r\xfds\xf3\x95kBQ\xa4\xf0^\xa2\xfe(\x8d\xadyQ\x12\xbbI\x9cH`\xd7\xd9.K\xe1\xcc\x02\xd1e\xd1\xf8\r\x0eք\x92\x88\xd6l\xe3\x1an\x05\xc5g\x8e>Kؔa@ΡUT\xdan\x8f\v)V\xd6L\x87\xd1\x16\x00m\xe3\xe5Y%U\x87S\x17\v!\x0e\xa2\xe8ѻ'k\xe5\xbe\x1c\x1d,L\x15\x85e\xce\x12L\xc3v\xa5=\xc5`\x06w<\x81\x02\xd5\x0e\xa1$\xbb\x11/T\v4\xb9+'Ha\xbck\x11\x8a7\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1cYL7\x8f\x9b\xa55\xef\xd6\x1f\x8a\xa2~\xfb||\x99eYȯc\x1f\xc4!\xe9\u070f\x82\xd9\xcd\xde_ɼZ\xf1\xfe-\xce\x1a2\xae\xf4\x1a\xde\xd9\xec\x80\x1c\xdb\xfd\xc3.ak\xa8(\x90\x84\t\xd7@r\xb2g9\xb9\x0f\xa4\xbc\x05`\xee\x9c\t\xb9=\xf2\xa0\xe2T\xccS&\xb5\xb3\xf9[\x8e\xb9=*<\u007f\xc4\xc3\xf9ő\xf6:\xbf\x15\xe7q0I\xe7\x1f)\xad\xdak\x91\"?\xc0\xb9\xfdvn\x1d\xb3%K\xe4\x04\xe7m\x81TG7\xb5g\xf9KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x13or\xe1\xe7f\x11-ӥ\xd4#\xc7N#h}\x96ڸ\r\xc0\x8e\xbb=\xb0C\x18\x13\xfd\xf9]C`[\x83\n\xb4\x91*\x9c.\x93\xda\xedm\x90\x13\xe7\xf5<\xef\x89\xd5\xf5n\xa4\x03LA\xe6y\xa3!\x9cN?w\xc7\xce\xf4\xf7<\xcc\xc4:K\x16v\xa9d\x82ZϋR\xa4\xe5\x98ٰ\xad7k\x99\v\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6kkߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\x9b5V\x14\xac\x9f\xe9\x10\x8d\xee\xb5\xeb\x1d\x16\xa0\a\xe6\x02&\xb5\xab\xacRY\xe67{\x91\xfc\xbd9\x1e\x05\x17\xb7v x\xfbb\xce\n\x04U\x8e\xa7\x862ס\u007fÐ\xba\"6~\x85pn/\xedY\x8d\xc2\x0eg\x8fO2\xe29\x05\xe4L\viڛ5~\xa47\x1a\xb6\\i\xd3 \xbc\x00*\xd7\xf68\xf9ecLq\xa3\xd4\xc9!\xe6'\u05fb\xb5\xad\x98\xc9'\x9fe\xb2$\xb0\x0e\xc4\xcf\xd8\x1e\x81o\x81\x1b@\x91\xc8J\xd8\r/R\x174\xcc\x02\x88\x8e\x89ΘD\xda\xccVgQ\x15\xf1\x04YY\xe9\xe4bvw\xac\xdd\xe5\a\xc6\xe3v\xa7\xe04\xb6\x9a\xa9\x8c\x92\xa1\xd2M\x93\xf1\xa9%\xedt\xa2\x82}\xe5EU\x00+\x88-K\xe2ƭKJ\t\xb9G\x8e\xd7O\x8c\x1b\x9f\x8e\xe9\x0eV\x97i\xd3D\x16e\x8e\x06C\xbaI\"\x85\xe6)\xd6\xee\x83\xe7\xff`\xf2\xceXa\xb0e<\xaf\xd4\x02\x1d\xbd\x983K\xe36\xaf\x9e\x9e?\x18\x8bGde\x89\x19\xb9\xe9\xbe\xc0i\x9e\xb7\x1f\xa5Z\xe62\u007fV\xf8\xfc\xaei\xa98I\xa9\x9c\xf3NgaZ\xef\xb5\xeb\x9dz\xe1e\xe20\xe6\x9e\xceB\xb5\x98\xbc\xba\xa7uyuO_\xdd\xd3W\xf7\xb4W^\xdd\xd3W\xf7\xf4\xd5=\x1d.\xaf\xeei\xab\xbc\xba\xa7\xd1\xf6#\x06Õݹ\x9dh\x10\x85Ud\n\xc6\x1c\xda3c\xf9L\xa3\xeb\xbc\xd2\x06Ւ\f\xe9\xdb\xe1\x9e\x039\xf4\x89k\xb2\xb2\xd7\x1dǤ\xa6I]i\x8c^\x9d2MK2,&w\xb1%\xc2\v\x8fN\x83\x1e϶\x8fM\xa0\x9bK\x9b\xeb\xe6\x8e\xd7\xe9j\xee\xaf\x11\x82\x18\x19\x86\xf7\xdcs\x17\xa6\xda9W\xdd\xdc7\x1b\a\x04\x8c\u007f\x97y\xe5\x91im3\xc9lӉ\xf8c\x16>вw\xb8\xd0%\xa6\xea$~\xff\xaei\x19\x91m6\x9ec\xe6O-Ѱ\xfd\xdbu\xf7\x8b\x91>\xe3ldfO\xdcd\xee2\x17\x85\xaeb\xd7Nk\x0fr\xea/R\xf6i<\x02Q*\x10\x11\x90*E5{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xc8\xfa\x16|\x02?r\x91:ސ\x10\xb6\xfc\v\xfbj\xa1\xbd'S;>\xe3b\xd4x\x9b\xbdC#\x8d%#5j\x83\"{\xb8\xad\xd7pÒ\xacn8\x02ю\x9c1\r[\xa9\nf\xe0\xbc>\x8d\xbb\f=\xa9\xe6|\r\xf0\x83\xac\x0fB[\xaf܌\xc0ռ(\xf3\x03E?p\xde\x05\xf4m\xa23*~ڿ\x04\xe7\x1fD\x8b\x88\x80\xef\xba=\x06\x8e}\xc3shI.\xab\xb4\x1ea\x82\xddL\x1c\xe0\xf3\x83\xf5\xa6\xec#PI\xf3X\x96\xf7\x95B$\xdc{Kk\x04\xe4\u0603\x82\x8bH6~8\xac\x8dTl\x87\x1f\xa4{k1\x86f\xdd\x1e\x9d\xe76\xbd\xae\n\xa9\"\xfe\xeeר$\xbb\xb9\xf5\x016\x19d~\xb55g\xe9\x84\xed\x98\x12\x9bY\xe7\xc6\xe4\x11\x93\xbb\xbf\xff\xe0&dx\x81\xeb\xf7\x95;\xa8_\x95Li$J\x87\x89\xbaN\x9bq;\x97\xc9'ȥ\xa7\xc3\xf7\xfdy(\xb4\x19k6'\xe0\xa4\xd9\xec;O\x1f\x06\xd2ň\xfc\xc3p\xcfV \xdbb\xe2\xd4Ѿ\u070e\xc2bZ˄[]d\xb7\x84l\xa2\xd8\xcb=\x157eQ&TF\xa5\xf1ӓ@\xf5%,T}+\x1c\xa7f^\xe2\xfc\xe9\xa8c`\xf0\x90\xfa \xfd\xd7k>d\xf7\x84'\x90v\xafT\x86\xbd-\xae\xeb\xb7I\x8fI7\xb3\xfe\xc7\xd7\xfe\xb0\xef\xba\x1a~\x0etU\xbfPz\x16AY\xf7\ng̣\xaf\xee\xb9΄\x95\xa6R\u07bc&\x95\xb2\xef\xe6\x11\x10t\xcfʝ\xf6\xeck\xf3\x04\xf6\f/\x9bG\xb1\x9bh\u007f\xf6\t\xee\x01\xfe\xd5\x0fȎ\xbe\xa8ꬫ{\"{E\xf0Oc\xe7\xe0:\xb0\xef\f\xce\xcc\xf43\xb5\xa9\x93|=\xa1m\xc7\xf0>\xe1\xdd\x18\xea\xc3Y\x9b+\xf8\x88O\x03\xb57\x82&q|\xa6\xe6R31\xb5{\x04CO^ONq_\xf7\xb2y\xb1\x03ڢ\xab\xe6z\xcd{\t7,\xcf[\x10]\x0e\xec\x10[\xff̷n\x03'\xa19\xfd\xe5\xa8Ũ\xe2\x9aTZc\nkpI\x1dUjT{L[B\xe2mx\xbb\xa6\xda4\xcfE¯\xbf\x9d5\xab\x92%\t\x96\xc6'v\xb5\xffk\x80\xf3s\xfb#\xbc\xfco\u007f&R8G[_\xc1\xbf\xfe}\x06\xde\x00?\x84\xe7\xfd\xa9\xf2\xbf\x01\x00\x00\xff\xff\x9dJq\x1dHa\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), } var CRDs = crds() diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 7fc0f7a4c6..232955a35d 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -110,6 +110,12 @@ type BackupSpec struct { // +optional // +nullable OrderedResources map[string]string `json:"orderedResources,omitempty"` + + // CSISnapshotTimeout specifies the time used to wait for CSI VolumeSnapshot status turns to + // ReadyToUse during creation, before returning error as timeout. + // The default value is 10 minute. + // +optional + CSISnapshotTimeout metav1.Duration `json:"csiSnapshotTimeout,omitempty"` } // BackupHooks contains custom behaviors that should be executed at different phases of the backup. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index c0a8f2c567..9cc49a8b5b 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -250,6 +250,7 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { (*out)[key] = val } } + out.CSISnapshotTimeout = in.CSISnapshotTimeout } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index 0fa2a3b7fe..73a994b465 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -233,3 +233,9 @@ func (b *BackupBuilder) OrderedResources(orders map[string]string) *BackupBuilde b.object.Spec.OrderedResources = orders return b } + +// CSISnapshotTimeout sets the Backup's CSISnapshotTimeout +func (b *BackupBuilder) CSISnapshotTimeout(timeout time.Duration) *BackupBuilder { + b.object.Spec.CSISnapshotTimeout.Duration = timeout + return b +} diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 6190d7f9a1..3c2c23838d 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -98,6 +98,7 @@ type CreateOptions struct { SnapshotLocations []string FromSchedule string OrderedResources string + CSISnapshotTimeout time.Duration client veleroclient.Interface } @@ -122,6 +123,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringSliceVar(&o.SnapshotLocations, "volume-snapshot-locations", o.SnapshotLocations, "List of locations (at most one per provider) where volume snapshots should be stored.") flags.VarP(&o.Selector, "selector", "l", "Only back up resources matching this label selector.") flags.StringVar(&o.OrderedResources, "ordered-resources", "", "Mapping Kinds to an ordered list of specific resources of that Kind. Resource names are separated by commas and their names are in format 'namespace/resourcename'. For cluster scope resource, simply use resource name. Key-value pairs in the mapping are separated by semi-colon. Example: 'pods=ns1/pod1,ns1/pod2;persistentvolumeclaims=ns1/pvc4,ns1/pvc8'. Optional.") + flags.DurationVar(&o.CSISnapshotTimeout, "csi-snapshot-timeout", o.CSISnapshotTimeout, "How long to wait for CSI snapshot creation before timeout.") f := flags.VarPF(&o.SnapshotVolumes, "snapshot-volumes", "", "Take snapshots of PersistentVolumes as part of the backup.") // this allows the user to just specify "--snapshot-volumes" as shorthand for "--snapshot-volumes=true" // like a normal bool flag @@ -332,7 +334,8 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro LabelSelector(o.Selector.LabelSelector). TTL(o.TTL). StorageLocation(o.StorageLocation). - VolumeSnapshotLocations(o.SnapshotLocations...) + VolumeSnapshotLocations(o.SnapshotLocations...). + CSISnapshotTimeout(o.CSISnapshotTimeout) if len(o.OrderedResources) > 0 { orders, err := ParseOrderedResources(o.OrderedResources) if err != nil { diff --git a/pkg/cmd/cli/backup/create_test.go b/pkg/cmd/cli/backup/create_test.go index 401e93e473..8941630256 100644 --- a/pkg/cmd/cli/backup/create_test.go +++ b/pkg/cmd/cli/backup/create_test.go @@ -19,6 +19,7 @@ package backup import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,6 +36,7 @@ func TestCreateOptions_BuildBackup(t *testing.T) { o.Labels.Set("velero.io/test=true") o.OrderedResources = "pods=p1,p2;persistentvolumeclaims=pvc1,pvc2" orders, err := ParseOrderedResources(o.OrderedResources) + o.CSISnapshotTimeout = 20 * time.Minute assert.NoError(t, err) backup, err := o.BuildBackup(testNamespace) @@ -46,6 +48,7 @@ func TestCreateOptions_BuildBackup(t *testing.T) { SnapshotVolumes: o.SnapshotVolumes.Value, IncludeClusterResources: o.IncludeClusterResources.Value, OrderedResources: orders, + CSISnapshotTimeout: metav1.Duration{Duration: o.CSISnapshotTimeout}, }, backup.Spec) assert.Equal(t, map[string]string{ diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index c5fe9398cc..e32a71c0cd 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -145,6 +145,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, DefaultVolumesToRestic: o.BackupOptions.DefaultVolumesToRestic.Value, OrderedResources: orders, + CSISnapshotTimeout: metav1.Duration{Duration: o.BackupOptions.CSISnapshotTimeout}, }, Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3c83ba75a3..e837020d3a 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -102,6 +102,8 @@ const ( // the default TTL for a backup defaultBackupTTL = 30 * 24 * time.Hour + defaultCSISnapshotTimeout = 10 * time.Minute + // defaultCredentialsDirectory is the path on disk where credential // files will be written to defaultCredentialsDirectory = "/tmp/credentials" @@ -111,7 +113,7 @@ type serverConfig struct { // TODO(2.0) Deprecate defaultBackupLocation pluginDir, metricsAddress, defaultBackupLocation string backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration - defaultBackupTTL, storeValidationFrequency time.Duration + defaultBackupTTL, storeValidationFrequency, defaultCSISnapshotTimeout time.Duration restoreResourcePriorities []string defaultVolumeSnapshotLocations map[string]string restoreOnly bool @@ -142,6 +144,7 @@ func NewCommand(f client.Factory) *cobra.Command { defaultVolumeSnapshotLocations: make(map[string]string), backupSyncPeriod: defaultBackupSyncPeriod, defaultBackupTTL: defaultBackupTTL, + defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, storeValidationFrequency: defaultStoreValidationFrequency, podVolumeOperationTimeout: defaultPodVolumeOperationTimeout, restoreResourcePriorities: defaultRestorePriorities, @@ -650,6 +653,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.config.defaultBackupLocation, s.config.defaultVolumesToRestic, s.config.defaultBackupTTL, + s.config.defaultCSISnapshotTimeout, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), defaultVolumeSnapshotLocations, s.metrics, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 3f54e8f4f3..eff0861ebd 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -87,6 +87,7 @@ type backupController struct { defaultBackupLocation string defaultVolumesToRestic bool defaultBackupTTL time.Duration + defaultCSISnapshotTimeout time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string metrics *metrics.ServerMetrics @@ -111,6 +112,7 @@ func NewBackupController( defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, + defaultCSISnapshotTimeout time.Duration, volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, defaultSnapshotLocations map[string]string, metrics *metrics.ServerMetrics, @@ -135,6 +137,7 @@ func NewBackupController( defaultBackupLocation: defaultBackupLocation, defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, + defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, snapshotLocationLister: volumeSnapshotLocationLister, defaultSnapshotLocations: defaultSnapshotLocations, metrics: metrics, @@ -359,6 +362,11 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Spec.TTL.Duration = c.defaultBackupTTL } + if request.Spec.CSISnapshotTimeout.Duration == 0 { + // set default CSI VolumeSnapshot timeout + request.Spec.CSISnapshotTimeout.Duration = c.defaultCSISnapshotTimeout + } + // calculate expiration request.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(request.Spec.TTL.Duration)} @@ -638,7 +646,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { backupLog.Error(err) } - err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots) + err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots, backup.Spec.CSISnapshotTimeout.Duration) if err != nil { backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) } @@ -879,9 +887,10 @@ func encodeToJSONGzip(data interface{}, desc string) (*bytes.Buffer, []error) { // using goroutine here instead of waiting in CSI plugin, because it's not easy to make BackupItemAction // parallel by now. After BackupItemAction parallel is implemented, this logic should be moved to CSI plugin // as https://github.com/vmware-tanzu/velero-plugin-for-csi/pull/100 -func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []*snapshotv1api.VolumeSnapshot) error { +func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []*snapshotv1api.VolumeSnapshot, + csiSnapshotTimeout time.Duration) error { eg, _ := errgroup.WithContext(ctx) - timeout := 10 * time.Minute + timeout := csiSnapshotTimeout interval := 5 * time.Second for _, vs := range volumesnapshots { diff --git a/site/content/docs/main/api-types/backup.md b/site/content/docs/main/api-types/backup.md index 37cb30bf9e..23805b21d7 100644 --- a/site/content/docs/main/api-types/backup.md +++ b/site/content/docs/main/api-types/backup.md @@ -29,6 +29,10 @@ metadata: namespace: velero # Parameters about the backup. Required. spec: + # CSISnapshotTimeout specifies the time used to wait for + # CSI VolumeSnapshot status turns to ReadyToUse during creation, before + # returning error as timeout. The default value is 10 minute. + csiSnapshotTimeout: 10m # Array of namespaces to include in the backup. If unspecified, all namespaces are included. # Optional. includedNamespaces: diff --git a/site/content/docs/main/api-types/schedule.md b/site/content/docs/main/api-types/schedule.md index 9c5dc26cee..5bc430c8ff 100644 --- a/site/content/docs/main/api-types/schedule.md +++ b/site/content/docs/main/api-types/schedule.md @@ -34,6 +34,10 @@ spec: schedule: 0 7 * * * # Template is the spec that should be used for each backup triggered by this schedule. template: + # CSISnapshotTimeout specifies the time used to wait for + # CSI VolumeSnapshot status turns to ReadyToUse during creation, before + # returning error as timeout. The default value is 10 minute. + csiSnapshotTimeout: 10m # Array of namespaces to include in the scheduled backup. If unspecified, all namespaces are included. # Optional. includedNamespaces: diff --git a/site/content/docs/main/examples.md b/site/content/docs/main/examples.md index 58a35fd5da..d774b387dd 100644 --- a/site/content/docs/main/examples.md +++ b/site/content/docs/main/examples.md @@ -47,10 +47,10 @@ cd velero kubectl apply -f examples/nginx-app/with-pv.yaml ``` -1. Create a backup with PV snapshotting: +1. Create a backup with PV snapshotting. `--csi-snapshot-timeout` is used to setup time to wait before CSI snapshot creation timeout. The default value is 10 minutes: ```bash - velero backup create nginx-backup --include-namespaces nginx-example + velero backup create nginx-backup --include-namespaces nginx-example --csi-snapshot-timeout=20m ``` 1. Simulate a disaster: diff --git a/site/content/docs/v1.9/api-types/backup.md b/site/content/docs/v1.9/api-types/backup.md index 37cb30bf9e..11f37fc502 100644 --- a/site/content/docs/v1.9/api-types/backup.md +++ b/site/content/docs/v1.9/api-types/backup.md @@ -29,6 +29,11 @@ metadata: namespace: velero # Parameters about the backup. Required. spec: + # Available since v1.9.1. + # CSISnapshotTimeout specifies the time used to wait for + # CSI VolumeSnapshot status turns to ReadyToUse during creation, before + # returning error as timeout. The default value is 10 minute. + csiSnapshotTimeout: 10m # Array of namespaces to include in the backup. If unspecified, all namespaces are included. # Optional. includedNamespaces: diff --git a/site/content/docs/v1.9/api-types/schedule.md b/site/content/docs/v1.9/api-types/schedule.md index 9c5dc26cee..ccc441999d 100644 --- a/site/content/docs/v1.9/api-types/schedule.md +++ b/site/content/docs/v1.9/api-types/schedule.md @@ -34,6 +34,11 @@ spec: schedule: 0 7 * * * # Template is the spec that should be used for each backup triggered by this schedule. template: + # Available since v1.9.1. + # CSISnapshotTimeout specifies the time used to wait for + # CSI VolumeSnapshot status turns to ReadyToUse during creation, before + # returning error as timeout. The default value is 10 minute. + csiSnapshotTimeout: 10m # Array of namespaces to include in the scheduled backup. If unspecified, all namespaces are included. # Optional. includedNamespaces: From fb445b3c0d7a9c5fbbc4246fe1d4f1872a1f035b Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 5 Jul 2022 12:37:14 +0000 Subject: [PATCH 246/366] Add migration E2E test Signed-off-by: danfengl --- .gitignore | 2 + pkg/client/client.go | 13 +- pkg/client/factory.go | 11 +- pkg/client/factory_test.go | 6 +- pkg/cmd/velero/velero.go | 2 +- pkg/restore/prioritize_group_version.go | 2 +- test/e2e/Makefile | 13 +- test/e2e/backup/backup.go | 11 +- test/e2e/backups/deletion.go | 7 +- test/e2e/backups/sync_backups.go | 18 +- test/e2e/backups/ttl.go | 6 +- test/e2e/basic/enable_api_group_versions.go | 12 +- test/e2e/bsl-mgmt/deletion.go | 32 +-- test/e2e/e2e_suite_test.go | 49 ++++ test/e2e/migration/migration.go | 256 ++++++++++++++++++ .../e2e/orderedresources/ordered_resources.go | 2 +- test/e2e/privilegesmgmt/ssr.go | 18 +- test/e2e/test/test.go | 10 +- test/e2e/types.go | 30 +- test/e2e/upgrade/upgrade.go | 66 ++--- test/e2e/util/k8s/client.go | 20 +- test/e2e/util/k8s/common.go | 15 +- test/e2e/util/k8s/namespace.go | 7 +- test/e2e/util/kibishii/kibishii_utils.go | 10 +- test/e2e/util/providers/aws_utils.go | 7 + test/e2e/util/providers/common.go | 2 +- test/e2e/util/velero/install.go | 9 +- test/e2e/util/velero/velero_utils.go | 60 +++- 28 files changed, 509 insertions(+), 187 deletions(-) create mode 100644 test/e2e/migration/migration.go diff --git a/.gitignore b/.gitignore index b83bbf61e7..18482277e2 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ tilt-resources/velero_v1_backupstoragelocation.yaml tilt-resources/deployment.yaml tilt-resources/restic.yaml tilt-resources/cloud + +test/e2e/report.xml diff --git a/pkg/client/client.go b/pkg/client/client.go index dccdc05fcf..331b786f26 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -27,15 +27,20 @@ import ( "github.com/vmware-tanzu/velero/pkg/buildinfo" ) +func buildConfigFromFlags(context, kubeconfigPath string, precedence []string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath, Precedence: precedence}, + &clientcmd.ConfigOverrides{ + CurrentContext: context, + }).ClientConfig() +} + // Config returns a *rest.Config, using either the kubeconfig (if specified) or an in-cluster // configuration. func Config(kubeconfig, kubecontext, baseName string, qps float32, burst int) (*rest.Config, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() loadingRules.ExplicitPath = kubeconfig - configOverrides := &clientcmd.ConfigOverrides{CurrentContext: kubecontext} - kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - - clientConfig, err := kubeConfig.ClientConfig() + clientConfig, err := buildConfigFromFlags(kubecontext, kubeconfig, loadingRules.Precedence) if err != nil { return nil, errors.Wrap(err, "error finding Kubernetes API server config in --kubeconfig, $KUBECONFIG, or in-cluster configuration") } diff --git a/pkg/client/factory.go b/pkg/client/factory.go index 73b7d37513..3a0933af79 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -77,10 +77,11 @@ type factory struct { } // NewFactory returns a Factory. -func NewFactory(baseName string, config VeleroConfig) Factory { +func NewFactory(baseName, kubecontext string, config VeleroConfig) Factory { f := &factory{ - flags: pflag.NewFlagSet("", pflag.ContinueOnError), - baseName: baseName, + flags: pflag.NewFlagSet("", pflag.ContinueOnError), + baseName: baseName, + kubecontext: kubecontext, } f.namespace = os.Getenv("VELERO_NAMESPACE") @@ -96,8 +97,7 @@ func NewFactory(baseName string, config VeleroConfig) Factory { f.flags.StringVar(&f.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use to talk to the Kubernetes apiserver. If unset, try the environment variable KUBECONFIG, as well as in-cluster configuration") f.flags.StringVarP(&f.namespace, "namespace", "n", f.namespace, "The namespace in which Velero should operate") - f.flags.StringVar(&f.kubecontext, "kubecontext", "", "The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context)") - + //f.flags.StringVar(&f.kubecontext, "kubecontext", "", "The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context)") return f } @@ -127,7 +127,6 @@ func (f *factory) KubeClient() (kubernetes.Interface, error) { if err != nil { return nil, err } - kubeClient, err := kubernetes.NewForConfig(clientConfig) if err != nil { return nil, errors.WithStack(err) diff --git a/pkg/client/factory_test.go b/pkg/client/factory_test.go index 33b04231a9..1ea28cb874 100644 --- a/pkg/client/factory_test.go +++ b/pkg/client/factory_test.go @@ -31,14 +31,14 @@ func TestFactory(t *testing.T) { // Env variable should set the namespace if no config or argument are used os.Setenv("VELERO_NAMESPACE", "env-velero") - f := NewFactory("velero", make(map[string]interface{})) + f := NewFactory("velero", "", make(map[string]interface{})) assert.Equal(t, "env-velero", f.Namespace()) os.Unsetenv("VELERO_NAMESPACE") // Argument should change the namespace - f = NewFactory("velero", make(map[string]interface{})) + f = NewFactory("velero", "", make(map[string]interface{})) s := "flag-velero" flags := new(pflag.FlagSet) @@ -50,7 +50,7 @@ func TestFactory(t *testing.T) { // An argument overrides the env variable if both are set. os.Setenv("VELERO_NAMESPACE", "env-velero") - f = NewFactory("velero", make(map[string]interface{})) + f = NewFactory("velero", "", make(map[string]interface{})) flags = new(pflag.FlagSet) f.BindFlags(flags) diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index a7e5c2aed6..8775f69cd0 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -89,7 +89,7 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre }, } - f := client.NewFactory(name, config) + f := client.NewFactory(name, "", config) f.BindFlags(c.PersistentFlags()) // Bind features directly to the root command so it's available to all callers. diff --git a/pkg/restore/prioritize_group_version.go b/pkg/restore/prioritize_group_version.go index e68b867d38..1fde8b6058 100644 --- a/pkg/restore/prioritize_group_version.go +++ b/pkg/restore/prioritize_group_version.go @@ -214,7 +214,7 @@ func userPriorityConfigMap() (*corev1.ConfigMap, error) { return nil, errors.Wrap(err, "reading client config file") } - fc := client.NewFactory("APIGroupVersionsRestore", cfg) + fc := client.NewFactory("APIGroupVersionsRestore", "", cfg) kc, err := fc.KubeClient() if err != nil { diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 152f6674a8..f676fabd6c 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -62,6 +62,9 @@ UPGRADE_FROM_VELERO_VERSION ?= v1.7.1,v1.8.1 # to the end, nil string will be set if UPGRADE_FROM_VELERO_CLI is shorter than UPGRADE_FROM_VELERO_VERSION UPGRADE_FROM_VELERO_CLI ?= +MIGRATE_FROM_VELERO_VERSION ?= v1.8.1,self +MIGRATE_FROM_VELERO_CLI ?= + VELERO_NAMESPACE ?= velero CREDS_FILE ?= BSL_BUCKET ?= @@ -86,6 +89,10 @@ ADDITIONAL_BSL_CONFIG ?= FEATURES ?= DEBUG_E2E_TEST ?= false +DEFAULT_CLUSTER ?= +STANDBY_CLUSTER ?= + + .PHONY:ginkgo ginkgo: # Make sure ginkgo is in $GOPATH/bin go get github.com/onsi/ginkgo/ginkgo @@ -105,6 +112,8 @@ run: ginkgo -restic-helper-image=$(RESTIC_HELPER_IMAGE) \ -upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ -upgrade-from-velero-version=$(UPGRADE_FROM_VELERO_VERSION) \ + -migrate-from-velero-cli=$(MIGRATE_FROM_VELERO_CLI) \ + -migrate-from-velero-version=$(MIGRATE_FROM_VELERO_VERSION) \ -velero-namespace=$(VELERO_NAMESPACE) \ -credentials-file=$(CREDS_FILE) \ -bucket=$(BSL_BUCKET) \ @@ -123,7 +132,9 @@ run: ginkgo -install-velero=$(INSTALL_VELERO) \ -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ -kibishii-directory=$(KIBISHII_DIRECTORY) \ - -debug-e2e-test=$(DEBUG_E2E_TEST) + -debug-e2e-test=$(DEBUG_E2E_TEST) \ + -default-cluster=$(DEFAULT_CLUSTER) \ + -standby-cluster=$(STANDBY_CLUSTER) build: ginkgo mkdir -p $(OUTPUT_DIR) diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 103568e058..e4e62db593 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -42,14 +42,9 @@ func BackupRestoreTest(useVolumeSnapshots bool) { kibishiiNamespace := "kibishii-workload" var ( backupName, restoreName string - client TestClient err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { Skip("Volume snapshots not supported on kind") @@ -78,7 +73,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { restoreName = "restore-" + UUIDgen.String() // Even though we are using Velero's CloudProvider plugin for object storage, the kubernetes cluster is running on // KinD. So use the kind installation for Kibishii. - Expect(RunKibishiiTests(client, VeleroCfg, backupName, restoreName, "", kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), + Expect(RunKibishiiTests(*VeleroCfg.ClientToInstallVelero, VeleroCfg, backupName, restoreName, "", kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace") }) @@ -106,7 +101,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { secretKey: VeleroCfg.AdditionalBSLCredentials, } - Expect(CreateSecretFromFiles(context.TODO(), client, VeleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) + Expect(CreateSecretFromFiles(context.TODO(), *VeleroCfg.ClientToInstallVelero, VeleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) // Create additional BSL using credential additionalBsl := fmt.Sprintf("bsl-%s", UUIDgen) @@ -133,7 +128,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { backupName = fmt.Sprintf("%s-%s", backupName, UUIDgen) restoreName = fmt.Sprintf("%s-%s", restoreName, UUIDgen) } - Expect(RunKibishiiTests(client, VeleroCfg, backupName, restoreName, bsl, kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), + Expect(RunKibishiiTests(*VeleroCfg.ClientToInstallVelero, VeleroCfg, backupName, restoreName, bsl, kibishiiNamespace, useVolumeSnapshots)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace using BSL %s", bsl) } }) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 89d1186318..c597d7f4b6 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -47,14 +47,9 @@ func BackupDeletionWithRestic() { func backup_deletion_test(useVolumeSnapshots bool) { var ( backupName string - client TestClient err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { Skip("Volume snapshots not supported on kind") @@ -80,7 +75,7 @@ func backup_deletion_test(useVolumeSnapshots bool) { When("kibishii is the sample workload", func() { It("Deleted backups are deleted from object storage and backups deleted from object storage can be deleted locally", func() { backupName = "backup-" + UUIDgen.String() - Expect(runBackupDeletionTests(client, VeleroCfg, backupName, "", useVolumeSnapshots, VeleroCfg.KibishiiDirectory)).To(Succeed(), + Expect(runBackupDeletionTests(*VeleroCfg.ClientToInstallVelero, VeleroCfg, backupName, "", useVolumeSnapshots, VeleroCfg.KibishiiDirectory)).To(Succeed(), "Failed to run backup deletion test") }) }) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index 37162bac14..db062e67d3 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -53,17 +53,9 @@ func (b *SyncBackups) Init() { func BackupsSyncTest() { test := new(SyncBackups) var ( - client TestClient - err error + err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) - - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") - BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { @@ -82,11 +74,11 @@ func BackupsSyncTest() { It("Backups in object storage should be synced to a new Velero successfully", func() { test.Init() By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS)) - Expect(CreateNamespace(test.ctx, client, test.testNS)).To(Succeed(), + Expect(CreateNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) defer func() { - Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) + Expect(DeleteNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) }() var BackupCfg BackupConfig @@ -119,12 +111,12 @@ func BackupsSyncTest() { It("Deleted backups in object storage are synced to be deleted in Velero", func() { test.Init() By(fmt.Sprintf("Prepare workload as target to backup by creating namespace in %s namespace", test.testNS), func() { - Expect(CreateNamespace(test.ctx, client, test.testNS)).To(Succeed(), + Expect(CreateNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) }) if !VeleroCfg.Debug { defer func() { - Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), + Expect(DeleteNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) }() } diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index 439ada6939..bc36c8bd31 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -60,11 +60,11 @@ func (b *TTL) Init() { func TTLTest() { useVolumeSnapshots := true test := new(TTL) - client, err := NewTestClient() + client, err := NewTestClient(VeleroCfg.DefaultCluster) if err != nil { println(err.Error()) } - Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + //Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") BeforeEach(func() { flag.Parse() @@ -142,7 +142,7 @@ func TTLTest() { By(fmt.Sprintf("Restore %s", test.testNS), func() { Expect(VeleroRestore(test.ctx, VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, test.restoreName, test.backupName)).To(Succeed(), func() string { + VeleroCfg.VeleroNamespace, test.restoreName, test.backupName, "")).To(Succeed(), func() string { RunDebug(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", test.restoreName) return "Fail to restore workload" diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 1266a79d4d..86afaf8926 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -45,14 +45,8 @@ func APIGropuVersionsTest() { resource, group string err error ctx = context.Background() - client TestClient ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) - BeforeEach(func() { resource = "rockbands" group = "music.example.io" @@ -91,7 +85,7 @@ func APIGropuVersionsTest() { It("Should back up API group version and restore by version priority", func() { Expect(runEnableAPIGroupVersionsTests( ctx, - client, + *VeleroCfg.ClientToInstallVelero, resource, group, )).To(Succeed(), "Failed to successfully backup and restore multiple API Groups") @@ -288,7 +282,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso restore := "restore-rockbands-" + UUIDgen.String() + "-" + strconv.Itoa(i) if tc.want != nil { - if err := VeleroRestore(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, restore, backup); err != nil { + if err := VeleroRestore(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, restore, backup, ""); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", restore) return errors.Wrapf(err, "restore %s namespaces on target cluster", namespacesStr) } @@ -327,7 +321,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso } else { // No custom resource should have been restored. Expect "no resource found" // error during restore. - err := VeleroRestore(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, restore, backup) + err := VeleroRestore(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, restore, backup, "") if err.Error() != "Unexpected restore phase got PartiallyFailed, expecting Completed" { return errors.New("expected error but not none") diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index e5dd0a6b54..768363e48c 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -53,15 +53,9 @@ func BslDeletionWithRestic() { } func BslDeletionTest(useVolumeSnapshots bool) { var ( - client TestClient - err error + err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) - less := func(a, b string) bool { return a < b } BeforeEach(func() { if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { @@ -79,7 +73,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { AfterEach(func() { if VeleroCfg.InstallVelero { if !VeleroCfg.Debug { - Expect(DeleteNamespace(context.Background(), client, bslDeletionTestNs, + Expect(DeleteNamespace(context.Background(), *VeleroCfg.ClientToInstallVelero, bslDeletionTestNs, true)).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", bslDeletionTestNs)) Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, @@ -116,7 +110,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { } By(fmt.Sprintf("Create Secret for additional BSL %s", additionalBsl), func() { - Expect(CreateSecretFromFiles(context.TODO(), client, VeleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) + Expect(CreateSecretFromFiles(context.TODO(), *VeleroCfg.ClientToInstallVelero, VeleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) }) By(fmt.Sprintf("Create additional BSL using credential %s", secretName), func() { @@ -147,11 +141,11 @@ func BslDeletionTest(useVolumeSnapshots bool) { //label_2 := "for!=1" label_2 := "for=2" By("Create namespace for sample workload", func() { - Expect(CreateNamespace(oneHourTimeout, client, bslDeletionTestNs)).To(Succeed()) + Expect(CreateNamespace(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, bslDeletionTestNs)).To(Succeed()) }) By("Deploy sample workload of Kibishii", func() { - Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, VeleroCfg.CloudProvider, + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, VeleroCfg.CloudProvider, bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) }) @@ -218,14 +212,14 @@ func BslDeletionTest(useVolumeSnapshots bool) { var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_1), func() { - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) + snapshotCheckPoint, err = GetSnapshotCheckPoint(*VeleroCfg.ClientToInstallVelero, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLConfig, backupName_1, snapshotCheckPoint)).To(Succeed()) }) By(fmt.Sprintf("Snapshot of bsl %s should be created in cloud object store", backupLocation_2), func() { - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) + snapshotCheckPoint, err = GetSnapshotCheckPoint(*VeleroCfg.ClientToInstallVelero, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") var BSLCredentials, BSLConfig string if VeleroCfg.CloudProvider == "vsphere" { @@ -242,11 +236,11 @@ func BslDeletionTest(useVolumeSnapshots bool) { }) } else { // For Restics By(fmt.Sprintf("Resticrepositories for BSL %s should be created in Velero namespace", backupLocation_1), func() { - Expect(ResticRepositoriesCountShouldBe(context.Background(), + Expect(BackupRepositoriesCountShouldBe(context.Background(), VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_1, 1)).To(Succeed()) }) By(fmt.Sprintf("Resticrepositories for BSL %s should be created in Velero namespace", backupLocation_2), func() { - Expect(ResticRepositoriesCountShouldBe(context.Background(), + Expect(BackupRepositoriesCountShouldBe(context.Background(), VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_2, 1)).To(Succeed()) }) } @@ -317,7 +311,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { snapshotCheckPoint.NamespaceBackedUp = bslDeletionTestNs By(fmt.Sprintf("Snapshot should not be deleted in cloud object store after deleting bsl %s", backupLocation_1), func() { - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) + snapshotCheckPoint, err = GetSnapshotCheckPoint(*VeleroCfg.ClientToInstallVelero, VeleroCfg, 1, bslDeletionTestNs, backupName_1, []string{podName_1}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, @@ -332,7 +326,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { BSLCredentials = VeleroCfg.CloudCredentialsFile BSLConfig = VeleroCfg.BSLConfig } - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) + snapshotCheckPoint, err = GetSnapshotCheckPoint(*VeleroCfg.ClientToInstallVelero, VeleroCfg, 1, bslDeletionTestNs, backupName_2, []string{podName_2}) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, BSLCredentials, VeleroCfg.AdditionalBSLBucket, @@ -340,11 +334,11 @@ func BslDeletionTest(useVolumeSnapshots bool) { }) } else { // For Restic By(fmt.Sprintf("Resticrepositories for BSL %s should be deleted in Velero namespace", backupLocation_1), func() { - Expect(ResticRepositoriesCountShouldBe(context.Background(), + Expect(BackupRepositoriesCountShouldBe(context.Background(), VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_1, 0)).To(Succeed()) }) By(fmt.Sprintf("Resticrepositories for BSL %s should still exist in Velero namespace", backupLocation_2), func() { - Expect(ResticRepositoriesCountShouldBe(context.Background(), + Expect(BackupRepositoriesCountShouldBe(context.Background(), VeleroCfg.VeleroNamespace, bslDeletionTestNs+"-"+backupLocation_2, 1)).To(Succeed()) }) } diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 476149ae4c..088c4668d9 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -17,7 +17,10 @@ limitations under the License. package e2e_test import ( + "context" + "errors" "flag" + "fmt" "testing" . "github.com/onsi/ginkgo" @@ -35,6 +38,9 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/resource-filtering" . "github.com/vmware-tanzu/velero/test/e2e/scale" . "github.com/vmware-tanzu/velero/test/e2e/upgrade" + + . "github.com/vmware-tanzu/velero/test/e2e/migration" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" ) func init() { @@ -50,6 +56,8 @@ func init() { flag.StringVar(&VeleroCfg.ResticHelperImage, "restic-helper-image", "", "image for the velero restic restore helper to be tested.") flag.StringVar(&VeleroCfg.UpgradeFromVeleroCLI, "upgrade-from-velero-cli", "", "path to the pre-upgrade velero application to use.") flag.StringVar(&VeleroCfg.UpgradeFromVeleroVersion, "upgrade-from-velero-version", "v1.7.1", "image for the pre-upgrade velero server to be tested.") + flag.StringVar(&VeleroCfg.MigrateFromVeleroCLI, "migrate-from-velero-cli", "", "path to the origin velero application to use.") + flag.StringVar(&VeleroCfg.MigrateFromVeleroVersion, "migrate-from-velero-version", "self", "image for the origin velero server to be tested.") flag.StringVar(&VeleroCfg.BSLConfig, "bsl-config", "", "configuration to use for the backup storage location. Format is key1=value1,key2=value2") flag.StringVar(&VeleroCfg.BSLPrefix, "prefix", "", "prefix under which all Velero data should be stored within the bucket. Optional.") flag.StringVar(&VeleroCfg.VSLConfig, "vsl-config", "", "configuration to use for the volume snapshot location. Format is key1=value1,key2=value2") @@ -67,6 +75,8 @@ func init() { flag.StringVar(&VeleroCfg.Features, "features", "", "Comma-separated list of features to enable for this Velero process.") flag.BoolVar(&VeleroCfg.Debug, "debug-e2e-test", false, "Switch to control namespace cleaning.") flag.StringVar(&VeleroCfg.GCFrequency, "garbage-collection-frequency", "", "Frequency of garbage collection.") + flag.StringVar(&VeleroCfg.DefaultCluster, "default-cluster", "", "Default cluster context for migration test.") + flag.StringVar(&VeleroCfg.StandbyCluster, "standby-cluster", "", "Standby cluster context for migration test.") } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) @@ -106,8 +116,41 @@ var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controlle var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) +var _ = Describe("[Migration][Restic]", MigrationWithRestic) + +var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots) + var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) +func GetKubeconfigContext() error { + var err error + var tcDefault, tcStandby TestClient + tcDefault, err = NewTestClient(VeleroCfg.DefaultCluster) + VeleroCfg.DefaultClient = &tcDefault + VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient + if err != nil { + return err + } + + if VeleroCfg.DefaultCluster != "" { + err = KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster) + if err != nil { + return err + } + if VeleroCfg.StandbyCluster != "" { + tcStandby, err = NewTestClient(VeleroCfg.StandbyCluster) + VeleroCfg.StandbyClient = &tcStandby + if err != nil { + return err + } + } else { + return errors.New("migration test needs 2 clusters to run") + } + } + + return nil +} + func TestE2e(t *testing.T) { // Skip running E2E tests when running only "short" tests because: // 1. E2E tests are long running tests involving installation of Velero and performing backup and restore operations. @@ -116,6 +159,12 @@ func TestE2e(t *testing.T) { t.Skip("Skipping E2E tests") } + var err error + if err = GetKubeconfigContext(); err != nil { + fmt.Println(err) + t.FailNow() + } + RegisterFailHandler(Fail) junitReporter := reporters.NewJUnitReporter("report.xml") RunSpecsWithDefaultAndCustomReporters(t, "E2e Suite", []Reporter{junitReporter}) diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go new file mode 100644 index 0000000000..bdfdc126ac --- /dev/null +++ b/test/e2e/migration/migration.go @@ -0,0 +1,256 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package migration + +import ( + "context" + "flag" + "fmt" + "strings" + "time" + + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" + . "github.com/vmware-tanzu/velero/test/e2e/util/providers" + . "github.com/vmware-tanzu/velero/test/e2e/util/velero" +) + +var migrationNamespace string + +func MigrationWithSnapshots() { + for _, veleroCLI2Version := range GetVersionList(VeleroCfg.MigrateFromVeleroCLI, VeleroCfg.MigrateFromVeleroVersion) { + MigrationTest(true, veleroCLI2Version) + } +} + +func MigrationWithRestic() { + for _, veleroCLI2Version := range GetVersionList(VeleroCfg.MigrateFromVeleroCLI, VeleroCfg.MigrateFromVeleroVersion) { + MigrationTest(false, veleroCLI2Version) + } +} + +func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) { + var ( + backupName, restoreName string + backupScName, restoreScName string + err error + ) + + BeforeEach(func() { + UUIDgen, err = uuid.NewRandom() + migrationNamespace = "migration-workload-" + UUIDgen.String() + if useVolumeSnapshots && VeleroCfg.CloudProvider == "kind" { + Skip("Volume snapshots not supported on kind") + } + if useVolumeSnapshots && VeleroCfg.CloudProvider == "aws" { + Skip("Volume snapshots migration not supported on AWS provisioned by Sheperd public pool") + } + if VeleroCfg.DefaultCluster == "" && VeleroCfg.StandbyCluster == "" { + Skip("Migration test needs 2 clusters") + } + }) + AfterEach(func() { + if VeleroCfg.InstallVelero { + if !VeleroCfg.Debug { + By(fmt.Sprintf("Uninstall Velero and delete sample workload namespace %s", migrationNamespace), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + DeleteNamespace(context.Background(), *VeleroCfg.DefaultClient, migrationNamespace, true) + + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.StandbyCluster)).To(Succeed()) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + DeleteNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace, true) + }) + By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient + }) + } + } + }) + When("kibishii is the sample workload", func() { + It("should be successfully backed up and restored to the default BackupStorageLocation", func() { + flag.Parse() + UUIDgen, err = uuid.NewRandom() + Expect(err).To(Succeed()) + + oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*10) + + if veleroCLI2Version.VeleroCLI == "" { + //Assume tag of velero server image is identical to velero CLI version + //Download velero CLI if it's empty according to velero CLI version + By(fmt.Sprintf("Install the expected version Velero CLI (%s) for installing Velero", + veleroCLI2Version.VeleroVersion), func() { + if veleroCLI2Version.VeleroVersion == "self" { + veleroCLI2Version.VeleroCLI = VeleroCfg.VeleroCLI + } else { + veleroCLI2Version.VeleroCLI, err = InstallVeleroCLI(veleroCLI2Version.VeleroVersion) + Expect(err).To(Succeed()) + } + }) + } + + By(fmt.Sprintf("Install Velero in cluster-A (%s) to backup workload", VeleroCfg.DefaultCluster), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + + OriginVeleroCfg := VeleroCfg + OriginVeleroCfg.MigrateFromVeleroVersion = veleroCLI2Version.VeleroVersion + OriginVeleroCfg.VeleroCLI = veleroCLI2Version.VeleroCLI + OriginVeleroCfg.ClientToInstallVelero = OriginVeleroCfg.DefaultClient + if veleroCLI2Version.VeleroVersion != "self" { + fmt.Printf("Using default images address of Velero CLI %s\n", veleroCLI2Version.VeleroVersion) + OriginVeleroCfg.VeleroImage = "" + OriginVeleroCfg.ResticHelperImage = "" + OriginVeleroCfg.Plugins = "" + } + fmt.Println(OriginVeleroCfg) + Expect(VeleroInstall(context.Background(), &OriginVeleroCfg, useVolumeSnapshots)).To(Succeed()) + if veleroCLI2Version.VeleroVersion != "self" { + Expect(CheckVeleroVersion(context.Background(), OriginVeleroCfg.VeleroCLI, + OriginVeleroCfg.MigrateFromVeleroVersion)).To(Succeed()) + } + }) + + backupName = "backup-" + UUIDgen.String() + backupScName = backupName + "-sc" + restoreName = "restore-" + UUIDgen.String() + restoreScName = restoreName + "-sc" + + By("Create namespace for sample workload", func() { + Expect(CreateNamespace(oneHourTimeout, *VeleroCfg.DefaultClient, migrationNamespace)).To(Succeed(), + fmt.Sprintf("Failed to create namespace %s to install Kibishii workload", migrationNamespace)) + }) + + By("Deploy sample workload of Kibishii", func() { + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.DefaultClient, VeleroCfg.CloudProvider, + migrationNamespace, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, + VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + }) + + By(fmt.Sprintf("Backup namespace %s", migrationNamespace), func() { + var BackupStorageClassCfg BackupConfig + BackupStorageClassCfg.BackupName = backupScName + BackupStorageClassCfg.IncludeResources = "StorageClass" + BackupStorageClassCfg.IncludeClusterResources = true + Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupStorageClassCfg)).ShouldNot(HaveOccurred(), func() string { + err = VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName) + return "Get backup logs" + }) + + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName + BackupCfg.Namespace = migrationNamespace + BackupCfg.UseVolumeSnapshots = useVolumeSnapshots + BackupCfg.BackupLocation = "" + BackupCfg.Selector = "" + //BackupCfg.ExcludeResources = "tierentitlementbindings,tierentitlements,tiers,capabilities,customresourcedefinitions" + Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { + err = VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName) + return "Get backup logs" + }) + + }) + + if useVolumeSnapshots { + if VeleroCfg.CloudProvider == "vsphere" { + // TODO - remove after upload progress monitoring is implemented + By("Waiting for vSphere uploads to complete", func() { + Expect(WaitForVSphereUploadCompletion(context.Background(), time.Hour, + migrationNamespace)).To(Succeed()) + }) + } + var snapshotCheckPoint SnapshotCheckPoint + snapshotCheckPoint.NamespaceBackedUp = migrationNamespace + By("Snapshot should be created in cloud object store", func() { + snapshotCheckPoint, err := GetSnapshotCheckPoint(*VeleroCfg.DefaultClient, VeleroCfg, 2, + migrationNamespace, backupName, KibishiiPodNameList) + Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") + Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, + VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, + VeleroCfg.BSLConfig, backupName, snapshotCheckPoint)).To(Succeed()) + }) + } + + if useVolumeSnapshots && VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { + // Upgrade test is not running daily since no CSI plugin v1.0 released, because builds before + // v1.0 have issues to fail upgrade case. + By("Sleep 5 minutes to avoid snapshot recreated by unknown reason ", func() { + time.Sleep(5 * time.Minute) + }) + } + // the snapshots of AWS may be still in pending status when do the restore, wait for a while + // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 + // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed + if VeleroCfg.CloudProvider == "aws" && useVolumeSnapshots { + fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") + time.Sleep(5 * time.Minute) + } + + By(fmt.Sprintf("Install Velero in cluster-B (%s) to restore workload", VeleroCfg.StandbyCluster), func() { + ns, err := GetNamespace(context.Background(), *VeleroCfg.DefaultClient, migrationNamespace) + Expect(ns.Name).To(Equal(migrationNamespace)) + Expect(err).NotTo(HaveOccurred()) + + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.StandbyCluster)).To(Succeed()) + _, err = GetNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace) + Expect(err).To(HaveOccurred()) + strings.Contains(fmt.Sprint(err), "namespaces \""+migrationNamespace+"\" not found") + + fmt.Println(err) + + VeleroCfg.ObjectStoreProvider = "" + VeleroCfg.ClientToInstallVelero = VeleroCfg.StandbyClient + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) + }) + + By(fmt.Sprintf("Waiting for backups sync to Velero in cluster-B (%s)", VeleroCfg.StandbyCluster), func() { + Expect(WaitForBackupToBeCreated(context.Background(), VeleroCfg.VeleroCLI, backupName, 5*time.Minute)).To(Succeed()) + Expect(WaitForBackupToBeCreated(context.Background(), VeleroCfg.VeleroCLI, backupScName, 5*time.Minute)).To(Succeed()) + }) + + By(fmt.Sprintf("Restore %s", migrationNamespace), func() { + Expect(VeleroRestore(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, restoreScName, backupScName, "StorageClass")).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, "", restoreName) + return "Fail to restore workload" + }) + Expect(VeleroRestore(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, restoreName, backupName, "")).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, "", restoreName) + return "Fail to restore workload" + }) + }) + + By(fmt.Sprintf("Verify workload %s after restore ", migrationNamespace), func() { + Expect(KibishiiVerifyAfterRestore(*VeleroCfg.StandbyClient, migrationNamespace, + oneHourTimeout)).To(Succeed(), "Fail to verify workload after restore") + }) + }) + }) +} diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go index 2a9fe4c975..af6074ee8f 100644 --- a/test/e2e/orderedresources/ordered_resources.go +++ b/test/e2e/orderedresources/ordered_resources.go @@ -112,7 +112,7 @@ func ScheduleOrderedResources() { func (o *OrderedResources) Init() error { rand.Seed(time.Now().UnixNano()) UUIDgen, _ = uuid.NewRandom() - client, err := NewTestClient() + client, err := NewTestClient(VeleroCfg.DefaultCluster) if err != nil { return fmt.Errorf("failed to init ordered resources test with err %v", err) } diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index ee1ed5631f..526efb1e84 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -37,15 +37,9 @@ import ( func SSRTest() { testNS := "ssr-test" var ( - client TestClient - err error + err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) - BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { @@ -62,10 +56,10 @@ func SSRTest() { }) It(fmt.Sprintf("Should create an ssr object in the %s namespace and later removed by controller", VeleroCfg.VeleroNamespace), func() { - defer DeleteNamespace(context.TODO(), client, testNS, false) + defer DeleteNamespace(context.TODO(), *VeleroCfg.ClientToInstallVelero, testNS, false) ctx, _ := context.WithTimeout(context.Background(), time.Duration(time.Minute*10)) By(fmt.Sprintf("Create %s namespace", testNS)) - Expect(CreateNamespace(ctx, client, testNS)).To(Succeed(), + Expect(CreateNamespace(ctx, *VeleroCfg.ClientToInstallVelero, testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", testNS)) By(fmt.Sprintf("Get version in %s namespace", testNS)) @@ -82,7 +76,7 @@ func SSRTest() { By(fmt.Sprintf("Check ssr object in %s namespace", VeleroCfg.VeleroNamespace)) err = waitutil.PollImmediate(5*time.Second, time.Minute, func() (bool, error) { - if err = client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + if err = VeleroCfg.ClientToInstallVelero.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { return false, fmt.Errorf("failed to list ssr object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) } if len(ssrListResp.Items) != 1 { @@ -105,7 +99,7 @@ func SSRTest() { Expect(err).To(Succeed(), fmt.Sprintf("Failed to check ssr object in %s namespace", VeleroCfg.VeleroNamespace)) By(fmt.Sprintf("Check ssr object in %s namespace", testNS)) - Expect(client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: testNS})).To(Succeed(), + Expect(VeleroCfg.ClientToInstallVelero.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: testNS})).To(Succeed(), fmt.Sprintf("Failed to list ssr object in %s namespace", testNS)) Expect(len(ssrListResp.Items)).To(BeNumerically("==", 1), fmt.Sprintf("Count of ssr object in %s namespace is not 1", testNS)) @@ -117,7 +111,7 @@ func SSRTest() { By(fmt.Sprintf("Waiting ssr object in %s namespace deleted", VeleroCfg.VeleroNamespace)) err = waitutil.PollImmediateInfinite(5*time.Second, func() (bool, error) { - if err = client.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + if err = VeleroCfg.ClientToInstallVelero.Kubebuilder.List(ctx, ssrListResp, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { if apierrors.IsNotFound(err) { return true, nil } diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 811b69ac94..0161dafc5c 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -73,9 +73,7 @@ var TestClientInstance TestClient func TestFunc(test VeleroBackupRestoreTest) func() { return func() { By("Create test client instance", func() { - var err error - TestClientInstance, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + TestClientInstance = *VeleroCfg.ClientToInstallVelero }) Expect(test.Init()).To(Succeed(), "Failed to instantiate test cases") BeforeEach(func() { @@ -99,13 +97,11 @@ func TestFunc(test VeleroBackupRestoreTest) func() { func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { return func() { - var err error var countIt int By("Create test client instance", func() { - TestClientInstance, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") + TestClientInstance = *VeleroCfg.ClientToInstallVelero }) - //Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") + for k := range tests { Expect(tests[k].Init()).To(Succeed(), fmt.Sprintf("Failed to instantiate test %s case", tests[k].GetTestMsg().Desc)) } diff --git a/test/e2e/types.go b/test/e2e/types.go index 9a92ea495d..bb54562e19 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -20,6 +20,8 @@ import ( "time" "github.com/google/uuid" + + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" ) var UUIDgen uuid.UUID @@ -47,6 +49,8 @@ type VerleroConfig struct { ResticHelperImage string UpgradeFromVeleroVersion string UpgradeFromVeleroCLI string + MigrateFromVeleroVersion string + MigrateFromVeleroCLI string Plugins string AddBSLPlugins string InstallVelero bool @@ -54,6 +58,11 @@ type VerleroConfig struct { Features string Debug bool GCFrequency string + DefaultCluster string + StandbyCluster string + ClientToInstallVelero *TestClient + DefaultClient *TestClient + StandbyClient *TestClient } type SnapshotCheckPoint struct { @@ -67,10 +76,19 @@ type SnapshotCheckPoint struct { } type BackupConfig struct { - BackupName string - Namespace string - BackupLocation string - UseVolumeSnapshots bool - Selector string - TTL time.Duration + BackupName string + Namespace string + BackupLocation string + UseVolumeSnapshots bool + Selector string + TTL time.Duration + IncludeResources string + ExcludeResources string + IncludeClusterResources bool + OrderedResources string +} + +type VeleroCLI2Version struct { + VeleroVersion string + VeleroCLI string } diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 7461697a25..22e491f219 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -33,56 +33,28 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) -type UpgradeFromVelero struct { - UpgradeFromVeleroVersion string - UpgradeFromVeleroCLI string -} - const ( upgradeNamespace = "upgrade-workload" ) -func GetUpgradePathList() []UpgradeFromVelero { - var upgradeFromVeleroList []UpgradeFromVelero - UpgradeFromVeleroVersionList := strings.Split(VeleroCfg.UpgradeFromVeleroVersion, ",") - UpgradeFromVeleroCliList := strings.Split(VeleroCfg.UpgradeFromVeleroCLI, ",") - - for _, upgradeFromVeleroVersion := range UpgradeFromVeleroVersionList { - upgradeFromVeleroList = append(upgradeFromVeleroList, - UpgradeFromVelero{upgradeFromVeleroVersion, ""}) - } - for i, upgradeFromVeleroCli := range UpgradeFromVeleroCliList { - if i == len(UpgradeFromVeleroVersionList)-1 { - break - } - upgradeFromVeleroList[i].UpgradeFromVeleroCLI = upgradeFromVeleroCli - } - return upgradeFromVeleroList -} - func BackupUpgradeRestoreWithSnapshots() { - for _, upgradeFromVelero := range GetUpgradePathList() { + for _, upgradeFromVelero := range GetVersionList(VeleroCfg.UpgradeFromVeleroCLI, VeleroCfg.UpgradeFromVeleroVersion) { BackupUpgradeRestoreTest(true, upgradeFromVelero) } } func BackupUpgradeRestoreWithRestic() { - for _, upgradeFromVelero := range GetUpgradePathList() { + for _, upgradeFromVelero := range GetVersionList(VeleroCfg.UpgradeFromVeleroCLI, VeleroCfg.UpgradeFromVeleroVersion) { BackupUpgradeRestoreTest(false, upgradeFromVelero) } } -func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero UpgradeFromVelero) { +func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) { var ( backupName, restoreName string - client TestClient err error ) - By("Create test client instance", func() { - client, err = NewTestClient() - Expect(err).NotTo(HaveOccurred(), "Failed to instantiate cluster client for backup tests") - }) BeforeEach(func() { if !VeleroCfg.InstallVelero { Skip("Upgrade test should not be triggered if VeleroCfg.InstallVelero is set to false") @@ -101,7 +73,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade if VeleroCfg.InstallVelero { if !VeleroCfg.Debug { By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { - DeleteNamespace(context.Background(), client, upgradeNamespace, true) + DeleteNamespace(context.Background(), *VeleroCfg.ClientToInstallVelero, upgradeNamespace, true) }) By("Uninstall Velero", func() { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, @@ -115,24 +87,25 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade flag.Parse() UUIDgen, err = uuid.NewRandom() Expect(err).To(Succeed()) + oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) - if upgradeFromVelero.UpgradeFromVeleroCLI == "" { + if veleroCLI2Version.VeleroCLI == "" { //Assume tag of velero server image is identical to velero CLI version //Download velero CLI if it's empty according to velero CLI version By(fmt.Sprintf("Install the expected old version Velero CLI (%s) for installing Velero", - upgradeFromVelero.UpgradeFromVeleroVersion), func() { - upgradeFromVelero.UpgradeFromVeleroCLI, err = InstallVeleroCLI(upgradeFromVelero.UpgradeFromVeleroVersion) + veleroCLI2Version.VeleroVersion), func() { + veleroCLI2Version.VeleroCLI, err = InstallVeleroCLI(veleroCLI2Version.VeleroVersion) Expect(err).To(Succeed()) }) } VeleroCfg.GCFrequency = "" By(fmt.Sprintf("Install the expected old version Velero (%s) for upgrade", - upgradeFromVelero.UpgradeFromVeleroVersion), func() { + veleroCLI2Version.VeleroVersion), func() { //Set VeleroImage and ResticHelperImage to blank //VeleroImage and ResticHelperImage should be the default value in originalCli tmpCfgForOldVeleroInstall := VeleroCfg - tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion - tmpCfgForOldVeleroInstall.VeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI + tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion = veleroCLI2Version.VeleroVersion + tmpCfgForOldVeleroInstall.VeleroCLI = veleroCLI2Version.VeleroCLI tmpCfgForOldVeleroInstall.VeleroImage = "" tmpCfgForOldVeleroInstall.ResticHelperImage = "" tmpCfgForOldVeleroInstall.Plugins = "" @@ -146,17 +119,16 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade backupName = "backup-" + UUIDgen.String() restoreName = "restore-" + UUIDgen.String() tmpCfg := VeleroCfg - tmpCfg.UpgradeFromVeleroCLI = upgradeFromVelero.UpgradeFromVeleroCLI - tmpCfg.UpgradeFromVeleroVersion = upgradeFromVelero.UpgradeFromVeleroVersion - oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60) + tmpCfg.UpgradeFromVeleroCLI = veleroCLI2Version.VeleroCLI + tmpCfg.UpgradeFromVeleroVersion = veleroCLI2Version.VeleroVersion By("Create namespace for sample workload", func() { - Expect(CreateNamespace(oneHourTimeout, client, upgradeNamespace)).To(Succeed(), + Expect(CreateNamespace(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, upgradeNamespace)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s to install Kibishii workload", upgradeNamespace)) }) By("Deploy sample workload of Kibishii", func() { - Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, client, tmpCfg.CloudProvider, + Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, tmpCfg.CloudProvider, upgradeNamespace, tmpCfg.RegistryCredentialFile, tmpCfg.Features, tmpCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) }) @@ -187,7 +159,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade var snapshotCheckPoint SnapshotCheckPoint snapshotCheckPoint.NamespaceBackedUp = upgradeNamespace By("Snapshot should be created in cloud object store", func() { - snapshotCheckPoint, err := GetSnapshotCheckPoint(client, VeleroCfg, 2, + snapshotCheckPoint, err := GetSnapshotCheckPoint(*VeleroCfg.ClientToInstallVelero, VeleroCfg, 2, upgradeNamespace, backupName, KibishiiPodNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(VeleroCfg.CloudProvider, @@ -197,7 +169,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade } By(fmt.Sprintf("Simulating a disaster by removing namespace %s\n", upgradeNamespace), func() { - Expect(DeleteNamespace(oneHourTimeout, client, upgradeNamespace, true)).To(Succeed(), + Expect(DeleteNamespace(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, upgradeNamespace, true)).To(Succeed(), fmt.Sprintf("failed to delete namespace %s", upgradeNamespace)) }) @@ -225,7 +197,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade By(fmt.Sprintf("Restore %s", upgradeNamespace), func() { Expect(VeleroRestore(oneHourTimeout, tmpCfg.VeleroCLI, - tmpCfg.VeleroNamespace, restoreName, backupName)).To(Succeed(), func() string { + tmpCfg.VeleroNamespace, restoreName, backupName, "")).To(Succeed(), func() string { RunDebug(context.Background(), tmpCfg.VeleroCLI, tmpCfg.VeleroNamespace, "", restoreName) return "Fail to restore workload" @@ -233,7 +205,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, upgradeFromVelero Upgrade }) By(fmt.Sprintf("Verify workload %s after restore ", upgradeNamespace), func() { - Expect(KibishiiVerifyAfterRestore(client, upgradeNamespace, + Expect(KibishiiVerifyAfterRestore(*VeleroCfg.ClientToInstallVelero, upgradeNamespace, oneHourTimeout)).To(Succeed(), "Fail to verify workload after restore") }) }) diff --git a/test/e2e/util/k8s/client.go b/test/e2e/util/k8s/client.go index a6907045d3..4601bae8fc 100644 --- a/test/e2e/util/k8s/client.go +++ b/test/e2e/util/k8s/client.go @@ -17,8 +17,6 @@ limitations under the License. package k8s import ( - "sync" - "k8s.io/client-go/kubernetes" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -48,28 +46,24 @@ type TestClient struct { } var ( - once sync.Once - testClient TestClient - err error + err error ) -func NewTestClient() (TestClient, error) { - once.Do(func() { // <-- atomic, does not allow repeating - testClient, err = InitTestClient() // <-- thread safe - }) - return testClient, err +// NewTestClient returns a set of ready-to-use API clients. +func NewTestClient(kubecontext string) (TestClient, error) { + return InitTestClient(kubecontext) } -// NewTestClient returns a set of ready-to-use API clients. -func InitTestClient() (TestClient, error) { +func InitTestClient(kubecontext string) (TestClient, error) { config, err := client.LoadConfig() if err != nil { return TestClient{}, err } - f := client.NewFactory("e2e", config) + f := client.NewFactory("e2e", kubecontext, config) clientGo, err := f.KubeClient() + if err != nil { return TestClient{}, err } diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index d562c75b35..6a378268cb 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/vmware-tanzu/velero/pkg/builder" + veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" common "github.com/vmware-tanzu/velero/test/e2e/util/common" ) @@ -56,11 +57,11 @@ func CreateSecretFromFiles(ctx context.Context, client TestClient, namespace str // WaitForPods waits until all of the pods have gone to PodRunning state func WaitForPods(ctx context.Context, client TestClient, namespace string, pods []string) error { - timeout := 10 * time.Minute + timeout := 5 * time.Minute interval := 5 * time.Second err := wait.PollImmediate(interval, timeout, func() (bool, error) { for _, podName := range pods { - checkPod, err := client.ClientGo.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + checkPod, err := client.ClientGo.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) if err != nil { //Should ignore "etcdserver: request timed out" kind of errors, try to get pod status again before timeout. fmt.Println(errors.Wrap(err, fmt.Sprintf("Failed to verify pod %s/%s is %s, try again...", namespace, podName, corev1api.PodRunning))) @@ -143,3 +144,13 @@ func KubectlApplyByFile(ctx context.Context, file string) error { args := []string{"apply", "-f", file, "--force=true"} return exec.CommandContext(ctx, "kubectl", args...).Run() } + +func KubectlConfigUseContext(ctx context.Context, kubectlContext string) error { + cmd := exec.CommandContext(ctx, "kubectl", + "config", "use-context", kubectlContext) + fmt.Printf("Kubectl config use-context cmd =%v\n", cmd) + stdout, stderr, err := veleroexec.RunCommand(cmd) + fmt.Print(stdout) + fmt.Print(stderr) + return err +} diff --git a/test/e2e/util/k8s/namespace.go b/test/e2e/util/k8s/namespace.go index 9b28873ef9..b5248e425d 100644 --- a/test/e2e/util/k8s/namespace.go +++ b/test/e2e/util/k8s/namespace.go @@ -67,21 +67,22 @@ func GetNamespace(ctx context.Context, client TestClient, namespace string) (*co } func DeleteNamespace(ctx context.Context, client TestClient, namespace string, wait bool) error { - if err := client.ClientGo.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{}); err != nil { + oneMinuteTimeout, _ := context.WithTimeout(context.Background(), time.Minute*1) + if err := client.ClientGo.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}); err != nil { return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) } if !wait { return nil } - return waitutil.PollImmediateInfinite(5*time.Second, func() (bool, error) { - if _, err := client.ClientGo.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{}); err != nil { + if _, err := client.ClientGo.CoreV1().Namespaces().Get(oneMinuteTimeout, namespace, metav1.GetOptions{}); err != nil { if apierrors.IsNotFound(err) { return true, nil } return false, err } + fmt.Printf("namespace %q is still being deleted...\n", namespace) logrus.Debugf("namespace %q is still being deleted...", namespace) return false, nil }) diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 8406cda55b..d6afbac013 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -118,7 +118,7 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re time.Sleep(5 * time.Minute) } - if err := VeleroRestore(oneHourTimeout, veleroCLI, veleroNamespace, restoreName, backupName); err != nil { + if err := VeleroRestore(oneHourTimeout, veleroCLI, veleroNamespace, restoreName, backupName, ""); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, "", restoreName) return errors.Wrapf(err, "Restore %s failed from backup %s", restoreName, backupName) } @@ -134,8 +134,7 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re func installKibishii(ctx context.Context, namespace string, cloudPlatform, veleroFeatures, kibishiiDirectory string, useVolumeSnapshots bool) error { if strings.EqualFold(cloudPlatform, "azure") && - strings.EqualFold(veleroFeatures, "EnableCSI") && - useVolumeSnapshots { + strings.EqualFold(veleroFeatures, "EnableCSI") { cloudPlatform = "azure-csi" } // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories @@ -182,7 +181,8 @@ func generateData(ctx context.Context, namespace string, levels int, filesPerLev func verifyData(ctx context.Context, namespace string, levels int, filesPerLevel int, dirsPerLevel int, fileSize int, blockSize int, passNum int, expectedNodes int) error { - kibishiiVerifyCmd := exec.CommandContext(ctx, "kubectl", "exec", "-n", namespace, "jump-pad", "--", + timeout, _ := context.WithTimeout(context.Background(), time.Minute*5) + kibishiiVerifyCmd := exec.CommandContext(timeout, "kubectl", "exec", "-n", namespace, "jump-pad", "--", "/usr/local/bin/verify.sh", strconv.Itoa(levels), strconv.Itoa(filesPerLevel), strconv.Itoa(dirsPerLevel), strconv.Itoa(fileSize), strconv.Itoa(blockSize), strconv.Itoa(passNum), strconv.Itoa(expectedNodes)) fmt.Printf("kibishiiVerifyCmd cmd =%v\n", kibishiiVerifyCmd) @@ -238,7 +238,7 @@ func KibishiiVerifyAfterRestore(client TestClient, kibishiiNamespace string, one if err := waitForKibishiiPods(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to wait for ready status of kibishii pods in %s", kibishiiNamespace) } - + time.Sleep(60 * time.Second) // TODO - check that namespace exists fmt.Printf("running kibishii verify\n") if err := verifyData(oneHourTimeout, kibishiiNamespace, 2, 10, 10, 1024, 1024, 0, 2); err != nil { diff --git a/test/e2e/util/providers/aws_utils.go b/test/e2e/util/providers/aws_utils.go index 3ada35acd4..e84c4fd637 100644 --- a/test/e2e/util/providers/aws_utils.go +++ b/test/e2e/util/providers/aws_utils.go @@ -181,7 +181,14 @@ func (s AWSStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupObj for _, n := range result.Snapshots { fmt.Println(n.SnapshotId) + if n.SnapshotId != nil { + fmt.Println(*n.SnapshotId) + } fmt.Println(n.Tags) + fmt.Println(n.VolumeId) + if n.VolumeId != nil { + fmt.Println(*n.VolumeId) + } } if len(result.Snapshots) != snapshotCheck.ExpectCount { return errors.New(fmt.Sprintf("Snapshot count is not as expected %d", snapshotCheck.ExpectCount)) diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index 2a4e2218b4..61ffd1bf97 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -35,7 +35,7 @@ type ObjectsInStorage interface { } func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string) error { - fmt.Printf("|| VERIFICATION || - %s %s should exist in storage [%s]\n", subPrefix, backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - %s should exist in storage [%s %s]\n", backupName, bslPrefix, subPrefix) exist, err := IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if !exist { return errors.Wrap(err, fmt.Sprintf("|| UNEXPECTED ||Backup object %s is not exist in object store after backup as expected\n", backupName)) diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index ed3f6156dd..0b3e5962ba 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -76,7 +76,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps // backup, but needed to pick up the provider plugins earlier. vSphere plugin no longer needs a Volume // Snapshot location specified veleroCfg.ObjectStoreProvider = "aws" - if err := configvSpherePlugin(); err != nil { + if err := configvSpherePlugin(*veleroCfg.ClientToInstallVelero); err != nil { return errors.WithMessagef(err, "Failed to config vsphere plugin") } } @@ -106,11 +106,8 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps } //configvSpherePlugin refers to https://github.com/vmware-tanzu/velero-plugin-for-vsphere/blob/v1.3.0/docs/vanilla.md -func configvSpherePlugin() error { - cli, err := NewTestClient() - if err != nil { - return errors.WithMessagef(err, "Failed to instantiate cluster client to config vsphere plugin") - } +func configvSpherePlugin(cli TestClient) error { + var err error vsphereSecret := "velero-vsphere-config-secret" configmaptName := "velero-vsphere-plugin-config" if err := clearupvSpherePluginConfig(cli.ClientGo, VeleroCfg.VeleroNamespace, vsphereSecret, configmaptName); err != nil { diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 46b9982e1d..aa54a0cfff 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -309,9 +309,11 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin args := []string{ "--namespace", veleroNamespace, "create", "backup", backupCfg.BackupName, - "--include-namespaces", backupCfg.Namespace, "--wait", } + if backupCfg.Namespace != "" { + args = append(args, "--include-namespaces", backupCfg.Namespace) + } if backupCfg.Selector != "" { args = append(args, "--selector", backupCfg.Selector) } @@ -332,6 +334,23 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.TTL != 0 { args = append(args, "--ttl", backupCfg.TTL.String()) } + + if backupCfg.IncludeResources != "" { + args = append(args, "--include-resources", backupCfg.IncludeResources) + } + + if backupCfg.ExcludeResources != "" { + args = append(args, "--exclude-resources", backupCfg.ExcludeResources) + } + + if backupCfg.IncludeClusterResources { + args = append(args, "--include-cluster-resources") + } + + if backupCfg.OrderedResources != "" { + args = append(args, "--ordered-resources", backupCfg.OrderedResources) + } + return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupCfg.BackupName, args) } @@ -358,11 +377,14 @@ func VeleroBackupIncludeNamespaces(ctx context.Context, veleroCLI string, velero } // VeleroRestore uses the VeleroCLI to restore from a Velero backup. -func VeleroRestore(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string, backupName string) error { +func VeleroRestore(ctx context.Context, veleroCLI, veleroNamespace, restoreName, backupName, includeResources string) error { args := []string{ "--namespace", veleroNamespace, "create", "restore", restoreName, "--from-backup", backupName, "--wait", } + if includeResources != "" { + args = append(args, "--include-resources", includeResources) + } return VeleroRestoreExec(ctx, veleroCLI, veleroNamespace, restoreName, args) } @@ -761,7 +783,7 @@ func IsBackupExist(ctx context.Context, veleroCLI string, backupName string) (bo return false, err } } - fmt.Printf("Backup %s exist locally according to output %s", backupName, out) + fmt.Printf("Backup <%s> exist locally according to output \n[%s]\n", backupName, out) return true, nil } @@ -785,9 +807,9 @@ func WaitForExpectedStateOfBackup(ctx context.Context, veleroCLI string, backupN if exist, err := IsBackupExist(ctx, veleroCLI, backupName); err != nil { return false, err } else { - msg := "does not exist" - if existing { - msg = "was found" + msg := "does not exist as expect" + if exist { + msg = "was found as expect" } if exist == existing { fmt.Println("Backup <" + backupName + "> " + msg) @@ -892,22 +914,22 @@ func SnapshotCRsCountShouldBe(ctx context.Context, namespace, backupName string, } } -func ResticRepositoriesCountShouldBe(ctx context.Context, veleroNamespace, targetNamespace string, expectedCount int) error { +func BackupRepositoriesCountShouldBe(ctx context.Context, veleroNamespace, targetNamespace string, expectedCount int) error { resticArr, err := GetResticRepositories(ctx, veleroNamespace, targetNamespace) if err != nil { - return errors.Wrapf(err, "Fail to get GetResticRepositories") + return errors.Wrapf(err, "Fail to get BackupRepositories") } if len(resticArr) == expectedCount { return nil } else { - return errors.New(fmt.Sprintf("Resticrepositories count %d in namespace %s is not as expected %d", len(resticArr), targetNamespace, expectedCount)) + return errors.New(fmt.Sprintf("BackupRepositories count %d in namespace %s is not as expected %d", len(resticArr), targetNamespace, expectedCount)) } } func GetResticRepositories(ctx context.Context, veleroNamespace, targetNamespace string) ([]string, error) { CmdLine1 := &common.OsCommandLine{ Cmd: "kubectl", - Args: []string{"get", "-n", veleroNamespace, "resticrepositories"}, + Args: []string{"get", "-n", veleroNamespace, "BackupRepositories"}, } CmdLine2 := &common.OsCommandLine{ @@ -968,3 +990,21 @@ func GetBackupTTL(ctx context.Context, veleroNamespace, backupName string) (stri // return complete, nil return stdout, err } + +func GetVersionList(veleroCli, veleroVersion string) []VeleroCLI2Version { + var veleroCLI2VersionList []VeleroCLI2Version + veleroVersionList := strings.Split(veleroVersion, ",") + veleroCliList := strings.Split(veleroCli, ",") + + for _, veleroVersion := range veleroVersionList { + veleroCLI2VersionList = append(veleroCLI2VersionList, + VeleroCLI2Version{veleroVersion, ""}) + } + for i, veleroCli := range veleroCliList { + if i == len(veleroVersionList)-1 { + break + } + veleroCLI2VersionList[i].VeleroCLI = veleroCli + } + return veleroCLI2VersionList +} From 649c3a77dfd6d2c72463a145021e569f1a03af06 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 4 Aug 2022 18:32:11 +0800 Subject: [PATCH 247/366] unified repo provider impl Signed-off-by: Lyndon-Li --- changelogs/unreleased/5179-lyndon | 1 + pkg/repository/provider/provider.go | 3 +- pkg/repository/provider/unified_repo.go | 248 +++++++++++++--- pkg/repository/provider/unified_repo_test.go | 280 ++++++++++++++++-- pkg/repository/udmrepo/mocks/BackupRepo.go | 185 ++++++++++++ .../udmrepo/mocks/BackupRepoService.go | 81 +++++ pkg/repository/udmrepo/mocks/ObjectReader.go | 95 ++++++ pkg/repository/udmrepo/mocks/ObjectWriter.go | 126 ++++++++ pkg/repository/udmrepo/repo-option-consts.go | 58 ---- pkg/repository/udmrepo/repo-options.go | 171 +++++++++++ pkg/repository/udmrepo/repo.go | 60 +--- pkg/repository/udmrepo/service/service.go | 37 +++ pkg/util/ownership/backup_owner.go | 42 --- 13 files changed, 1168 insertions(+), 219 deletions(-) create mode 100644 changelogs/unreleased/5179-lyndon create mode 100644 pkg/repository/udmrepo/mocks/BackupRepo.go create mode 100644 pkg/repository/udmrepo/mocks/BackupRepoService.go create mode 100644 pkg/repository/udmrepo/mocks/ObjectReader.go create mode 100644 pkg/repository/udmrepo/mocks/ObjectWriter.go delete mode 100644 pkg/repository/udmrepo/repo-option-consts.go create mode 100644 pkg/repository/udmrepo/repo-options.go create mode 100644 pkg/repository/udmrepo/service/service.go delete mode 100644 pkg/util/ownership/backup_owner.go diff --git a/changelogs/unreleased/5179-lyndon b/changelogs/unreleased/5179-lyndon new file mode 100644 index 0000000000..65ae6cc00c --- /dev/null +++ b/changelogs/unreleased/5179-lyndon @@ -0,0 +1 @@ +Add changes for Kopia Integration: Unified Repository Provider - method implementation \ No newline at end of file diff --git a/pkg/repository/provider/provider.go b/pkg/repository/provider/provider.go index 36d69a594e..8e6a639a4a 100644 --- a/pkg/repository/provider/provider.go +++ b/pkg/repository/provider/provider.go @@ -23,10 +23,9 @@ import ( ) // RepoParam includes the parameters to manipulate a backup repository -// SubDir is used to generate the path in the backup storage type RepoParam struct { - SubDir string BackupLocation *velerov1api.BackupStorageLocation + BackupRepo *velerov1api.BackupRepository } type Provider interface { diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 8ddff57999..994d0a5ca2 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -30,7 +30,7 @@ import ( repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" - "github.com/vmware-tanzu/velero/pkg/util/ownership" + reposervice "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/service" ) type unifiedRepoProvider struct { @@ -49,28 +49,28 @@ var getS3BucketRegion = repoconfig.GetAWSBucketRegion var getAzureStorageDomain = repoconfig.GetAzureStorageDomain type localFuncTable struct { - getRepoPassword func(credentials.SecretStore, RepoParam) (string, error) getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error) } var funcTable = localFuncTable{ - getRepoPassword: getRepoPassword, getStorageVariables: getStorageVariables, getStorageCredentials: getStorageCredentials, } +const ( + repoOpDescFullMaintain = "full maintenance" + repoOpDescQuickMaintain = "quick maintenance" + repoOpDescForget = "forget" +) + // NewUnifiedRepoProvider creates the service provider for Unified Repo -// workPath is the path for Unified Repo to store some local information -// workPath could be empty, if so, the default path will be used func NewUnifiedRepoProvider( credentialGetter credentials.CredentialGetter, - workPath string, log logrus.FieldLogger, ) (Provider, error) { repo := unifiedRepoProvider{ credentialGetter: credentialGetter, - workPath: workPath, log: log, } @@ -89,12 +89,18 @@ func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) e log.Debug("Start to init repo") - repoOption, err := urp.getRepoOption(param) + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithStoreOptions(urp, param), + udmrepo.WithDescription(repoOpDescFullMaintain), + ) + if err != nil { return errors.Wrap(err, "error to get repo options") } - err = urp.repoService.Init(ctx, repoOption, true) + err = urp.repoService.Init(ctx, *repoOption, true) if err != nil { return errors.Wrap(err, "error to init backup repo") } @@ -105,22 +111,124 @@ func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) e } func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoParam) error { - ///TODO + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to connect repo") + + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithStoreOptions(urp, param), + udmrepo.WithDescription(repoOpDescFullMaintain), + ) + + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Init(ctx, *repoOption, false) + if err != nil { + return errors.Wrap(err, "error to connect backup repo") + } + + log.Debug("Connect repo complete") + return nil } func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam) error { - ///TODO + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to prepare repo") + + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithStoreOptions(urp, param), + udmrepo.WithDescription(repoOpDescFullMaintain), + ) + + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Init(ctx, *repoOption, false) + if err == nil { + log.Debug("Repo has already been initialized remotely") + return nil + } + + err = urp.repoService.Init(ctx, *repoOption, true) + if err != nil { + return errors.Wrap(err, "error to init backup repo") + } + + log.Debug("Prepare repo complete") + return nil } func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) error { - ///TODO + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to prune repo") + + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithGenOptions(map[string]string{udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainFull}), + udmrepo.WithDescription(repoOpDescFullMaintain), + ) + + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Maintain(ctx, *repoOption) + if err != nil { + return errors.Wrap(err, "error to prune backup repo") + } + + log.Debug("Prune repo complete") + return nil } func (urp *unifiedRepoProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { - ///TODO + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to prune repo quick") + + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithGenOptions(map[string]string{udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainQuick}), + udmrepo.WithDescription(repoOpDescQuickMaintain), + ) + + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Maintain(ctx, *repoOption) + if err != nil { + return errors.Wrap(err, "error to prune backup repo quick") + } + + log.Debug("Prune repo quick complete") + return nil } @@ -129,60 +237,108 @@ func (urp *unifiedRepoProvider) EnsureUnlockRepo(ctx context.Context, param Repo } func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { - ///TODO + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + "snapshotID": snapshotID, + }) + + log.Debug("Start to forget snapshot") + + repoOption, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(urp, param), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithDescription(repoOpDescForget), + ) + + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + bkRepo, err := urp.repoService.Open(ctx, *repoOption) + if err != nil { + return errors.Wrap(err, "error to open backup repo") + } + + defer func() { + c := bkRepo.Close(ctx) + if c != nil { + log.WithError(c).Error("Failed to close repo") + } + }() + + err = bkRepo.DeleteManifest(ctx, udmrepo.ID(snapshotID)) + if err != nil { + return errors.Wrap(err, "error to delete manifest") + } + + log.Debug("Forget snapshot complete") + return nil } -func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) { - if secretStore == nil { - return "", errors.New("invalid credentials interface") +func (urp *unifiedRepoProvider) GetPassword(param interface{}) (string, error) { + repoParam, ok := param.(RepoParam) + if !ok { + return "", errors.New("invalid parameter") } - buf, err := secretStore.Get(repokey.RepoKeySelector()) + repoPassword, err := getRepoPassword(urp.credentialGetter.FromSecret, repoParam) if err != nil { - return "", errors.Wrap(err, "error to get password buffer") + return "", errors.Wrap(err, "error to get repo password") } - return strings.TrimSpace(string(buf)), nil + return repoPassword, nil } -func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) { - repoPassword, err := funcTable.getRepoPassword(urp.credentialGetter.FromSecret, param) - if err != nil { - return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo password") +func (urp *unifiedRepoProvider) GetStoreType(param interface{}) (string, error) { + repoParam, ok := param.(RepoParam) + if !ok { + return "", errors.New("invalid parameter") } - storeVar, err := funcTable.getStorageVariables(param.BackupLocation, param.SubDir) - if err != nil { - return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get storage variables") + return getStorageType(repoParam.BackupLocation), nil +} + +func (urp *unifiedRepoProvider) GetStoreOptions(param interface{}) (map[string]string, error) { + repoParam, ok := param.(RepoParam) + if !ok { + return map[string]string{}, errors.New("invalid parameter") } - storeCred, err := funcTable.getStorageCredentials(param.BackupLocation, urp.credentialGetter.FromFile) + storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, repoParam.BackupRepo.Spec.VolumeNamespace) if err != nil { - return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo credentials") + return map[string]string{}, errors.Wrap(err, "error to get storage variables") } - repoOption := udmrepo.RepoOptions{ - StorageType: getStorageType(param.BackupLocation), - RepoPassword: repoPassword, - ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)), - Ownership: udmrepo.OwnershipOptions{ - Username: ownership.GetRepositoryOwner().Username, - DomainName: ownership.GetRepositoryOwner().DomainName, - }, - StorageOptions: make(map[string]string), - GeneralOptions: make(map[string]string), + storeCred, err := funcTable.getStorageCredentials(repoParam.BackupLocation, urp.credentialGetter.FromFile) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error to get repo credentials") } + storeOptions := make(map[string]string) for k, v := range storeVar { - repoOption.StorageOptions[k] = v + storeOptions[k] = v } for k, v := range storeCred { - repoOption.StorageOptions[k] = v + storeOptions[k] = v } - return repoOption, nil + return storeOptions, nil +} + +func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) { + if secretStore == nil { + return "", errors.New("invalid credentials interface") + } + + rawPass, err := secretStore.Get(repokey.RepoKeySelector()) + if err != nil { + return "", errors.Wrap(err, "error to get password") + } + + return strings.TrimSpace(rawPass), nil } func getStorageType(backupLocation *velerov1api.BackupStorageLocation) string { @@ -304,12 +460,6 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo return result, nil } -func getRepoConfigFile(workPath string, repoID string) string { - ///TODO: call udmrepo to get config file - return "" -} - func createRepoService(log logrus.FieldLogger) udmrepo.BackupRepoService { - ///TODO: call udmrepo create repo service - return nil + return reposervice.Create(log) } diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index f2cccb8e5d..8e41b9b41b 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "errors" "testing" @@ -31,6 +32,8 @@ import ( credmock "github.com/vmware-tanzu/velero/internal/credentials/mocks" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + reposervicenmocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/mocks" + velerotest "github.com/vmware-tanzu/velero/pkg/test" ) func TestGetStorageCredentials(t *testing.T) { @@ -451,7 +454,7 @@ func TestGetRepoPassword(t *testing.T) { name: "error from secret interface", getter: new(credmock.SecretStore), credStoreError: errors.New("fake error"), - expectedErr: "error to get password buffer: fake error", + expectedErr: "error to get password: fake error", }, { name: "secret with whitespace", @@ -488,43 +491,41 @@ func TestGetRepoPassword(t *testing.T) { } } -func TestGetRepoOption(t *testing.T) { +func TestGetStoreOptions(t *testing.T) { testCases := []struct { - name string - funcTable localFuncTable - getRepoPassword func(velerocredentials.SecretStore, RepoParam) (string, error) - getStorageCredentials func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) - getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) - expected udmrepo.RepoOptions - expectedErr string + name string + funcTable localFuncTable + repoParam interface{} + expected map[string]string + expectedErr string }{ { - name: "get repo password fail", - funcTable: localFuncTable{ - getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { - return "", errors.New("fake-error-1") - }, - }, - expectedErr: "error to get repo password: fake-error-1", + name: "wrong param type", + repoParam: struct{}{}, + expected: map[string]string{}, + expectedErr: "invalid parameter", }, { name: "get storage variable fail", + repoParam: RepoParam{ + BackupLocation: &velerov1api.BackupStorageLocation{}, + BackupRepo: &velerov1api.BackupRepository{}, + }, funcTable: localFuncTable{ - getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { - return "fake-password", nil - }, getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { return map[string]string{}, errors.New("fake-error-2") }, }, + expected: map[string]string{}, expectedErr: "error to get storage variables: fake-error-2", }, { name: "get storage credentials fail", + repoParam: RepoParam{ + BackupLocation: &velerov1api.BackupStorageLocation{}, + BackupRepo: &velerov1api.BackupRepository{}, + }, funcTable: localFuncTable{ - getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { - return "fake-password", nil - }, getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { return map[string]string{}, nil }, @@ -532,6 +533,7 @@ func TestGetRepoOption(t *testing.T) { return map[string]string{}, errors.New("fake-error-3") }, }, + expected: map[string]string{}, expectedErr: "error to get repo credentials: fake-error-3", }, } @@ -539,11 +541,241 @@ func TestGetRepoOption(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { funcTable = tc.funcTable + urp := unifiedRepoProvider{} - password, err := urp.getRepoOption(RepoParam{}) + options, err := urp.GetStoreOptions(tc.repoParam) - require.Equal(t, tc.expected, password) + require.Equal(t, tc.expected, options) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestPrepareRepo(t *testing.T) { + testCases := []struct { + name string + funcTable localFuncTable + getter *credmock.SecretStore + repoService *reposervicenmocks.BackupRepoService + retFuncInit func(context.Context, udmrepo.RepoOptions, bool) error + credStoreReturn string + credStoreError error + expectedErr string + }{ + { + name: "get repo option fail", + repoService: new(reposervicenmocks.BackupRepoService), + expectedErr: "error to get repo options: error to get repo password: invalid credentials interface", + }, + { + name: "get repo option fail, get password fail", + getter: new(credmock.SecretStore), + repoService: new(reposervicenmocks.BackupRepoService), + credStoreError: errors.New("fake-password-error"), + expectedErr: "error to get repo options: error to get repo password: error to get password: fake-password-error", + }, + { + name: "get repo option fail, get store options fail", + getter: new(credmock.SecretStore), + repoService: new(reposervicenmocks.BackupRepoService), + credStoreReturn: "fake-password", + funcTable: localFuncTable{ + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, errors.New("fake-store-option-error") + }, + }, + expectedErr: "error to get repo options: error to get storage variables: fake-store-option-error", + }, + { + name: "already initialized", + getter: new(credmock.SecretStore), + credStoreReturn: "fake-password", + funcTable: localFuncTable{ + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, nil + }, + }, + repoService: new(reposervicenmocks.BackupRepoService), + retFuncInit: func(ctx context.Context, repoOption udmrepo.RepoOptions, createNew bool) error { + if !createNew { + return nil + } else { + return errors.New("fake-error") + } + }, + }, + { + name: "initialize fail", + getter: new(credmock.SecretStore), + credStoreReturn: "fake-password", + funcTable: localFuncTable{ + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, nil + }, + }, + repoService: new(reposervicenmocks.BackupRepoService), + retFuncInit: func(ctx context.Context, repoOption udmrepo.RepoOptions, createNew bool) error { + if !createNew { + return errors.New("fake-error-1") + } else { + return errors.New("fake-error-2") + } + }, + expectedErr: "error to init backup repo: fake-error-2", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + funcTable = tc.funcTable + + var secretStore velerocredentials.SecretStore + if tc.getter != nil { + tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError) + secretStore = tc.getter + } + + urp := unifiedRepoProvider{ + credentialGetter: velerocredentials.CredentialGetter{ + FromSecret: secretStore, + }, + repoService: tc.repoService, + log: velerotest.NewLogger(), + } + + tc.repoService.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(tc.retFuncInit) + + err := urp.PrepareRepo(context.Background(), RepoParam{ + BackupLocation: &velerov1api.BackupStorageLocation{}, + BackupRepo: &velerov1api.BackupRepository{}, + }) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestForget(t *testing.T) { + var backupRepo *reposervicenmocks.BackupRepo + + testCases := []struct { + name string + funcTable localFuncTable + getter *credmock.SecretStore + repoService *reposervicenmocks.BackupRepoService + backupRepo *reposervicenmocks.BackupRepo + retFuncOpen []interface{} + retFuncDelete interface{} + credStoreReturn string + credStoreError error + expectedErr string + }{ + { + name: "get repo option fail", + expectedErr: "error to get repo options: error to get repo password: invalid credentials interface", + }, + { + name: "repo open fail", + getter: new(credmock.SecretStore), + credStoreReturn: "fake-password", + funcTable: localFuncTable{ + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, nil + }, + }, + repoService: new(reposervicenmocks.BackupRepoService), + retFuncOpen: []interface{}{ + func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo { + return backupRepo + }, + + func(context.Context, udmrepo.RepoOptions) error { + return errors.New("fake-error-2") + }, + }, + expectedErr: "error to open backup repo: fake-error-2", + }, + { + name: "delete fail", + getter: new(credmock.SecretStore), + credStoreReturn: "fake-password", + funcTable: localFuncTable{ + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, nil + }, + }, + repoService: new(reposervicenmocks.BackupRepoService), + backupRepo: new(reposervicenmocks.BackupRepo), + retFuncOpen: []interface{}{ + func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo { + return backupRepo + }, + + func(context.Context, udmrepo.RepoOptions) error { + return nil + }, + }, + retFuncDelete: func(context.Context, udmrepo.ID) error { + return errors.New("fake-error-3") + }, + expectedErr: "error to delete manifest: fake-error-3", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + funcTable = tc.funcTable + + var secretStore velerocredentials.SecretStore + if tc.getter != nil { + tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError) + secretStore = tc.getter + } + + urp := unifiedRepoProvider{ + credentialGetter: velerocredentials.CredentialGetter{ + FromSecret: secretStore, + }, + repoService: tc.repoService, + log: velerotest.NewLogger(), + } + + backupRepo = tc.backupRepo + + if tc.repoService != nil { + tc.repoService.On("Open", mock.Anything, mock.Anything).Return(tc.retFuncOpen[0], tc.retFuncOpen[1]) + } + + if tc.backupRepo != nil { + backupRepo.On("DeleteManifest", mock.Anything, mock.Anything).Return(tc.retFuncDelete) + backupRepo.On("Close", mock.Anything).Return(nil) + } + + err := urp.Forget(context.Background(), "", RepoParam{ + BackupLocation: &velerov1api.BackupStorageLocation{}, + }) if tc.expectedErr == "" { assert.NoError(t, err) diff --git a/pkg/repository/udmrepo/mocks/BackupRepo.go b/pkg/repository/udmrepo/mocks/BackupRepo.go new file mode 100644 index 0000000000..ea8e2ba3c4 --- /dev/null +++ b/pkg/repository/udmrepo/mocks/BackupRepo.go @@ -0,0 +1,185 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + time "time" + + mock "github.com/stretchr/testify/mock" + + udmrepo "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +// BackupRepo is an autogenerated mock type for the BackupRepo type +type BackupRepo struct { + mock.Mock +} + +// Close provides a mock function with given fields: ctx +func (_m *BackupRepo) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteManifest provides a mock function with given fields: ctx, id +func (_m *BackupRepo) DeleteManifest(ctx context.Context, id udmrepo.ID) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.ID) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// FindManifests provides a mock function with given fields: ctx, filter +func (_m *BackupRepo) FindManifests(ctx context.Context, filter udmrepo.ManifestFilter) ([]*udmrepo.ManifestEntryMetadata, error) { + ret := _m.Called(ctx, filter) + + var r0 []*udmrepo.ManifestEntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.ManifestFilter) []*udmrepo.ManifestEntryMetadata); ok { + r0 = rf(ctx, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*udmrepo.ManifestEntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, udmrepo.ManifestFilter) error); ok { + r1 = rf(ctx, filter) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Flush provides a mock function with given fields: ctx +func (_m *BackupRepo) Flush(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetManifest provides a mock function with given fields: ctx, id, mani +func (_m *BackupRepo) GetManifest(ctx context.Context, id udmrepo.ID, mani *udmrepo.RepoManifest) error { + ret := _m.Called(ctx, id, mani) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.ID, *udmrepo.RepoManifest) error); ok { + r0 = rf(ctx, id, mani) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewObjectWriter provides a mock function with given fields: ctx, opt +func (_m *BackupRepo) NewObjectWriter(ctx context.Context, opt udmrepo.ObjectWriteOptions) udmrepo.ObjectWriter { + ret := _m.Called(ctx, opt) + + var r0 udmrepo.ObjectWriter + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.ObjectWriteOptions) udmrepo.ObjectWriter); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(udmrepo.ObjectWriter) + } + } + + return r0 +} + +// OpenObject provides a mock function with given fields: ctx, id +func (_m *BackupRepo) OpenObject(ctx context.Context, id udmrepo.ID) (udmrepo.ObjectReader, error) { + ret := _m.Called(ctx, id) + + var r0 udmrepo.ObjectReader + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.ID) udmrepo.ObjectReader); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(udmrepo.ObjectReader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, udmrepo.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PutManifest provides a mock function with given fields: ctx, mani +func (_m *BackupRepo) PutManifest(ctx context.Context, mani udmrepo.RepoManifest) (udmrepo.ID, error) { + ret := _m.Called(ctx, mani) + + var r0 udmrepo.ID + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.RepoManifest) udmrepo.ID); ok { + r0 = rf(ctx, mani) + } else { + r0 = ret.Get(0).(udmrepo.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, udmrepo.RepoManifest) error); ok { + r1 = rf(ctx, mani) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Time provides a mock function with given fields: +func (_m *BackupRepo) Time() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +type mockConstructorTestingTNewBackupRepo interface { + mock.TestingT + Cleanup(func()) +} + +// NewBackupRepo creates a new instance of BackupRepo. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewBackupRepo(t mockConstructorTestingTNewBackupRepo) *BackupRepo { + mock := &BackupRepo{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/mocks/BackupRepoService.go b/pkg/repository/udmrepo/mocks/BackupRepoService.go new file mode 100644 index 0000000000..135c0058c4 --- /dev/null +++ b/pkg/repository/udmrepo/mocks/BackupRepoService.go @@ -0,0 +1,81 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + udmrepo "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +// BackupRepoService is an autogenerated mock type for the BackupRepoService type +type BackupRepoService struct { + mock.Mock +} + +// Init provides a mock function with given fields: ctx, repoOption, createNew +func (_m *BackupRepoService) Init(ctx context.Context, repoOption udmrepo.RepoOptions, createNew bool) error { + ret := _m.Called(ctx, repoOption, createNew) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.RepoOptions, bool) error); ok { + r0 = rf(ctx, repoOption, createNew) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Maintain provides a mock function with given fields: ctx, repoOption +func (_m *BackupRepoService) Maintain(ctx context.Context, repoOption udmrepo.RepoOptions) error { + ret := _m.Called(ctx, repoOption) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.RepoOptions) error); ok { + r0 = rf(ctx, repoOption) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Open provides a mock function with given fields: ctx, repoOption +func (_m *BackupRepoService) Open(ctx context.Context, repoOption udmrepo.RepoOptions) (udmrepo.BackupRepo, error) { + ret := _m.Called(ctx, repoOption) + + var r0 udmrepo.BackupRepo + if rf, ok := ret.Get(0).(func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo); ok { + r0 = rf(ctx, repoOption) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(udmrepo.BackupRepo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, udmrepo.RepoOptions) error); ok { + r1 = rf(ctx, repoOption) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewBackupRepoService interface { + mock.TestingT + Cleanup(func()) +} + +// NewBackupRepoService creates a new instance of BackupRepoService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewBackupRepoService(t mockConstructorTestingTNewBackupRepoService) *BackupRepoService { + mock := &BackupRepoService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/mocks/ObjectReader.go b/pkg/repository/udmrepo/mocks/ObjectReader.go new file mode 100644 index 0000000000..2410acd6e6 --- /dev/null +++ b/pkg/repository/udmrepo/mocks/ObjectReader.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// ObjectReader is an autogenerated mock type for the ObjectReader type +type ObjectReader struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *ObjectReader) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Length provides a mock function with given fields: +func (_m *ObjectReader) Length() int64 { + ret := _m.Called() + + var r0 int64 + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + return r0 +} + +// Read provides a mock function with given fields: p +func (_m *ObjectReader) Read(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Seek provides a mock function with given fields: offset, whence +func (_m *ObjectReader) Seek(offset int64, whence int) (int64, error) { + ret := _m.Called(offset, whence) + + var r0 int64 + if rf, ok := ret.Get(0).(func(int64, int) int64); ok { + r0 = rf(offset, whence) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(int64, int) error); ok { + r1 = rf(offset, whence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewObjectReader interface { + mock.TestingT + Cleanup(func()) +} + +// NewObjectReader creates a new instance of ObjectReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewObjectReader(t mockConstructorTestingTNewObjectReader) *ObjectReader { + mock := &ObjectReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/mocks/ObjectWriter.go b/pkg/repository/udmrepo/mocks/ObjectWriter.go new file mode 100644 index 0000000000..277a0ed4a3 --- /dev/null +++ b/pkg/repository/udmrepo/mocks/ObjectWriter.go @@ -0,0 +1,126 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + udmrepo "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +// ObjectWriter is an autogenerated mock type for the ObjectWriter type +type ObjectWriter struct { + mock.Mock +} + +// Checkpoint provides a mock function with given fields: +func (_m *ObjectWriter) Checkpoint() (udmrepo.ID, error) { + ret := _m.Called() + + var r0 udmrepo.ID + if rf, ok := ret.Get(0).(func() udmrepo.ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(udmrepo.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *ObjectWriter) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Result provides a mock function with given fields: +func (_m *ObjectWriter) Result() (udmrepo.ID, error) { + ret := _m.Called() + + var r0 udmrepo.ID + if rf, ok := ret.Get(0).(func() udmrepo.ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(udmrepo.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Seek provides a mock function with given fields: offset, whence +func (_m *ObjectWriter) Seek(offset int64, whence int) (int64, error) { + ret := _m.Called(offset, whence) + + var r0 int64 + if rf, ok := ret.Get(0).(func(int64, int) int64); ok { + r0 = rf(offset, whence) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(int64, int) error); ok { + r1 = rf(offset, whence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Write provides a mock function with given fields: p +func (_m *ObjectWriter) Write(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewObjectWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewObjectWriter creates a new instance of ObjectWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewObjectWriter(t mockConstructorTestingTNewObjectWriter) *ObjectWriter { + mock := &ObjectWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/repo-option-consts.go b/pkg/repository/udmrepo/repo-option-consts.go deleted file mode 100644 index 7cf55d017c..0000000000 --- a/pkg/repository/udmrepo/repo-option-consts.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package udmrepo - -const ( - StorageTypeS3 = "s3" - StorageTypeAzure = "azure" - StorageTypeFs = "filesystem" - StorageTypeGcs = "gcs" - - GenOptionMaintainMode = "mode" - GenOptionMaintainFull = "full" - GenOptionMaintainQuick = "quick" - - StoreOptionS3KeyId = "accessKeyID" - StoreOptionS3Provider = "providerName" - StoreOptionS3SecretKey = "secretAccessKey" - StoreOptionS3Token = "sessionToken" - StoreOptionS3Endpoint = "endpoint" - StoreOptionS3DisableTls = "doNotUseTLS" - StoreOptionS3DisableTlsVerify = "skipTLSVerify" - - StoreOptionAzureKey = "storageKey" - StoreOptionAzureDomain = "storageDomain" - StoreOptionAzureStorageAccount = "storageAccount" - StoreOptionAzureToken = "sasToken" - - StoreOptionFsPath = "fspath" - - StoreOptionGcsReadonly = "readonly" - - StoreOptionOssBucket = "bucket" - StoreOptionOssRegion = "region" - - StoreOptionCredentialFile = "credFile" - StoreOptionPrefix = "prefix" - StoreOptionPrefixName = "unified-repo" - - ThrottleOptionReadOps = "readOPS" - ThrottleOptionWriteOps = "writeOPS" - ThrottleOptionListOps = "listOPS" - ThrottleOptionUploadBytes = "uploadBytes" - ThrottleOptionDownloadBytes = "downloadBytes" -) diff --git a/pkg/repository/udmrepo/repo-options.go b/pkg/repository/udmrepo/repo-options.go new file mode 100644 index 0000000000..b5fd48cf79 --- /dev/null +++ b/pkg/repository/udmrepo/repo-options.go @@ -0,0 +1,171 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package udmrepo + +import ( + "os" + "path/filepath" + "strings" +) + +const ( + StorageTypeS3 = "s3" + StorageTypeAzure = "azure" + StorageTypeFs = "filesystem" + StorageTypeGcs = "gcs" + + GenOptionMaintainMode = "mode" + GenOptionMaintainFull = "full" + GenOptionMaintainQuick = "quick" + + StoreOptionS3KeyId = "accessKeyID" + StoreOptionS3Provider = "providerName" + StoreOptionS3SecretKey = "secretAccessKey" + StoreOptionS3Token = "sessionToken" + StoreOptionS3Endpoint = "endpoint" + StoreOptionS3DisableTls = "doNotUseTLS" + StoreOptionS3DisableTlsVerify = "skipTLSVerify" + + StoreOptionAzureKey = "storageKey" + StoreOptionAzureDomain = "storageDomain" + StoreOptionAzureStorageAccount = "storageAccount" + StoreOptionAzureToken = "sasToken" + + StoreOptionFsPath = "fspath" + + StoreOptionGcsReadonly = "readonly" + + StoreOptionOssBucket = "bucket" + StoreOptionOssRegion = "region" + + StoreOptionCredentialFile = "credFile" + StoreOptionPrefix = "prefix" + StoreOptionPrefixName = "unified-repo" + + ThrottleOptionReadOps = "readOPS" + ThrottleOptionWriteOps = "writeOPS" + ThrottleOptionListOps = "listOPS" + ThrottleOptionUploadBytes = "uploadBytes" + ThrottleOptionDownloadBytes = "downloadBytes" +) + +type RepoOptions struct { + // StorageType is a repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + // RepoPassword is the backup repository's password, if any + RepoPassword string + // ConfigFilePath is a custom path to save the repository's configuration, if any + ConfigFilePath string + // GeneralOptions takes other repository specific options + GeneralOptions map[string]string + // StorageOptions takes storage specific options + StorageOptions map[string]string + + // Description is a description of the backup repository/backup repository operation. + // It is for logging/debugging purpose only and doesn't control any behavior of the backup repository. + Description string +} + +type PasswordGetter interface { + GetPassword(param interface{}) (string, error) +} + +type StoreOptionsGetter interface { + GetStoreType(param interface{}) (string, error) + GetStoreOptions(param interface{}) (map[string]string, error) +} + +func NewRepoOptions(options ...func(*RepoOptions) error) (*RepoOptions, error) { + ro := &RepoOptions{} + for _, o := range options { + err := o(ro) + if err != nil { + return nil, err + } + } + + return ro, nil +} + +func WithPassword(getter PasswordGetter, param interface{}) func(*RepoOptions) error { + return func(ro *RepoOptions) error { + password, err := getter.GetPassword(param) + if err != nil { + return err + } + + ro.RepoPassword = password + + return nil + } +} + +func WithConfigFile(workPath string, repoID string) func(*RepoOptions) error { + return func(ro *RepoOptions) error { + ro.ConfigFilePath = getRepoConfigFile(workPath, repoID) + return nil + } +} + +func WithGenOptions(genOptions map[string]string) func(*RepoOptions) error { + return func(ro *RepoOptions) error { + for k, v := range genOptions { + ro.GeneralOptions[k] = v + } + + return nil + } +} + +func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOptions) error { + return func(ro *RepoOptions) error { + storeType, err := getter.GetStoreType(param) + if err != nil { + return err + } + + storeOptions, err := getter.GetStoreOptions(param) + if err != nil { + return err + } + + ro.StorageType = storeType + + for k, v := range storeOptions { + ro.StorageOptions[k] = v + } + + return nil + } +} + +func WithDescription(desc string) func(*RepoOptions) error { + return func(ro *RepoOptions) error { + ro.Description = desc + return nil + } +} + +func getRepoConfigFile(workPath string, repoID string) string { + if workPath == "" { + workPath = filepath.Join(os.Getenv("HOME"), "udmrepo") + } + + name := "repo-" + strings.ToLower(repoID) + ".conf" + + return filepath.Join(workPath, name) +} diff --git a/pkg/repository/udmrepo/repo.go b/pkg/repository/udmrepo/repo.go index be18a6d176..01d434fdad 100644 --- a/pkg/repository/udmrepo/repo.go +++ b/pkg/repository/udmrepo/repo.go @@ -70,79 +70,51 @@ type ObjectWriteOptions struct { BackupMode int // OBJECT_DATA_BACKUP_* } -// OwnershipOptions is used to add some access control to the unified repository. -// For example, some privileged operations of the unified repository can be done by the -// repository owner only; the data of a backup may be manipulated by the backup owner -// who created it only. It is optional for a backup repository to support this ownership control. -type OwnershipOptions struct { - Username string - DomainName string - FullQualified string -} - -type RepoOptions struct { - // A repository specific string to identify a backup storage, i.e., "s3", "filesystem" - StorageType string - // Backup repository password, if any - RepoPassword string - // A custom path to save the repository's configuration, if any - ConfigFilePath string - // The ownership for the current repository operation - Ownership OwnershipOptions - // Other repository specific options - GeneralOptions map[string]string - // Storage specific options - StorageOptions map[string]string - - // Description of the backup repository - Description string -} - // BackupRepoService is used to initialize, open or maintain a backup repository type BackupRepoService interface { - // Create a backup repository or connect to an existing backup repository. + // Init creates a backup repository or connect to an existing backup repository. // repoOption: option to the backup repository and the underlying backup storage. // createNew: indicates whether to create a new or connect to an existing backup repository. Init(ctx context.Context, repoOption RepoOptions, createNew bool) error - // Open an backup repository that has been created/connected. + // Open opens an backup repository that has been created/connected. // repoOption: options to open the backup repository and the underlying storage. Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) - // Periodically called to maintain the backup repository to eliminate redundant data and improve performance. + // Maintain is periodically called to maintain the backup repository to eliminate redundant data. // repoOption: options to maintain the backup repository. Maintain(ctx context.Context, repoOption RepoOptions) error } // BackupRepo provides the access to the backup repository type BackupRepo interface { - // Open an existing object for read. + // OpenObject opens an existing object for read. // id: the object's unified identifier. OpenObject(ctx context.Context, id ID) (ObjectReader, error) - // Get a manifest data. + // GetManifest gets a manifest data from the backup repository. GetManifest(ctx context.Context, id ID, mani *RepoManifest) error - // Get one or more manifest data that match the given labels + // FindManifests gets one or more manifest data that match the given labels FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) - // Create a new object and return the object's writer interface. + // NewObjectWriter creates a new object and return the object's writer interface. // return: A unified identifier of the object on success. NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter - // Save a manifest object + // PutManifest saves a manifest object into the backup repository. PutManifest(ctx context.Context, mani RepoManifest) (ID, error) - // Delete a manifest object + // DeleteManifest deletes a manifest object from the backup repository. DeleteManifest(ctx context.Context, id ID) error - // Flush all the backup repository data + // Flush flushes all the backup repository data Flush(ctx context.Context) error - // Get the local time of the backup repository. It may be different from the time of the caller + // Time returns the local time of the backup repository. It may be different from the time of the caller Time() time.Time - // Close the backup repository + // Close closes the backup repository Close(ctx context.Context) error } @@ -157,15 +129,15 @@ type ObjectReader interface { type ObjectWriter interface { io.WriteCloser - // For some cases, i.e. block incremental, the object is not written sequentially + // Seeker is used in the cases that the object is not written sequentially io.Seeker - // Periodically called to preserve the state of data written to the repo so far. - // Return a unified identifier that represent the current state. + // Checkpoint is periodically called to preserve the state of data written to the repo so far. + // Checkpoint returns a unified identifier that represent the current state. // An empty ID could be returned on success if the backup repository doesn't support this. Checkpoint() (ID, error) - // Wait for the completion of the object write. + // Result waits for the completion of the object write. // Result returns the object's unified identifier after the write completes. Result() (ID, error) } diff --git a/pkg/repository/udmrepo/service/service.go b/pkg/repository/udmrepo/service/service.go new file mode 100644 index 0000000000..55fbb03c8f --- /dev/null +++ b/pkg/repository/udmrepo/service/service.go @@ -0,0 +1,37 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +const ( + defaultUsername = "default" + defaultDomain = "default" +) + +func Create(logger logrus.FieldLogger) udmrepo.BackupRepoService { + ///TODO: create from kopiaLib + return nil +} + +func GetRepoUser() (username, domain string) { + return defaultUsername, defaultDomain +} diff --git a/pkg/util/ownership/backup_owner.go b/pkg/util/ownership/backup_owner.go deleted file mode 100644 index 078c799dd9..0000000000 --- a/pkg/util/ownership/backup_owner.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ownership - -import "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" - -const ( - defaultOwnerUsername = "default" - defaultOwnerDomain = "default" -) - -// GetBackupOwner returns the owner used by uploaders when saving a snapshot or -// opening the unified repository. At present, use the default owner only -func GetBackupOwner() udmrepo.OwnershipOptions { - return udmrepo.OwnershipOptions{ - Username: defaultOwnerUsername, - DomainName: defaultOwnerDomain, - } -} - -// GetBackupOwner returns the owner used to create/connect the unified repository. -//At present, use the default owner only -func GetRepositoryOwner() udmrepo.OwnershipOptions { - return udmrepo.OwnershipOptions{ - Username: defaultOwnerUsername, - DomainName: defaultOwnerDomain, - } -} From 49e151739f9c65b85e4fa39a2be971effdd06961 Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 1 Aug 2022 09:00:32 +0000 Subject: [PATCH 248/366] Kopia Uploader integration on shim progress uploader Signed-off-by: Ming --- changelogs/unreleased/5163-qiuming-best | 1 + go.mod | 73 +++-- go.sum | 379 +++++++++++++++++++++--- pkg/uploader/kopia/progress.go | 151 ++++++++++ pkg/uploader/kopia/shim.go | 255 ++++++++++++++++ pkg/uploader/provider/provider.go | 44 +++ pkg/uploader/types.go | 29 ++ 7 files changed, 866 insertions(+), 66 deletions(-) create mode 100644 changelogs/unreleased/5163-qiuming-best create mode 100644 pkg/uploader/kopia/progress.go create mode 100644 pkg/uploader/kopia/shim.go create mode 100644 pkg/uploader/provider/provider.go create mode 100644 pkg/uploader/types.go diff --git a/changelogs/unreleased/5163-qiuming-best b/changelogs/unreleased/5163-qiuming-best new file mode 100644 index 0000000000..c9669c7e74 --- /dev/null +++ b/changelogs/unreleased/5163-qiuming-best @@ -0,0 +1 @@ +Kopia uploader integration on shim progress uploader module diff --git a/go.mod b/go.mod index 640e78078d..158ce888ad 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/vmware-tanzu/velero go 1.17 require ( - cloud.google.com/go/storage v1.10.0 + cloud.google.com/go/storage v1.21.0 github.com/Azure/azure-pipeline-go v0.2.3 github.com/Azure/azure-sdk-for-go v61.4.0+incompatible github.com/Azure/azure-storage-blob-go v0.14.0 @@ -11,36 +11,37 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.3.0 github.com/apex/log v1.9.0 - github.com/aws/aws-sdk-go v1.28.2 + github.com/aws/aws-sdk-go v1.43.31 github.com/bombsimon/logrusr v1.1.0 github.com/evanphx/json-patch v4.11.0+incompatible github.com/fatih/color v1.13.0 github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.6 - github.com/google/uuid v1.2.0 + github.com/google/go-cmp v0.5.7 + github.com/google/uuid v1.3.0 github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-plugin v1.4.3 github.com/joho/godotenv v1.3.0 + github.com/kopia/kopia v0.10.7 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.16.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_golang v1.12.1 github.com/robfig/cron v1.1.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/afero v1.6.0 github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/vmware-tanzu/crash-diagnostics v0.3.7 - golang.org/x/mod v0.4.2 - golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 - golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f + golang.org/x/mod v0.5.1 + golang.org/x/net v0.0.0-20220325170049-de3da57026de + golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/api v0.56.0 - google.golang.org/grpc v1.40.0 + google.golang.org/api v0.74.0 + google.golang.org/grpc v1.45.0 k8s.io/api v0.22.2 k8s.io/apiextensions-apiserver v0.22.2 k8s.io/apimachinery v0.22.2 @@ -53,34 +54,42 @@ require ( ) require ( - cloud.google.com/go v0.93.3 // indirect + cloud.google.com/go v0.100.2 // indirect + cloud.google.com/go/compute v1.5.0 // indirect + cloud.google.com/go/iam v0.1.1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.14 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.4.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.1.0 // indirect + github.com/googleapis/gax-go/v2 v2.2.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.15.1 // indirect + github.com/klauspost/cpuid/v2 v2.0.12 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect @@ -88,30 +97,34 @@ require ( github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/natefinch/atomic v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/stretchr/objx v0.2.0 // indirect github.com/vladimirvivien/gexe v0.1.1 // indirect + github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.23.0 // indirect go.starlark.net v0.0.0-20201006213952-227f4aabceb5 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.19.0 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee // indirect + golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index eccd53764e..58f63b5f24 100644 --- a/go.sum +++ b/go.sum @@ -23,17 +23,29 @@ cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAV cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3 h1:wPBktZFzYBcCZVARvwVKqH1uEj+aLXofJEtrb4oOsio= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= +cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -42,13 +54,18 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14= +cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v61.4.0+incompatible h1:BF2Pm3aQWIa6q9KmxyF1JYKYXtVw67vtvu2Wd54NGuY= github.com/Azure/azure-sdk-for-go v61.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -67,8 +84,9 @@ github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMl github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc= +github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= @@ -95,6 +113,9 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -102,12 +123,21 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/kingpin v0.0.0-20200323085623-b6657d9477a6/go.mod h1:b6br6/pDFSfMkBgC96TbpOji05q5pa+v5rIlS0Y6XtI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= @@ -117,10 +147,14 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.28.2 h1:j5IXG9CdyLfcVfICqo1PXVv+rua+QQHbkXuvuU/JF+8= -github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= +github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -136,25 +170,39 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bombsimon/logrusr v1.1.0 h1:Y03FI4Z/Shyrc9jF26vuaUbnPxC5NMJnTtJA/3Lihq8= github.com/bombsimon/logrusr v1.1.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo= +github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0= +github.com/chromedp/cdproto v0.0.0-20220321060548-7bc2623472b3/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= +github.com/chromedp/chromedp v0.8.0/go.mod h1:odCVV9o9i7HUKwHMFz9Y7T6s4Kbcz4GOyPlwKWopI9Q= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -166,10 +214,14 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -178,14 +230,22 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -193,6 +253,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -202,9 +263,13 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/foomo/htpasswd v0.0.0-20200116085101-e3a90e78da9c/go.mod h1:SHawtolbB0ZOFoRWgDwakX5WpwuIWAK88bUXVZqK0Ss= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8= +github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= @@ -218,6 +283,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -242,16 +308,27 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -286,10 +363,12 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/fswalker v0.2.1-0.20200214223026-f0e929ba4126/go.mod h1:ZSEBqY0IHKqWPeAbTyvccv9bb9vCnaQfHe31cm911Ng= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -301,8 +380,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -327,22 +407,30 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/readahead v0.0.0-20161222183148-eaceba169032/go.mod h1:qYysrqQXuV4tzsizt4oOQ6mrBZQ0xnQXP3ylXX8Jk5Y= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -354,8 +442,11 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hanwen/go-fuse/v2 v2.1.1-0.20220112183258-f57e95bda82d/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= @@ -370,9 +461,12 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -381,6 +475,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -389,11 +484,15 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -403,9 +502,11 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgb github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -413,15 +514,31 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= +github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= +github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kopia/htmluibuild v0.0.0-20220326183613-bbc499ed4dad/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw= +github.com/kopia/kopia v0.10.7 h1:6s0ZIZW3Ge2ozzefddASy7CIUadp/5tF9yCDKQfAKKI= +github.com/kopia/kopia v0.10.7/go.mod h1:0d9THPD+jwomPcXvPbCdmLyX6phQVP7AqcCcDEajfNA= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -429,8 +546,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -439,12 +561,14 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -461,6 +585,11 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.23/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -481,24 +610,37 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -506,6 +648,7 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -513,52 +656,92 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sanity-io/litter v1.5.4/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -569,6 +752,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= @@ -576,6 +760,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -599,18 +784,25 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/studio-b12/gowebdav v0.0.0-20211106090535-29e74efa701f/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= @@ -623,6 +815,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vladimirvivien/gexe v0.1.1 h1:2A0SBaOSKH+cwLVdt6H+KkHZotZWRNLlWygANGw5DxE= github.com/vladimirvivien/gexe v0.1.1/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= github.com/vmware-tanzu/crash-diagnostics v0.3.7 h1:6gbv/3o1FzyRLS7Dz/+yVg1Lk1oRBQLyI3d1YTtlTT8= @@ -635,10 +828,18 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= +github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= @@ -647,6 +848,8 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -672,36 +875,53 @@ go.starlark.net v0.0.0-20201006213952-227f4aabceb5 h1:ApvY/1gw+Yiqb/FKeks3KnVPWp go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -709,6 +929,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee h1:qlrAyYdKz4o7rWVUjiKqQJMa4PEpd55fqBU8jpsl4Iw= +golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -722,20 +944,23 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -745,6 +970,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -774,6 +1000,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -785,8 +1012,14 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -802,8 +1035,11 @@ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -823,6 +1059,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -844,6 +1081,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -873,6 +1111,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -884,6 +1123,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -893,13 +1133,25 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -918,6 +1170,7 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -937,6 +1190,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -946,6 +1201,8 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -979,7 +1236,6 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -988,6 +1244,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1015,9 +1272,21 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.56.0 h1:08F9XVYTLOGeSQb3xI9C0gXMuQanhdGed0cWFhDozbI= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= +google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7C80= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1031,6 +1300,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1079,14 +1349,39 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 h1:z+ErRPu0+KS02Td3fOAgdX+lnPDh/VyaABEJPD4JRQs= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220211171837-173942840c17/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220216160803-4663080d8bc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1107,8 +1402,11 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1122,8 +1420,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1134,15 +1433,20 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216/go.mod h1:zJ2QpyDCYo1KvLXlmdnFlQAyF/Qfth0fB8239Qg7BIE= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1161,6 +1465,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1168,6 +1473,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.19.12/go.mod h1:EK+KvSq2urA6+CjVdZyAHEphXoLq2K2eW6lxOzTKSaY= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= @@ -1228,3 +1534,4 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/pkg/uploader/kopia/progress.go b/pkg/uploader/kopia/progress.go new file mode 100644 index 0000000000..050d190dc3 --- /dev/null +++ b/pkg/uploader/kopia/progress.go @@ -0,0 +1,151 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopia + +import ( + "sync/atomic" + "time" + + "github.com/vmware-tanzu/velero/pkg/uploader" +) + +//Throttle throttles controlle the interval of output result +type Throttle struct { + throttle int64 + interval time.Duration +} + +func (t *Throttle) ShouldOutput() bool { + nextOutputTimeUnixNano := atomic.LoadInt64(&t.throttle) + if nowNano := time.Now().UnixNano(); nowNano > nextOutputTimeUnixNano { //nolint:forbidigo + if atomic.CompareAndSwapInt64(&t.throttle, nextOutputTimeUnixNano, nowNano+t.interval.Nanoseconds()) { + return true + } + } + return false +} + +func (p *KopiaProgress) InitThrottle(interval time.Duration) { + p.outputThrottle.throttle = 0 + p.outputThrottle.interval = interval +} + +// KopiaProgress represents a backup or restore counters. +type KopiaProgress struct { + // all int64 must precede all int32 due to alignment requirements on ARM + // +checkatomic + uploadedBytes int64 //the total bytes has uploaded + cachedBytes int64 //the total bytes has cached + hashededBytes int64 //the total bytes has hashed + // +checkatomic + uploadedFiles int32 //the total files has ignored + // +checkatomic + ignoredErrorCount int32 //the total errors has ignored + // +checkatomic + fatalErrorCount int32 //the total errors has occurred + estimatedFileCount int32 // +checklocksignore the total count of files to be processed + estimatedTotalBytes int64 // +checklocksignore the total size of files to be processed + // +checkatomic + processedBytes int64 // which statistic all bytes has been processed currently + outputThrottle Throttle // which control the frequency of update progress + UpFunc func(uploader.UploaderProgress) //which called by UpdateProgress func, it is used to update pvb or pvr status +} + +//UploadedBytes the total bytes has uploaded currently +func (p *KopiaProgress) UploadedBytes(numBytes int64) { + atomic.AddInt64(&p.uploadedBytes, numBytes) + atomic.AddInt32(&p.uploadedFiles, 1) + + p.UpdateProgress() +} + +//Error statistic the total Error has occurred +func (p *KopiaProgress) Error(path string, err error, isIgnored bool) { + if isIgnored { + atomic.AddInt32(&p.ignoredErrorCount, 1) + } else { + atomic.AddInt32(&p.fatalErrorCount, 1) + } +} + +//EstimatedDataSize statistic the total size of files to be processed and total files to be processed +func (p *KopiaProgress) EstimatedDataSize(fileCount int, totalBytes int64) { + atomic.StoreInt64(&p.estimatedTotalBytes, totalBytes) + atomic.StoreInt32(&p.estimatedFileCount, int32(fileCount)) + + p.UpdateProgress() +} + +//UpdateProgress which called by UpdateProgress func, it is used to update pvb or pvr status +func (p *KopiaProgress) UpdateProgress() { + if p.outputThrottle.ShouldOutput() { + p.UpFunc(uploader.UploaderProgress{ + TotalBytes: atomic.LoadInt64(&p.estimatedTotalBytes), + BytesDone: atomic.LoadInt64(&p.processedBytes), + }) + } +} + +//UploadStarted statistic the total Error has occurred +func (p *KopiaProgress) UploadStarted() {} + +//CachedFile statistic the total bytes been cached currently +func (p *KopiaProgress) CachedFile(fname string, numBytes int64) { + atomic.AddInt64(&p.cachedBytes, numBytes) + p.UpdateProgress() +} + +//HashedBytes statistic the total bytes been hashed currently +func (p *KopiaProgress) HashedBytes(numBytes int64) { + atomic.AddInt64(&p.processedBytes, numBytes) + atomic.AddInt64(&p.hashededBytes, numBytes) + p.UpdateProgress() +} + +//HashingFile statistic the file been hashed currently +func (p *KopiaProgress) HashingFile(fname string) {} + +//ExcludedFile statistic the file been excluded currently +func (p *KopiaProgress) ExcludedFile(fname string, numBytes int64) {} + +//ExcludedDir statistic the dir been excluded currently +func (p *KopiaProgress) ExcludedDir(dirname string) {} + +//FinishedHashingFile which will called when specific file finished hash +func (p *KopiaProgress) FinishedHashingFile(fname string, numBytes int64) { + p.UpdateProgress() +} + +//StartedDirectory called when begin to upload one directory +func (p *KopiaProgress) StartedDirectory(dirname string) {} + +//FinishedDirectory called when finish to upload one directory +func (p *KopiaProgress) FinishedDirectory(dirname string) { + p.UpdateProgress() +} + +//UploadFinished which report the files flushed after the Upload has completed. +func (p *KopiaProgress) UploadFinished() { + p.UpdateProgress() +} + +//ProgressBytes which statistic all bytes has been processed currently +func (p *KopiaProgress) ProgressBytes(processedBytes int64, totalBytes int64) { + atomic.StoreInt64(&p.processedBytes, processedBytes) + atomic.StoreInt64(&p.estimatedTotalBytes, totalBytes) + p.UpdateProgress() +} diff --git a/pkg/uploader/kopia/shim.go b/pkg/uploader/kopia/shim.go new file mode 100644 index 0000000000..e8e2466d44 --- /dev/null +++ b/pkg/uploader/kopia/shim.go @@ -0,0 +1,255 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopia + +import ( + "context" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/content" + "github.com/kopia/kopia/repo/manifest" + "github.com/kopia/kopia/repo/object" +) + +//shimRepository which is one adapter for unifited repo and kopia. +//it implement kopia RepositoryWriter interfaces +type shimRepository struct { + udmRepo udmrepo.BackupRepo +} + +//shimObjectWriter object writer for unifited repo +type shimObjectWriter struct { + repoWriter udmrepo.ObjectWriter +} + +//shimObjectReader object reader for unifited repo +type shimObjectReader struct { + repoReader udmrepo.ObjectReader +} + +func NewShimRepo(repo udmrepo.BackupRepo) repo.RepositoryWriter { + return &shimRepository{ + udmRepo: repo, + } +} + +//OpenObject open specific object +func (sr *shimRepository) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { + reader, err := sr.udmRepo.OpenObject(ctx, udmrepo.ID(id)) + if err != nil { + return nil, errors.Wrapf(err, "failed to open object with id %v", id) + } + if reader == nil { + return nil, err + } + + return &shimObjectReader{ + repoReader: reader, + }, err +} + +// VerifyObject not supported +func (sr *shimRepository) VerifyObject(ctx context.Context, id object.ID) ([]content.ID, error) { + return nil, errors.New("not supported") +} + +// Get one or more manifest data that match the specific manifest id +func (sr *shimRepository) GetManifest(ctx context.Context, id manifest.ID, payload interface{}) (*manifest.EntryMetadata, error) { + repoMani := udmrepo.RepoManifest{ + Payload: payload, + } + + if err := sr.udmRepo.GetManifest(ctx, udmrepo.ID(id), &repoMani); err != nil { + return nil, errors.Wrapf(err, "failed to get manifest with id %v", id) + } + return GetKopiaManifestEntry(repoMani.Metadata), nil +} + +// Get one or more manifest data that match the given labels +func (sr *shimRepository) FindManifests(ctx context.Context, labels map[string]string) ([]*manifest.EntryMetadata, error) { + if metadata, err := sr.udmRepo.FindManifests(ctx, udmrepo.ManifestFilter{Labels: labels}); err != nil { + return nil, errors.Wrapf(err, "failed to get manifests with labels %v", labels) + } else { + return GetKopiaManifestEntries(metadata), nil + } +} + +//GetKopiaManifestEntries get metadata from specific ManifestEntryMetadata +func GetKopiaManifestEntry(uMani *udmrepo.ManifestEntryMetadata) *manifest.EntryMetadata { + var ret manifest.EntryMetadata + + ret.ID = manifest.ID(uMani.ID) + ret.Labels = uMani.Labels + ret.Length = int(uMani.Length) + ret.ModTime = uMani.ModTime + + return &ret +} + +//GetKopiaManifestEntries get metadata list from specific ManifestEntryMetadata +func GetKopiaManifestEntries(uMani []*udmrepo.ManifestEntryMetadata) []*manifest.EntryMetadata { + var ret []*manifest.EntryMetadata + + for _, entry := range uMani { + var e manifest.EntryMetadata + e.ID = manifest.ID(entry.ID) + e.Labels = entry.Labels + e.Length = int(entry.Length) + e.ModTime = entry.ModTime + + ret = append(ret, &e) + } + + return ret +} + +//Time Get the local time of the unified repo +func (sr *shimRepository) Time() time.Time { + return sr.udmRepo.Time() +} + +//ClientOptions is not supported by unified repo +func (sr *shimRepository) ClientOptions() repo.ClientOptions { + return repo.ClientOptions{} +} + +// Refresh not supported +func (sr *shimRepository) Refresh(ctx context.Context) error { + return errors.New("not supported") +} + +// ContentInfo not supported +func (sr *shimRepository) ContentInfo(ctx context.Context, contentID content.ID) (content.Info, error) { + return nil, errors.New("not supported") +} + +//PrefetchContents is not supported by unified repo +func (sr *shimRepository) PrefetchContents(ctx context.Context, contentIDs []content.ID, hint string) []content.ID { + return nil +} + +//PrefetchObjects is not supported by unified repo +func (sr *shimRepository) PrefetchObjects(ctx context.Context, objectIDs []object.ID, hint string) ([]content.ID, error) { + return nil, errors.New("not supported") +} + +//UpdateDescription is not supported by unified repo +func (sr *shimRepository) UpdateDescription(d string) { +} + +//NewWriter is not supported by unified repo +func (sr *shimRepository) NewWriter(ctx context.Context, option repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error) { + return nil, nil, errors.New("not supported") +} + +//Close will close unified repo +func (sr *shimRepository) Close(ctx context.Context) error { + return sr.udmRepo.Close(ctx) +} + +// NewObjectWriter creates an object writer +func (sr *shimRepository) NewObjectWriter(ctx context.Context, option object.WriterOptions) object.Writer { + var opt udmrepo.ObjectWriteOptions + opt.Description = option.Description + opt.Prefix = udmrepo.ID(option.Prefix) + opt.FullPath = "" + opt.AccessMode = udmrepo.ObjectDataAccessModeFile + + if strings.HasPrefix(option.Description, "DIR:") { + opt.DataType = udmrepo.ObjectDataTypeMetadata + } else { + opt.DataType = udmrepo.ObjectDataTypeData + } + + writer := sr.udmRepo.NewObjectWriter(ctx, opt) + if writer == nil { + return nil + } + + return &shimObjectWriter{ + repoWriter: writer, + } +} + +// PutManifest saves the given manifest payload with a set of labels. +func (sr *shimRepository) PutManifest(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + id, err := sr.udmRepo.PutManifest(ctx, udmrepo.RepoManifest{ + Payload: payload, + Metadata: &udmrepo.ManifestEntryMetadata{ + Labels: labels, + }, + }) + + return manifest.ID(id), err +} + +// DeleteManifest deletes the manifest with a given ID. +func (sr *shimRepository) DeleteManifest(ctx context.Context, id manifest.ID) error { + return sr.udmRepo.DeleteManifest(ctx, udmrepo.ID(id)) +} + +// Flush all the unifited repository data +func (sr *shimRepository) Flush(ctx context.Context) error { + return sr.udmRepo.Flush(ctx) +} + +// Flush all the unifited repository data +func (sr *shimObjectReader) Read(p []byte) (n int, err error) { + return sr.repoReader.Read(p) +} + +func (sr *shimObjectReader) Seek(offset int64, whence int) (int64, error) { + return sr.repoReader.Seek(offset, whence) +} + +//Close current io for ObjectReader +func (sr *shimObjectReader) Close() error { + return sr.repoReader.Close() +} + +// Length returns the logical size of the object +func (sr *shimObjectReader) Length() int64 { + return sr.repoReader.Length() +} + +// Write data +func (sr *shimObjectWriter) Write(p []byte) (n int, err error) { + return sr.repoWriter.Write(p) +} + +// Periodically called to preserve the state of data written to the repo so far. +func (sr *shimObjectWriter) Checkpoint() (object.ID, error) { + id, err := sr.repoWriter.Checkpoint() + return object.ID(id), err +} + +// Result returns the object's unified identifier after the write completes. +func (sr *shimObjectWriter) Result() (object.ID, error) { + id, err := sr.repoWriter.Result() + return object.ID(id), err +} + +// Close closes the repository and releases all resources. +func (sr *shimObjectWriter) Close() error { + return sr.repoWriter.Close() +} diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go new file mode 100644 index 0000000000..5a90a806f1 --- /dev/null +++ b/pkg/uploader/provider/provider.go @@ -0,0 +1,44 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// Provider which is designed for one pod volumn to do the backup or restore +type Provider interface { + // RunBackup which will do backup for one specific volumn and return snapshotID error + // updateFunc which is used for update backup progress into related pvb status + RunBackup( + ctx context.Context, + path string, + tags map[string]string, + parentSnapshot string, + updateFunc func(velerov1api.PodVolumeOperationProgress)) (string, error) + // RunRestore which will do restore for one specific volumn with given snapshot id and return error + // updateFunc which is used for update restore progress into related pvr status + RunRestore( + ctx context.Context, + snapshotID string, + volumePath string, + updateFunc func(velerov1api.PodVolumeOperationProgress)) error + // Close which will close related repository + Close(ctx context.Context) +} diff --git a/pkg/uploader/types.go b/pkg/uploader/types.go new file mode 100644 index 0000000000..27a864c844 --- /dev/null +++ b/pkg/uploader/types.go @@ -0,0 +1,29 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package uploader + +const ( + VeleroBackup string = "backup" + VeleroRestore string = "restore" +) + +type SnapshotInfo struct { + ID string `json:"id"` + Size int64 `json:"Size"` +} + +type UploaderProgress struct { + TotalBytes int64 `json:"totalBytes,omitempty"` + BytesDone int64 `json:"doneBytes,omitempty"` +} From c8544ea2121f394c3d956503972e154560cd309c Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 10 Aug 2022 10:08:53 +0000 Subject: [PATCH 249/366] Add namespace- mappping E2E test Signed-off-by: danfengl --- test/e2e/backups/deletion.go | 2 +- test/e2e/backups/ttl.go | 2 +- test/e2e/basic/namespace-mapping.go | 101 +++++++++++++++++++++++ test/e2e/bsl-mgmt/deletion.go | 2 +- test/e2e/e2e_suite_test.go | 3 + test/e2e/migration/migration.go | 4 +- test/e2e/test/test.go | 2 +- test/e2e/upgrade/upgrade.go | 4 +- test/e2e/util/k8s/namespace.go | 19 ++++- test/e2e/util/kibishii/kibishii_utils.go | 49 +++++++---- 10 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 test/e2e/basic/namespace-mapping.go diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index c597d7f4b6..0663e554ad 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -105,7 +105,7 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa } if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, deletionTest, - registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots); err != nil { + registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", deletionTest) } err := ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, VeleroCfg.BSLPrefix, VeleroCfg.BSLConfig, backupName, BackupObjectsPrefix, 1) diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index bc36c8bd31..78f1915a11 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -95,7 +95,7 @@ func TTLTest() { By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(test.ctx, client, VeleroCfg.CloudProvider, test.testNS, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, - VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + VeleroCfg.KibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData)).To(Succeed()) }) var BackupCfg BackupConfig diff --git a/test/e2e/basic/namespace-mapping.go b/test/e2e/basic/namespace-mapping.go new file mode 100644 index 0000000000..2f72f23440 --- /dev/null +++ b/test/e2e/basic/namespace-mapping.go @@ -0,0 +1,101 @@ +package basic + +import ( + "context" + "fmt" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/test" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/kibishii" +) + +type NamespaceMapping struct { + TestCase + MappedNamespaceList []string + kibishiiData *KibishiiData +} + +var OneNamespaceMappingTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1"}}}) +var MultiNamespacesMappingTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1", "ns2"}}}) + +func (n *NamespaceMapping) Init() error { + n.Client = TestClientInstance + n.kibishiiData = &KibishiiData{2, 10, 10, 1024, 1024, 0, 2} + + n.TestMsg = &TestMSG{ + Desc: "Backup resources with include namespace test", + FailedMSG: "Failed to backup with namespace include", + Text: fmt.Sprintf("should backup namespaces %s", *n.NSIncluded), + } + return nil +} + +func (n *NamespaceMapping) StartRun() error { + var mappedNS string + var mappedNSList []string + + for index, ns := range *n.NSIncluded { + mappedNS = mappedNS + ns + ":" + ns + UUIDgen.String() + mappedNSList = append(mappedNSList, ns+UUIDgen.String()) + if index+1 != len(*n.NSIncluded) { + mappedNS = mappedNS + "," + } + n.BackupName = n.BackupName + ns + n.RestoreName = n.RestoreName + ns + } + n.BackupName = n.BackupName + "backup-ns-mapping-" + UUIDgen.String() + n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() + + n.MappedNamespaceList = mappedNSList + fmt.Println(mappedNSList) + n.BackupArgs = []string{ + "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, + "--include-namespaces", strings.Join(*n.NSIncluded, ","), + "--default-volumes-to-restic", "--wait", + } + n.RestoreArgs = []string{ + "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, + "--from-backup", n.BackupName, "--namespace-mappings", mappedNS, + "--wait", + } + return nil +} +func (n *NamespaceMapping) CreateResources() error { + n.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + + for index, ns := range *n.NSIncluded { + n.kibishiiData.Levels = len(*n.NSIncluded) + index + By(fmt.Sprintf("Creating namespaces ...%s\n", ns), func() { + Expect(CreateNamespace(n.Ctx, n.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) + }) + By("Deploy sample workload of Kibishii", func() { + Expect(KibishiiPrepareBeforeBackup(n.Ctx, n.Client, VeleroCfg.CloudProvider, + ns, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, + VeleroCfg.KibishiiDirectory, false, n.kibishiiData)).To(Succeed()) + }) + } + return nil +} + +func (n *NamespaceMapping) Verify() error { + n.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + for index, ns := range n.MappedNamespaceList { + n.kibishiiData.Levels = len(*n.NSIncluded) + index + By(fmt.Sprintf("Verify workload %s after restore ", ns), func() { + Expect(KibishiiVerifyAfterRestore(n.Client, ns, + n.Ctx, n.kibishiiData)).To(Succeed(), "Fail to verify workload after restore") + }) + } + for _, ns := range *n.NSIncluded { + By(fmt.Sprintf("Verify namespace %s for backup is no longer exist after restore with namespace mapping", ns), func() { + Expect(NamespaceShouldNotExist(n.Ctx, n.Client, ns)).To(Succeed()) + }) + } + return nil +} diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 768363e48c..9800237d42 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -147,7 +147,7 @@ func BslDeletionTest(useVolumeSnapshots bool) { By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, VeleroCfg.CloudProvider, bslDeletionTestNs, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, - VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + VeleroCfg.KibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData)).To(Succeed()) }) // Restic can not backup PV only, so pod need to be labeled also diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 088c4668d9..61bf7b7db0 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -122,6 +122,9 @@ var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots) var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) +var _ = Describe("[NamespaceMapping][Single] Backup resources should follow the specific order in schedule", OneNamespaceMappingTest) +var _ = Describe("[NamespaceMapping][Multiple] Backup resources should follow the specific order in schedule", MultiNamespacesMappingTest) + func GetKubeconfigContext() error { var err error var tcDefault, tcStandby TestClient diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index bdfdc126ac..f0f376f611 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -144,7 +144,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.DefaultClient, VeleroCfg.CloudProvider, migrationNamespace, VeleroCfg.RegistryCredentialFile, VeleroCfg.Features, - VeleroCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + VeleroCfg.KibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData)).To(Succeed()) }) By(fmt.Sprintf("Backup namespace %s", migrationNamespace), func() { @@ -249,7 +249,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) By(fmt.Sprintf("Verify workload %s after restore ", migrationNamespace), func() { Expect(KibishiiVerifyAfterRestore(*VeleroCfg.StandbyClient, migrationNamespace, - oneHourTimeout)).To(Succeed(), "Fail to verify workload after restore") + oneHourTimeout, DefaultKibishiiData)).To(Succeed(), "Fail to verify workload after restore") }) }) }) diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 0161dafc5c..fe0ad7e9d9 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -174,7 +174,7 @@ func (t *TestCase) Verify() error { } func (t *TestCase) Clean() error { - return CleanupNamespaces(t.Ctx, t.Client, t.NSBaseName) + return CleanupNamespacesWithPoll(t.Ctx, t.Client, t.NSBaseName) } func (t *TestCase) GetTestMsg() *TestMSG { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 22e491f219..488e25961b 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -130,7 +130,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC By("Deploy sample workload of Kibishii", func() { Expect(KibishiiPrepareBeforeBackup(oneHourTimeout, *VeleroCfg.ClientToInstallVelero, tmpCfg.CloudProvider, upgradeNamespace, tmpCfg.RegistryCredentialFile, tmpCfg.Features, - tmpCfg.KibishiiDirectory, useVolumeSnapshots)).To(Succeed()) + tmpCfg.KibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData)).To(Succeed()) }) By(fmt.Sprintf("Backup namespace %s", upgradeNamespace), func() { @@ -206,7 +206,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC By(fmt.Sprintf("Verify workload %s after restore ", upgradeNamespace), func() { Expect(KibishiiVerifyAfterRestore(*VeleroCfg.ClientToInstallVelero, upgradeNamespace, - oneHourTimeout)).To(Succeed(), "Fail to verify workload after restore") + oneHourTimeout, DefaultKibishiiData)).To(Succeed(), "Fail to verify workload after restore") }) }) }) diff --git a/test/e2e/util/k8s/namespace.go b/test/e2e/util/k8s/namespace.go index b5248e425d..cbdc644eb5 100644 --- a/test/e2e/util/k8s/namespace.go +++ b/test/e2e/util/k8s/namespace.go @@ -67,7 +67,7 @@ func GetNamespace(ctx context.Context, client TestClient, namespace string) (*co } func DeleteNamespace(ctx context.Context, client TestClient, namespace string, wait bool) error { - oneMinuteTimeout, _ := context.WithTimeout(context.Background(), time.Minute*1) + tenMinuteTimeout, _ := context.WithTimeout(context.Background(), time.Minute*10) if err := client.ClientGo.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}); err != nil { return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) } @@ -76,7 +76,7 @@ func DeleteNamespace(ctx context.Context, client TestClient, namespace string, w } return waitutil.PollImmediateInfinite(5*time.Second, func() (bool, error) { - if _, err := client.ClientGo.CoreV1().Namespaces().Get(oneMinuteTimeout, namespace, metav1.GetOptions{}); err != nil { + if _, err := client.ClientGo.CoreV1().Namespaces().Get(tenMinuteTimeout, namespace, metav1.GetOptions{}); err != nil { if apierrors.IsNotFound(err) { return true, nil } @@ -90,6 +90,7 @@ func DeleteNamespace(ctx context.Context, client TestClient, namespace string, w func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseName string) error { namespaces, err := client.ClientGo.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { return errors.Wrap(err, "Could not retrieve namespaces") } @@ -99,6 +100,7 @@ func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseNam if err != nil { return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name) } + fmt.Printf("Delete namespace %s", checkNamespace.Name) } } return nil @@ -135,3 +137,16 @@ func WaitAllSelectedNSDeleted(ctx context.Context, client TestClient, label stri } }) } + +func NamespaceShouldNotExist(ctx context.Context, client TestClient, namespace string) error { + namespaces, err := client.ClientGo.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "Could not retrieve namespaces") + } + for _, checkNamespace := range namespaces.Items { + if checkNamespace.Name == namespace { + return errors.New(fmt.Sprintf("Namespace %s still exist", checkNamespace.Name)) + } + } + return nil +} diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index d6afbac013..b87f3e03bf 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -37,6 +37,17 @@ const ( jumpPadPod = "jump-pad" ) +type KibishiiData struct { + Levels int + DirsPerLevel int + FilesPerLevel int + FileLength int + BlockSize int + PassNum int + ExpectedNodes int +} + +var DefaultKibishiiData = &KibishiiData{2, 10, 10, 1024, 1024, 0, 2} var KibishiiPodNameList = []string{"kibishii-deployment-0", "kibishii-deployment-1"} // RunKibishiiTests runs kibishii tests on the provider. @@ -65,9 +76,10 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re } } }() + if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, - kibishiiDirectory, useVolumeSnapshots); err != nil { + kibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } @@ -123,7 +135,7 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re return errors.Wrapf(err, "Restore %s failed from backup %s", restoreName, backupName) } - if err := KibishiiVerifyAfterRestore(client, kibishiiNamespace, oneHourTimeout); err != nil { + if err := KibishiiVerifyAfterRestore(client, kibishiiNamespace, oneHourTimeout, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Error verifying kibishii after restore") } @@ -164,11 +176,11 @@ func installKibishii(ctx context.Context, namespace string, cloudPlatform, veler return err } -func generateData(ctx context.Context, namespace string, levels int, filesPerLevel int, dirsPerLevel int, fileSize int, - blockSize int, passNum int, expectedNodes int) error { +func generateData(ctx context.Context, namespace string, kibishiiData *KibishiiData) error { kibishiiGenerateCmd := exec.CommandContext(ctx, "kubectl", "exec", "-n", namespace, "jump-pad", "--", - "/usr/local/bin/generate.sh", strconv.Itoa(levels), strconv.Itoa(filesPerLevel), strconv.Itoa(dirsPerLevel), strconv.Itoa(fileSize), - strconv.Itoa(blockSize), strconv.Itoa(passNum), strconv.Itoa(expectedNodes)) + "/usr/local/bin/generate.sh", strconv.Itoa(kibishiiData.Levels), strconv.Itoa(kibishiiData.DirsPerLevel), + strconv.Itoa(kibishiiData.FilesPerLevel), strconv.Itoa(kibishiiData.FileLength), + strconv.Itoa(kibishiiData.BlockSize), strconv.Itoa(kibishiiData.PassNum), strconv.Itoa(kibishiiData.ExpectedNodes)) fmt.Printf("kibishiiGenerateCmd cmd =%v\n", kibishiiGenerateCmd) _, stderr, err := veleroexec.RunCommand(kibishiiGenerateCmd) @@ -179,12 +191,13 @@ func generateData(ctx context.Context, namespace string, levels int, filesPerLev return nil } -func verifyData(ctx context.Context, namespace string, levels int, filesPerLevel int, dirsPerLevel int, fileSize int, - blockSize int, passNum int, expectedNodes int) error { +func verifyData(ctx context.Context, namespace string, kibishiiData *KibishiiData) error { timeout, _ := context.WithTimeout(context.Background(), time.Minute*5) kibishiiVerifyCmd := exec.CommandContext(timeout, "kubectl", "exec", "-n", namespace, "jump-pad", "--", - "/usr/local/bin/verify.sh", strconv.Itoa(levels), strconv.Itoa(filesPerLevel), strconv.Itoa(dirsPerLevel), strconv.Itoa(fileSize), - strconv.Itoa(blockSize), strconv.Itoa(passNum), strconv.Itoa(expectedNodes)) + "/usr/local/bin/verify.sh", strconv.Itoa(kibishiiData.Levels), strconv.Itoa(kibishiiData.DirsPerLevel), + strconv.Itoa(kibishiiData.FilesPerLevel), strconv.Itoa(kibishiiData.FileLength), + strconv.Itoa(kibishiiData.BlockSize), strconv.Itoa(kibishiiData.PassNum), + strconv.Itoa(kibishiiData.ExpectedNodes)) fmt.Printf("kibishiiVerifyCmd cmd =%v\n", kibishiiVerifyCmd) stdout, stderr, err := veleroexec.RunCommand(kibishiiVerifyCmd) @@ -200,7 +213,7 @@ func waitForKibishiiPods(ctx context.Context, client TestClient, kibishiiNamespa func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, - kibishiiDirectory string, useVolumeSnapshots bool) error { + kibishiiDirectory string, useVolumeSnapshots bool, kibishiiData *KibishiiData) error { serviceAccountName := "default" // wait until the service account is created before patch the image pull secret @@ -224,14 +237,20 @@ func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClie if err := waitForKibishiiPods(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to wait for ready status of kibishii pods in %s", kibishiiNamespace) } - - if err := generateData(oneHourTimeout, kibishiiNamespace, 2, 10, 10, 1024, 1024, 0, 2); err != nil { + if kibishiiData == nil { + kibishiiData = DefaultKibishiiData + } + if err := generateData(oneHourTimeout, kibishiiNamespace, kibishiiData); err != nil { return errors.Wrap(err, "Failed to generate data") } return nil } -func KibishiiVerifyAfterRestore(client TestClient, kibishiiNamespace string, oneHourTimeout context.Context) error { +func KibishiiVerifyAfterRestore(client TestClient, kibishiiNamespace string, oneHourTimeout context.Context, + kibishiiData *KibishiiData) error { + if kibishiiData == nil { + kibishiiData = DefaultKibishiiData + } // wait for kibishii pod startup // TODO - Fix kibishii so we can check that it is ready to go fmt.Printf("Waiting for kibishii pods to be ready\n") @@ -241,7 +260,7 @@ func KibishiiVerifyAfterRestore(client TestClient, kibishiiNamespace string, one time.Sleep(60 * time.Second) // TODO - check that namespace exists fmt.Printf("running kibishii verify\n") - if err := verifyData(oneHourTimeout, kibishiiNamespace, 2, 10, 10, 1024, 1024, 0, 2); err != nil { + if err := verifyData(oneHourTimeout, kibishiiNamespace, kibishiiData); err != nil { return errors.Wrap(err, "Failed to verify data generated by kibishii") } return nil From 4e25f59dc1d8ec257b6b4e27834bb00e33ccccf7 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 16 Aug 2022 13:50:28 +0800 Subject: [PATCH 250/366] Add parameter "uploader-type" to velero server (#5212) This commit adds the parameter "uploader-type" to velero server, add exposes the setting via "velero install" in CLI. fixes #5062 Signed-off-by: Daniel Jiang Signed-off-by: Daniel Jiang --- changelogs/unreleased/5212-reasonerjt | 1 + pkg/cmd/cli/install/install.go | 10 ++++++++ pkg/cmd/server/server.go | 9 +++++++ pkg/install/deployment.go | 12 +++++++++- pkg/install/deployment_test.go | 4 ++++ pkg/install/resources.go | 2 ++ pkg/uploader/types.go | 26 +++++++++++++++++--- pkg/uploader/types_test.go | 34 +++++++++++++++++++++++++++ 8 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/5212-reasonerjt create mode 100644 pkg/uploader/types_test.go diff --git a/changelogs/unreleased/5212-reasonerjt b/changelogs/unreleased/5212-reasonerjt new file mode 100644 index 0000000000..be58b9adb7 --- /dev/null +++ b/changelogs/unreleased/5212-reasonerjt @@ -0,0 +1 @@ +Add parameter "uploader-type" to velero server \ No newline at end of file diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index 0f1819fa86..a908ff23f6 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -24,6 +24,8 @@ import ( "strings" "time" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -74,6 +76,7 @@ type InstallOptions struct { CACertFile string Features string DefaultVolumesToRestic bool + UploaderType string } // BindFlags adds command line values to the options struct. @@ -110,6 +113,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "File containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") flags.StringVar(&o.Features, "features", o.Features, "Comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled") flags.BoolVar(&o.DefaultVolumesToRestic, "default-volumes-to-restic", o.DefaultVolumesToRestic, "Bool flag to configure Velero server to use restic by default to backup all pod volumes on all backups. Optional.") + flags.StringVar(&o.UploaderType, "uploader-type", o.UploaderType, fmt.Sprintf("The type of uploader to transfer the data of pod volumes, the supported values are '%s', '%s'", uploader.ResticType, uploader.KopiaType)) } // NewInstallOptions instantiates a new, default InstallOptions struct. @@ -135,6 +139,7 @@ func NewInstallOptions() *InstallOptions { NoDefaultBackupLocation: false, CRDsOnly: false, DefaultVolumesToRestic: false, + UploaderType: uploader.ResticType, } } @@ -195,6 +200,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { CACertData: caCertData, Features: strings.Split(o.Features, ","), DefaultVolumesToRestic: o.DefaultVolumesToRestic, + UploaderType: o.UploaderType, }, nil } @@ -327,6 +333,10 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact return err } + if err := uploader.ValidateUploaderType(o.UploaderType); err != nil { + return err + } + // If we're only installing CRDs, we can skip the rest of the validation. if o.CRDsOnly { return nil diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f45a302207..8a0251f1b3 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -27,6 +27,8 @@ import ( "strings" "time" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/bombsimon/logrusr" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -127,6 +129,7 @@ type serverConfig struct { defaultResticMaintenanceFrequency time.Duration garbageCollectionFrequency time.Duration defaultVolumesToRestic bool + uploaderType string } type controllerRunInfo struct { @@ -157,6 +160,7 @@ func NewCommand(f client.Factory) *cobra.Command { formatFlag: logging.NewFormatFlag(), defaultResticMaintenanceFrequency: restic.DefaultMaintenanceFrequency, defaultVolumesToRestic: restic.DefaultVolumesToRestic, + uploaderType: uploader.ResticType, } ) @@ -222,6 +226,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default.") command.Flags().DurationVar(&config.garbageCollectionFrequency, "garbage-collection-frequency", config.garbageCollectionFrequency, "How often garbage collection is run for expired backups.") command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "Backup all volumes with restic by default.") + command.Flags().StringVar(&config.uploaderType, "uploader-type", config.uploaderType, "Type of uploader to handle the transfer of data of pod volumes") return command } @@ -251,6 +256,10 @@ type server struct { } func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) { + if err := uploader.ValidateUploaderType(config.uploaderType); err != nil { + return nil, err + } + if config.clientQPS < 0.0 { return nil, errors.New("client-qps must be positive") } diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index ebeb936f61..cf34f175f1 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -44,6 +44,7 @@ type podTemplateConfig struct { plugins []string features []string defaultVolumesToRestic bool + uploaderType string } func WithImage(image string) podTemplateOption { @@ -83,7 +84,6 @@ func WithEnvFromSecretKey(varName, secret, key string) podTemplateOption { func WithSecret(secretPresent bool) podTemplateOption { return func(c *podTemplateConfig) { c.withSecret = secretPresent - } } @@ -123,6 +123,12 @@ func WithFeatures(features []string) podTemplateOption { } } +func WithUploaderType(t string) podTemplateOption { + return func(c *podTemplateConfig) { + c.uploaderType = t + } +} + func WithDefaultVolumesToRestic() podTemplateOption { return func(c *podTemplateConfig) { c.defaultVolumesToRestic = true @@ -155,6 +161,10 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment args = append(args, "--default-volumes-to-restic=true") } + if len(c.uploaderType) > 0 { + args = append(args, fmt.Sprintf("--uploader-type=%s", c.uploaderType)) + } + deployment := &appsv1.Deployment{ ObjectMeta: objectMeta(namespace, "velero"), TypeMeta: metav1.TypeMeta{ diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index 616db03246..ef5f871a06 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -57,4 +57,8 @@ func TestDeployment(t *testing.T) { deploy = Deployment("velero", WithFeatures([]string{"EnableCSI", "foo", "bar", "baz"})) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--features=EnableCSI,foo,bar,baz", deploy.Spec.Template.Spec.Containers[0].Args[1]) + + deploy = Deployment("velero", WithUploaderType("kopia")) + assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) + assert.Equal(t, "--uploader-type=kopia", deploy.Spec.Template.Spec.Containers[0].Args[1]) } diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 78a9ed6891..7053b87c59 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -232,6 +232,7 @@ type VeleroOptions struct { CACertData []byte Features []string DefaultVolumesToRestic bool + UploaderType string } func AllCRDs() *unstructured.UnstructuredList { @@ -287,6 +288,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { WithSecret(secretPresent), WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency), WithGarbageCollectionFrequency(o.GarbageCollectionFrequency), + WithUploaderType(o.UploaderType), } if len(o.Features) > 0 { diff --git a/pkg/uploader/types.go b/pkg/uploader/types.go index 27a864c844..134e36cceb 100644 --- a/pkg/uploader/types.go +++ b/pkg/uploader/types.go @@ -1,9 +1,12 @@ /* -Copyright The Velero Contributors. +Copyright the Velero contributors. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,11 +16,28 @@ limitations under the License. package uploader +import ( + "fmt" + "strings" +) + const ( - VeleroBackup string = "backup" - VeleroRestore string = "restore" + ResticType = "restic" + KopiaType = "kopia" + VeleroBackup = "backup" + VeleroRestore = "restore" ) +// ValidateUploaderType validates if the input param is a valid uploader type. +// It will return an error if it's invalid. +func ValidateUploaderType(t string) error { + t = strings.TrimSpace(t) + if t != ResticType && t != KopiaType { + return fmt.Errorf("invalid uploader type '%s', valid upload types are: '%s', '%s'", t, ResticType, KopiaType) + } + return nil +} + type SnapshotInfo struct { ID string `json:"id"` Size int64 `json:"Size"` diff --git a/pkg/uploader/types_test.go b/pkg/uploader/types_test.go new file mode 100644 index 0000000000..492051bf2e --- /dev/null +++ b/pkg/uploader/types_test.go @@ -0,0 +1,34 @@ +package uploader + +import "testing" + +func TestValidateUploaderType(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + }{ + { + "'restic' is a valid type", + "restic", + false, + }, + { + "' kopia ' is a valid type (space will be trimmed)", + " kopia ", + false, + }, + { + "'anything_else' is invalid", + "anything_else", + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateUploaderType(tt.input); (err != nil) != tt.wantErr { + t.Errorf("ValidateUploaderType(), input = '%s' error = %v, wantErr %v", tt.input, err, tt.wantErr) + } + }) + } +} From 047c7531fa4c740aa903149e0c8000bb55cb0637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 8 Aug 2022 19:46:30 +0800 Subject: [PATCH 251/366] Refactor the restic repo related code for Kopia integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the restic repo related code for Kopia integration Signed-off-by: Wenkai Yin(尹文开) --- pkg/apis/velero/v1/backup_repository_types.go | 3 + pkg/cmd/server/server.go | 36 +-- pkg/controller/backup_deletion_controller.go | 11 +- .../restic_repository_controller.go | 7 +- .../restic_repository_controller_test.go | 4 +- pkg/repository/manager.go | 188 ++++++++++++ pkg/repository/manager_test.go | 47 +++ .../mocks/repository_manager.go | 0 pkg/repository/provider/restic.go | 69 +++++ pkg/repository/restic/repository.go | 122 ++++++++ pkg/restic/common.go | 29 ++ pkg/restic/common_test.go | 96 +++++++ pkg/restic/repository_keys.go | 75 ----- pkg/restic/repository_keys_test.go | 30 -- pkg/restic/repository_manager.go | 269 ------------------ pkg/restic/repository_manager_test.go | 121 -------- 16 files changed, 581 insertions(+), 526 deletions(-) create mode 100644 pkg/repository/manager.go create mode 100644 pkg/repository/manager_test.go rename pkg/{restic => repository}/mocks/repository_manager.go (100%) create mode 100644 pkg/repository/provider/restic.go create mode 100644 pkg/repository/restic/repository.go delete mode 100644 pkg/restic/repository_keys.go delete mode 100644 pkg/restic/repository_keys_test.go delete mode 100644 pkg/restic/repository_manager.go delete mode 100644 pkg/restic/repository_manager_test.go diff --git a/pkg/apis/velero/v1/backup_repository_types.go b/pkg/apis/velero/v1/backup_repository_types.go index 300ecae9c1..a64e3be689 100644 --- a/pkg/apis/velero/v1/backup_repository_types.go +++ b/pkg/apis/velero/v1/backup_repository_types.go @@ -51,6 +51,9 @@ const ( BackupRepositoryPhaseNew BackupRepositoryPhase = "New" BackupRepositoryPhaseReady BackupRepositoryPhase = "Ready" BackupRepositoryPhaseNotReady BackupRepositoryPhase = "NotReady" + + BackupRepositoryTypeRestic string = "restic" + BackupRepositoryTypeUnified string = "unified" ) // BackupRepositoryStatus is the current status of a BackupRepository. diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 8a0251f1b3..96dc264e8f 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -82,6 +82,8 @@ import ( "github.com/vmware-tanzu/velero/internal/storage" "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" ) @@ -248,7 +250,9 @@ type server struct { logger logrus.FieldLogger logLevel logrus.Level pluginRegistry clientmgmt.Registry - resticManager restic.RepositoryManager + repoManager repository.Manager + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer metrics *metrics.ServerMetrics config serverConfig mgr manager.Manager @@ -536,22 +540,10 @@ func (s *server) initRestic() error { return err } - res, err := restic.NewRepositoryManager( - s.ctx, - s.namespace, - s.veleroClient, - s.sharedInformerFactory.Velero().V1().BackupRepositories(), - s.veleroClient.VeleroV1(), - s.mgr.GetClient(), - s.kubeClient.CoreV1(), - s.kubeClient.CoreV1(), - s.credentialFileStore, - s.logger, - ) - if err != nil { - return err - } - s.resticManager = res + s.repoLocker = repository.NewRepoLocker() + s.repoEnsurer = repository.NewRepositoryEnsurer(s.sharedInformerFactory.Velero().V1().BackupRepositories(), s.veleroClient.VeleroV1(), s.logger) + + s.repoManager = repository.NewManager(s.namespace, s.mgr.GetClient(), s.repoLocker, s.repoEnsurer, s.credentialFileStore, s.logger) return nil } @@ -643,7 +635,8 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.discoveryHelper, client.NewDynamicFactory(s.dynamicClient), podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), - s.resticManager, + podvolume.NewBackupperFactory(s.repoLocker, s.repoEnsurer, s.veleroClient, s.kubeClient.CoreV1(), + s.kubeClient.CoreV1(), s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), s.config.podVolumeOperationTimeout, s.config.defaultVolumesToRestic, s.config.clientPageSize, @@ -704,7 +697,8 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string client.NewDynamicFactory(s.dynamicClient), s.config.restoreResourcePriorities, s.kubeClient.CoreV1().Namespaces(), - s.resticManager, + podvolume.NewRestorerFactory(s.repoLocker, s.repoEnsurer, s.veleroClient, s.kubeClient.CoreV1(), + s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), s.config.podVolumeOperationTimeout, s.config.resourceTerminatingTimeout, s.logger, @@ -812,7 +806,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) } - if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.resticManager).SetupWithManager(s.mgr); err != nil { + if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) } @@ -820,7 +814,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger, s.mgr.GetClient(), backupTracker, - s.resticManager, + s.repoManager, s.metrics, s.discoveryHelper, newPluginManager, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 52d358042e..a616dcb711 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -40,6 +40,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -56,7 +57,7 @@ type backupDeletionReconciler struct { client.Client logger logrus.FieldLogger backupTracker BackupTracker - resticMgr restic.RepositoryManager + repoMgr repository.Manager metrics *metrics.ServerMetrics clock clock.Clock discoveryHelper discovery.Helper @@ -69,7 +70,7 @@ func NewBackupDeletionReconciler( logger logrus.FieldLogger, client client.Client, backupTracker BackupTracker, - resticMgr restic.RepositoryManager, + repoMgr repository.Manager, metrics *metrics.ServerMetrics, helper discovery.Helper, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, @@ -79,7 +80,7 @@ func NewBackupDeletionReconciler( Client: client, logger: logger, backupTracker: backupTracker, - resticMgr: resticMgr, + repoMgr: repoMgr, metrics: metrics, clock: clock.RealClock{}, discoveryHelper: helper, @@ -435,7 +436,7 @@ func (r *backupDeletionReconciler) deleteExistingDeletionRequests(ctx context.Co } func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, backup *velerov1api.Backup) []error { - if r.resticMgr == nil { + if r.repoMgr == nil { return nil } @@ -449,7 +450,7 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba var errs []error for _, snapshot := range snapshots { - if err := r.resticMgr.Forget(ctx2, snapshot); err != nil { + if err := r.repoMgr.Forget(ctx2, snapshot); err != nil { errs = append(errs, err) } } diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index c3ca1505ac..d6cd869e3e 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -30,6 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -45,11 +46,11 @@ type ResticRepoReconciler struct { logger logrus.FieldLogger clock clock.Clock defaultMaintenanceFrequency time.Duration - repositoryManager restic.RepositoryManager + repositoryManager repository.Manager } func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client client.Client, - defaultMaintenanceFrequency time.Duration, repositoryManager restic.RepositoryManager) *ResticRepoReconciler { + defaultMaintenanceFrequency time.Duration, repositoryManager repository.Manager) *ResticRepoReconciler { c := &ResticRepoReconciler{ client, namespace, @@ -163,7 +164,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 // ensureRepo checks to see if a repository exists, and attempts to initialize it if // it does not exist. An error is returned if the repository can't be connected to // or initialized. -func ensureRepo(repo *velerov1api.BackupRepository, repoManager restic.RepositoryManager) error { +func ensureRepo(repo *velerov1api.BackupRepository, repoManager repository.Manager) error { if err := repoManager.ConnectToRepo(repo); err != nil { // If the repository has not yet been initialized, the error message will always include // the following string. This is the only scenario where we should try to initialize it. diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index 28e899329c..d693f510be 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -24,14 +24,14 @@ import ( ctrl "sigs.k8s.io/controller-runtime" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - resticmokes "github.com/vmware-tanzu/velero/pkg/restic/mocks" + repomokes "github.com/vmware-tanzu/velero/pkg/repository/mocks" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) const defaultMaintenanceFrequency = 10 * time.Minute func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { - mgr := &resticmokes.RepositoryManager{} + mgr := &repomokes.RepositoryManager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) } diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go new file mode 100644 index 0000000000..eb700d1062 --- /dev/null +++ b/pkg/repository/manager.go @@ -0,0 +1,188 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package repository + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository/provider" + "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" +) + +// Manager manages backup repositories. +type Manager interface { + // InitRepo initializes a repo with the specified name and identifier. + InitRepo(repo *velerov1api.BackupRepository) error + + // ConnectToRepo runs the 'restic snapshots' command against the + // specified repo, and returns an error if it fails. This is + // intended to be used to ensure that the repo exists/can be + // authenticated to. + ConnectToRepo(repo *velerov1api.BackupRepository) error + + // PruneRepo deletes unused data from a repo. + PruneRepo(repo *velerov1api.BackupRepository) error + + // UnlockRepo removes stale locks from a repo. + UnlockRepo(repo *velerov1api.BackupRepository) error + + // Forget removes a snapshot from the list of + // available snapshots in a repo. + Forget(context.Context, restic.SnapshotIdentifier) error +} + +type manager struct { + namespace string + providers map[string]provider.Provider + client client.Client + repoLocker *RepoLocker + repoEnsurer *RepositoryEnsurer + fileSystem filesystem.Interface + log logrus.FieldLogger +} + +// NewManager create a new repository manager. +func NewManager( + namespace string, + client client.Client, + repoLocker *RepoLocker, + repoEnsurer *RepositoryEnsurer, + credentialFileStore credentials.FileStore, + log logrus.FieldLogger, +) Manager { + mgr := &manager{ + namespace: namespace, + client: client, + providers: map[string]provider.Provider{}, + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + fileSystem: filesystem.NewFileSystem(), + log: log, + } + + mgr.providers[velerov1api.BackupRepositoryTypeRestic] = provider.NewResticRepositoryProvider(credentialFileStore, mgr.fileSystem, mgr.log) + + return mgr +} + +func (m *manager) InitRepo(repo *velerov1api.BackupRepository) error { + m.repoLocker.LockExclusive(repo.Name) + defer m.repoLocker.UnlockExclusive(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.InitRepo(context.Background(), param) +} + +func (m *manager) ConnectToRepo(repo *velerov1api.BackupRepository) error { + m.repoLocker.Lock(repo.Name) + defer m.repoLocker.Unlock(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.ConnectToRepo(context.Background(), param) +} + +func (m *manager) PruneRepo(repo *velerov1api.BackupRepository) error { + m.repoLocker.LockExclusive(repo.Name) + defer m.repoLocker.UnlockExclusive(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.PruneRepo(context.Background(), param) +} + +func (m *manager) UnlockRepo(repo *velerov1api.BackupRepository) error { + m.repoLocker.Lock(repo.Name) + defer m.repoLocker.Unlock(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.EnsureUnlockRepo(context.Background(), param) +} + +func (m *manager) Forget(ctx context.Context, snapshot restic.SnapshotIdentifier) error { + repo, err := m.repoEnsurer.EnsureRepo(ctx, m.namespace, snapshot.VolumeNamespace, snapshot.BackupStorageLocation) + if err != nil { + return err + } + + m.repoLocker.LockExclusive(repo.Name) + defer m.repoLocker.UnlockExclusive(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.Forget(context.Background(), snapshot.SnapshotID, param) +} + +func (m *manager) getRepositoryProvider(repo *velerov1api.BackupRepository) (provider.Provider, error) { + switch repo.Spec.RepositoryType { + case "", velerov1api.BackupRepositoryTypeRestic: + return m.providers[velerov1api.BackupRepositoryTypeRestic], nil + default: + return nil, fmt.Errorf("failed to get provider for repository %s", repo.Spec.RepositoryType) + } +} + +func (m *manager) assembleRepoParam(repo *velerov1api.BackupRepository) (provider.RepoParam, error) { + bsl := &velerov1api.BackupStorageLocation{} + if err := m.client.Get(context.Background(), client.ObjectKey{m.namespace, repo.Spec.BackupStorageLocation}, bsl); err != nil { + return provider.RepoParam{}, errors.WithStack(err) + } + return provider.RepoParam{ + BackupLocation: bsl, + BackupRepo: repo, + }, nil +} diff --git a/pkg/repository/manager_test.go b/pkg/repository/manager_test.go new file mode 100644 index 0000000000..7692a8b219 --- /dev/null +++ b/pkg/repository/manager_test.go @@ -0,0 +1,47 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package repository + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestGetRepositoryProvider(t *testing.T) { + mgr := NewManager("", nil, nil, nil, nil, nil).(*manager) + repo := &velerov1.BackupRepository{} + + // empty repository type + provider, err := mgr.getRepositoryProvider(repo) + require.Nil(t, err) + assert.NotNil(t, provider) + + // valid repository type + repo.Spec.RepositoryType = velerov1.BackupRepositoryTypeRestic + provider, err = mgr.getRepositoryProvider(repo) + require.Nil(t, err) + assert.NotNil(t, provider) + + // invalid repository type + repo.Spec.RepositoryType = "unknown" + _, err = mgr.getRepositoryProvider(repo) + require.NotNil(t, err) +} diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/repository/mocks/repository_manager.go similarity index 100% rename from pkg/restic/mocks/repository_manager.go rename to pkg/repository/mocks/repository_manager.go diff --git a/pkg/repository/provider/restic.go b/pkg/repository/provider/restic.go new file mode 100644 index 0000000000..3659f1be76 --- /dev/null +++ b/pkg/repository/provider/restic.go @@ -0,0 +1,69 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + "github.com/vmware-tanzu/velero/pkg/repository/restic" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" +) + +func NewResticRepositoryProvider(store credentials.FileStore, fs filesystem.Interface, log logrus.FieldLogger) Provider { + return &resticRepositoryProvider{ + svc: restic.NewRepositoryService(store, fs, log), + } +} + +type resticRepositoryProvider struct { + svc *restic.RepositoryService +} + +func (r *resticRepositoryProvider) InitRepo(ctx context.Context, param RepoParam) error { + return r.svc.InitRepo(param.BackupLocation, param.BackupRepo) +} + +func (r *resticRepositoryProvider) ConnectToRepo(ctx context.Context, param RepoParam) error { + return r.svc.ConnectToRepo(param.BackupLocation, param.BackupRepo) +} + +func (r *resticRepositoryProvider) PrepareRepo(ctx context.Context, param RepoParam) error { + if err := r.InitRepo(ctx, param); err != nil { + return err + } + return r.ConnectToRepo(ctx, param) +} + +func (r *resticRepositoryProvider) PruneRepo(ctx context.Context, param RepoParam) error { + return r.svc.PruneRepo(param.BackupLocation, param.BackupRepo) +} + +func (r *resticRepositoryProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { + // restic doesn't support this operation + return nil +} + +func (r *resticRepositoryProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error { + return r.svc.UnlockRepo(param.BackupLocation, param.BackupRepo) +} + +func (r *resticRepositoryProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { + return r.svc.Forget(param.BackupLocation, param.BackupRepo, snapshotID) +} diff --git a/pkg/repository/restic/repository.go b/pkg/repository/restic/repository.go new file mode 100644 index 0000000000..fa88a9cc44 --- /dev/null +++ b/pkg/repository/restic/repository.go @@ -0,0 +1,122 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restic + +import ( + "os" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" + "github.com/vmware-tanzu/velero/pkg/restic" + veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" +) + +func NewRepositoryService(store credentials.FileStore, fs filesystem.Interface, log logrus.FieldLogger) *RepositoryService { + return &RepositoryService{ + credentialsFileStore: store, + fileSystem: fs, + log: log, + } +} + +type RepositoryService struct { + credentialsFileStore credentials.FileStore + fileSystem filesystem.Interface + log logrus.FieldLogger +} + +func (r *RepositoryService) InitRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error { + return r.exec(restic.InitCommand(repo.Spec.ResticIdentifier), bsl) +} + +func (r *RepositoryService) ConnectToRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error { + snapshotsCmd := restic.SnapshotsCommand(repo.Spec.ResticIdentifier) + // use the '--latest=1' flag to minimize the amount of data fetched since + // we're just validating that the repo exists and can be authenticated + // to. + // "--last" is replaced by "--latest=1" in restic v0.12.1 + snapshotsCmd.ExtraFlags = append(snapshotsCmd.ExtraFlags, "--latest=1") + + return r.exec(snapshotsCmd, bsl) +} + +func (r *RepositoryService) PruneRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error { + return r.exec(restic.PruneCommand(repo.Spec.ResticIdentifier), bsl) +} + +func (r *RepositoryService) UnlockRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error { + return r.exec(restic.UnlockCommand(repo.Spec.ResticIdentifier), bsl) +} + +func (r *RepositoryService) Forget(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository, snapshotID string) error { + return r.exec(restic.ForgetCommand(repo.Spec.ResticIdentifier, snapshotID), bsl) +} + +func (r *RepositoryService) exec(cmd *restic.Command, bsl *velerov1api.BackupStorageLocation) error { + file, err := r.credentialsFileStore.Path(repokey.RepoKeySelector()) + if err != nil { + return err + } + // ignore error since there's nothing we can do and it's a temp file. + defer os.Remove(file) + + cmd.PasswordFile = file + + // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic + var caCertFile string + if bsl.Spec.ObjectStorage != nil && bsl.Spec.ObjectStorage.CACert != nil { + caCertFile, err = restic.TempCACertFile(bsl.Spec.ObjectStorage.CACert, bsl.Name, r.fileSystem) + if err != nil { + return errors.Wrap(err, "error creating temp cacert file") + } + // ignore error since there's nothing we can do and it's a temp file. + defer os.Remove(caCertFile) + } + cmd.CACertFile = caCertFile + + env, err := restic.CmdEnv(bsl, r.credentialsFileStore) + if err != nil { + return err + } + cmd.Env = env + + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(bsl, r.log) + if len(skipTLSRet) > 0 { + cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet) + } + + stdout, stderr, err := veleroexec.RunCommand(cmd.Cmd()) + r.log.WithFields(logrus.Fields{ + "repository": cmd.RepoName(), + "command": cmd.String(), + "stdout": stdout, + "stderr": stderr, + }).Debugf("Ran restic command") + if err != nil { + return errors.Wrapf(err, "error running command=%s, stdout=%s, stderr=%s", cmd.String(), stdout, stderr) + } + + return nil +} diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 6e2671625d..860f983f72 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -20,9 +20,11 @@ import ( "context" "fmt" "os" + "strconv" "time" "github.com/pkg/errors" + "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" @@ -49,6 +51,14 @@ const ( // DefaultVolumesToRestic specifies whether restic should be used, by default, to // take backup of all pod volumes. DefaultVolumesToRestic = false + + // insecureSkipTLSVerifyKey is the flag in BackupStorageLocation's config + // to indicate whether to skip TLS verify to setup insecure HTTPS connection. + insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" + + // resticInsecureTLSFlag is the flag for Restic command line to indicate + // skip TLS verify on https connection. + resticInsecureTLSFlag = "--insecure-tls" ) // SnapshotIdentifier uniquely identifies a restic snapshot @@ -176,3 +186,22 @@ func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileSto return env, nil } + +// GetInsecureSkipTLSVerifyFromBSL get insecureSkipTLSVerify flag from BSL configuration, +// Then return --insecure-tls flag with boolean value as result. +func GetInsecureSkipTLSVerifyFromBSL(backupLocation *velerov1api.BackupStorageLocation, logger logrus.FieldLogger) string { + result := "" + + if backupLocation == nil { + logger.Info("bsl is nil. return empty.") + return result + } + + if insecure, _ := strconv.ParseBool(backupLocation.Spec.Config[insecureSkipTLSVerifyKey]); insecure { + logger.Debugf("set --insecure-tls=true for Restic command according to BSL %s config", backupLocation.Name) + result = resticInsecureTLSFlag + "=true" + return result + } + + return result +} diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index fac82f9011..b2acee773f 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -22,6 +22,7 @@ import ( "sort" "testing" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1api "k8s.io/api/core/v1" @@ -216,3 +217,98 @@ func TestTempCACertFile(t *testing.T) { os.Remove(fileName) } + +func TestGetInsecureSkipTLSVerifyFromBSL(t *testing.T) { + log := logrus.StandardLogger() + tests := []struct { + name string + backupLocation *velerov1api.BackupStorageLocation + logger logrus.FieldLogger + expected string + }{ + { + "Test with nil BSL. Should return empty string.", + nil, + log, + "", + }, + { + "Test BSL with no configuration. Should return empty string.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + log, + "", + }, + { + "Test with AWS BSL's insecureSkipTLSVerify set to false.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Config: map[string]string{ + "insecureSkipTLSVerify": "false", + }, + }, + }, + log, + "", + }, + { + "Test with AWS BSL's insecureSkipTLSVerify set to true.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Config: map[string]string{ + "insecureSkipTLSVerify": "true", + }, + }, + }, + log, + "--insecure-tls=true", + }, + { + "Test with Azure BSL's insecureSkipTLSVerify set to invalid.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{ + "insecureSkipTLSVerify": "invalid", + }, + }, + }, + log, + "", + }, + { + "Test with GCP without insecureSkipTLSVerify.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "gcp", + Config: map[string]string{}, + }, + }, + log, + "", + }, + { + "Test with AWS without config.", + &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + }, + }, + log, + "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res := GetInsecureSkipTLSVerifyFromBSL(test.backupLocation, test.logger) + + assert.Equal(t, test.expected, res) + }) + } +} diff --git a/pkg/restic/repository_keys.go b/pkg/restic/repository_keys.go deleted file mode 100644 index 28c190f70e..0000000000 --- a/pkg/restic/repository_keys.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "context" - - "github.com/pkg/errors" - corev1api "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - - "github.com/vmware-tanzu/velero/pkg/builder" -) - -const ( - credentialsSecretName = "velero-restic-credentials" - credentialsKey = "repository-password" - - encryptionKey = "static-passw0rd" -) - -func EnsureCommonRepositoryKey(secretClient corev1client.SecretsGetter, namespace string) error { - _, err := secretClient.Secrets(namespace).Get(context.TODO(), credentialsSecretName, metav1.GetOptions{}) - if err != nil && !apierrors.IsNotFound(err) { - return errors.WithStack(err) - } - if err == nil { - return nil - } - - // if we got here, we got an IsNotFound error, so we need to create the key - - secret := &corev1api.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: credentialsSecretName, - }, - Type: corev1api.SecretTypeOpaque, - Data: map[string][]byte{ - credentialsKey: []byte(encryptionKey), - }, - } - - if _, err = secretClient.Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { - return errors.Wrapf(err, "error creating %s secret", credentialsSecretName) - } - - return nil -} - -// RepoKeySelector returns the SecretKeySelector which can be used to fetch -// the restic repository key. -func RepoKeySelector() *corev1api.SecretKeySelector { - // For now, all restic repos share the same key so we don't need the repoName to fetch it. - // When we move to full-backup encryption, we'll likely have a separate key per restic repo - // (all within the Velero server's namespace) so RepoKeySelector will need to select the key - // for that repo. - return builder.ForSecretKeySelector(credentialsSecretName, credentialsKey).Result() -} diff --git a/pkg/restic/repository_keys_test.go b/pkg/restic/repository_keys_test.go deleted file mode 100644 index 6af6641ce7..0000000000 --- a/pkg/restic/repository_keys_test.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRepoKeySelector(t *testing.T) { - selector := RepoKeySelector() - - require.Equal(t, credentialsSecretName, selector.Name) - require.Equal(t, credentialsKey, selector.Key) -} diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go deleted file mode 100644 index 39961fc02c..0000000000 --- a/pkg/restic/repository_manager.go +++ /dev/null @@ -1,269 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "context" - "os" - "strconv" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/vmware-tanzu/velero/internal/credentials" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - "github.com/vmware-tanzu/velero/pkg/podvolume" - "github.com/vmware-tanzu/velero/pkg/repository" - repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" - veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" - "github.com/vmware-tanzu/velero/pkg/util/filesystem" -) - -// RepositoryManager executes commands against restic repositories. -type RepositoryManager interface { - // InitRepo initializes a repo with the specified name and identifier. - InitRepo(repo *velerov1api.BackupRepository) error - - // ConnectToRepo runs the 'restic snapshots' command against the - // specified repo, and returns an error if it fails. This is - // intended to be used to ensure that the repo exists/can be - // authenticated to. - ConnectToRepo(repo *velerov1api.BackupRepository) error - - // PruneRepo deletes unused data from a repo. - PruneRepo(repo *velerov1api.BackupRepository) error - - // UnlockRepo removes stale locks from a repo. - UnlockRepo(repo *velerov1api.BackupRepository) error - - // Forget removes a snapshot from the list of - // available snapshots in a repo. - Forget(context.Context, SnapshotIdentifier) error - - podvolume.BackupperFactory - - podvolume.RestorerFactory -} - -type repositoryManager struct { - namespace string - veleroClient clientset.Interface - repoLister velerov1listers.BackupRepositoryLister - repoInformerSynced cache.InformerSynced - kbClient kbclient.Client - log logrus.FieldLogger - repoLocker *repository.RepoLocker - repoEnsurer *repository.RepositoryEnsurer - fileSystem filesystem.Interface - ctx context.Context - pvcClient corev1client.PersistentVolumeClaimsGetter - pvClient corev1client.PersistentVolumesGetter - credentialsFileStore credentials.FileStore - podvolume.BackupperFactory - podvolume.RestorerFactory -} - -const ( - // insecureSkipTLSVerifyKey is the flag in BackupStorageLocation's config - // to indicate whether to skip TLS verify to setup insecure HTTPS connection. - insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" - - // resticInsecureTLSFlag is the flag for Restic command line to indicate - // skip TLS verify on https connection. - resticInsecureTLSFlag = "--insecure-tls" -) - -// NewRepositoryManager constructs a RepositoryManager. -func NewRepositoryManager( - ctx context.Context, - namespace string, - veleroClient clientset.Interface, - repoInformer velerov1informers.BackupRepositoryInformer, - repoClient velerov1client.BackupRepositoriesGetter, - kbClient kbclient.Client, - pvcClient corev1client.PersistentVolumeClaimsGetter, - pvClient corev1client.PersistentVolumesGetter, - credentialFileStore credentials.FileStore, - log logrus.FieldLogger, -) (RepositoryManager, error) { - rm := &repositoryManager{ - namespace: namespace, - veleroClient: veleroClient, - repoLister: repoInformer.Lister(), - repoInformerSynced: repoInformer.Informer().HasSynced, - kbClient: kbClient, - pvcClient: pvcClient, - pvClient: pvClient, - credentialsFileStore: credentialFileStore, - log: log, - ctx: ctx, - - repoLocker: repository.NewRepoLocker(), - repoEnsurer: repository.NewRepositoryEnsurer(repoInformer, repoClient, log), - fileSystem: filesystem.NewFileSystem(), - } - rm.BackupperFactory = podvolume.NewBackupperFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, - rm.pvClient, rm.repoInformerSynced, rm.log) - rm.RestorerFactory = podvolume.NewRestorerFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, - rm.repoInformerSynced, rm.log) - - return rm, nil -} - -func (rm *repositoryManager) InitRepo(repo *velerov1api.BackupRepository) error { - // restic init requires an exclusive lock - rm.repoLocker.LockExclusive(repo.Name) - defer rm.repoLocker.UnlockExclusive(repo.Name) - - return rm.exec(InitCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) -} - -func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.BackupRepository) error { - // restic snapshots requires a non-exclusive lock - rm.repoLocker.Lock(repo.Name) - defer rm.repoLocker.Unlock(repo.Name) - - snapshotsCmd := SnapshotsCommand(repo.Spec.ResticIdentifier) - // use the '--latest=1' flag to minimize the amount of data fetched since - // we're just validating that the repo exists and can be authenticated - // to. - // "--last" is replaced by "--latest=1" in restic v0.12.1 - snapshotsCmd.ExtraFlags = append(snapshotsCmd.ExtraFlags, "--latest=1") - - return rm.exec(snapshotsCmd, repo.Spec.BackupStorageLocation) -} - -func (rm *repositoryManager) PruneRepo(repo *velerov1api.BackupRepository) error { - // restic prune requires an exclusive lock - rm.repoLocker.LockExclusive(repo.Name) - defer rm.repoLocker.UnlockExclusive(repo.Name) - - return rm.exec(PruneCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) -} - -func (rm *repositoryManager) UnlockRepo(repo *velerov1api.BackupRepository) error { - // restic unlock requires a non-exclusive lock - rm.repoLocker.Lock(repo.Name) - defer rm.repoLocker.Unlock(repo.Name) - - return rm.exec(UnlockCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) -} - -func (rm *repositoryManager) Forget(ctx context.Context, snapshot SnapshotIdentifier) error { - // We can't wait for this in the constructor, because this informer is coming - // from the shared informer factory, which isn't started until *after* the repo - // manager is instantiated & passed to the controller constructors. We'd get a - // deadlock if we tried to wait for this in the constructor. - if !cache.WaitForCacheSync(ctx.Done(), rm.repoInformerSynced) { - return errors.New("timed out waiting for cache to sync") - } - - repo, err := rm.repoEnsurer.EnsureRepo(ctx, rm.namespace, snapshot.VolumeNamespace, snapshot.BackupStorageLocation) - if err != nil { - return err - } - - // restic forget requires an exclusive lock - rm.repoLocker.LockExclusive(repo.Name) - defer rm.repoLocker.UnlockExclusive(repo.Name) - - return rm.exec(ForgetCommand(repo.Spec.ResticIdentifier, snapshot.SnapshotID), repo.Spec.BackupStorageLocation) -} - -func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { - file, err := rm.credentialsFileStore.Path(repokey.RepoKeySelector()) - if err != nil { - return err - } - // ignore error since there's nothing we can do and it's a temp file. - defer os.Remove(file) - - cmd.PasswordFile = file - - loc := &velerov1api.BackupStorageLocation{} - if err := rm.kbClient.Get(context.Background(), kbclient.ObjectKey{ - Namespace: rm.namespace, - Name: backupLocation, - }, loc); err != nil { - return errors.Wrap(err, "error getting backup storage location") - } - - // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - var caCertFile string - if loc.Spec.ObjectStorage != nil && loc.Spec.ObjectStorage.CACert != nil { - caCertFile, err = TempCACertFile(loc.Spec.ObjectStorage.CACert, backupLocation, rm.fileSystem) - if err != nil { - return errors.Wrap(err, "error creating temp cacert file") - } - // ignore error since there's nothing we can do and it's a temp file. - defer os.Remove(caCertFile) - } - cmd.CACertFile = caCertFile - - env, err := CmdEnv(loc, rm.credentialsFileStore) - if err != nil { - return err - } - cmd.Env = env - - // #4820: restrieve insecureSkipTLSVerify from BSL configuration for - // AWS plugin. If nothing is return, that means insecureSkipTLSVerify - // is not enable for Restic command. - skipTLSRet := GetInsecureSkipTLSVerifyFromBSL(loc, rm.log) - if len(skipTLSRet) > 0 { - cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet) - } - - stdout, stderr, err := veleroexec.RunCommand(cmd.Cmd()) - rm.log.WithFields(logrus.Fields{ - "repository": cmd.RepoName(), - "command": cmd.String(), - "stdout": stdout, - "stderr": stderr, - }).Debugf("Ran restic command") - if err != nil { - return errors.Wrapf(err, "error running command=%s, stdout=%s, stderr=%s", cmd.String(), stdout, stderr) - } - - return nil -} - -// GetInsecureSkipTLSVerifyFromBSL get insecureSkipTLSVerify flag from BSL configuration, -// Then return --insecure-tls flag with boolean value as result. -func GetInsecureSkipTLSVerifyFromBSL(backupLocation *velerov1api.BackupStorageLocation, logger logrus.FieldLogger) string { - result := "" - - if backupLocation == nil { - logger.Info("bsl is nil. return empty.") - return result - } - - if insecure, _ := strconv.ParseBool(backupLocation.Spec.Config[insecureSkipTLSVerifyKey]); insecure { - logger.Debugf("set --insecure-tls=true for Restic command according to BSL %s config", backupLocation.Name) - result = resticInsecureTLSFlag + "=true" - return result - } - - return result -} diff --git a/pkg/restic/repository_manager_test.go b/pkg/restic/repository_manager_test.go deleted file mode 100644 index 79d326bb80..0000000000 --- a/pkg/restic/repository_manager_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" -) - -func TestGetInsecureSkipTLSVerifyFromBSL(t *testing.T) { - log := logrus.StandardLogger() - tests := []struct { - name string - backupLocation *velerov1api.BackupStorageLocation - logger logrus.FieldLogger - expected string - }{ - { - "Test with nil BSL. Should return empty string.", - nil, - log, - "", - }, - { - "Test BSL with no configuration. Should return empty string.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "azure", - }, - }, - log, - "", - }, - { - "Test with AWS BSL's insecureSkipTLSVerify set to false.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "aws", - Config: map[string]string{ - "insecureSkipTLSVerify": "false", - }, - }, - }, - log, - "", - }, - { - "Test with AWS BSL's insecureSkipTLSVerify set to true.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "aws", - Config: map[string]string{ - "insecureSkipTLSVerify": "true", - }, - }, - }, - log, - "--insecure-tls=true", - }, - { - "Test with Azure BSL's insecureSkipTLSVerify set to invalid.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "azure", - Config: map[string]string{ - "insecureSkipTLSVerify": "invalid", - }, - }, - }, - log, - "", - }, - { - "Test with GCP without insecureSkipTLSVerify.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "gcp", - Config: map[string]string{}, - }, - }, - log, - "", - }, - { - "Test with AWS without config.", - &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Provider: "aws", - }, - }, - log, - "", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - res := GetInsecureSkipTLSVerifyFromBSL(test.backupLocation, test.logger) - - assert.Equal(t, test.expected, res) - }) - } -} From 775943c85830e1e88019d8f7342f41a376ca1462 Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Thu, 18 Aug 2022 18:17:39 +0800 Subject: [PATCH 252/366] code spell check (#5230) Signed-off-by: Lyndon-Li Signed-off-by: Lyndon-Li --- .github/workflows/pr-codespell.yml | 2 +- ...ugin-backup-and-restore-progress-design.md | 2 +- hack/release-tools/tag-release.sh | 2 +- .../alertmanagers.monitoring.coreos.com.json | 4 +-- ...searches.elasticsearch.k8s.elastic.co.json | 8 ++--- .../pprometheuses.monitoring.coreos.com.json | 4 +-- .../prometheuses.monitoring.coreos.com.json | 4 +-- .../alertmanagers.monitoring.coreos.com.json | 4 +-- ...searches.elasticsearch.k8s.elastic.co.json | 4 +-- .../prometheuses.monitoring.coreos.com.json | 4 +-- pkg/repository/udmrepo/repo-options.go | 32 +++++++++---------- test/e2e/e2e_suite_test.go | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/pr-codespell.yml b/.github/workflows/pr-codespell.yml index 22218f1f88..0305b84acb 100644 --- a/.github/workflows/pr-codespell.yml +++ b/.github/workflows/pr-codespell.yml @@ -14,7 +14,7 @@ jobs: uses: codespell-project/actions-codespell@master with: # ignore the config/.../crd.go file as it's generated binary data that is edited elswhere. - skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./go.sum + skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./go.sum,./LICENSE ignore_words_list: iam,aks,ist,bridget,ue check_filenames: true check_hidden: true diff --git a/design/Implemented/plugin-backup-and-restore-progress-design.md b/design/Implemented/plugin-backup-and-restore-progress-design.md index aa188f387b..dbbc67b40b 100644 --- a/design/Implemented/plugin-backup-and-restore-progress-design.md +++ b/design/Implemented/plugin-backup-and-restore-progress-design.md @@ -429,7 +429,7 @@ Instead, a new method for 'Progress' will be added to interface. Velero server r But, this involves good amount of changes and needs a way for backward compatibility. -As volume plugins are mostly K8s native, its fine to go ahead with current limiation. +As volume plugins are mostly K8s native, its fine to go ahead with current limitation. ### Update Backup CR Instead of creating new CRs, plugins can directly update the status of Backup CR. But, this deviates from current approach of having separate CRs like PodVolumeBackup/PodVolumeRestore to know operations progress. diff --git a/hack/release-tools/tag-release.sh b/hack/release-tools/tag-release.sh index 11a56887f8..4fbfa933e7 100755 --- a/hack/release-tools/tag-release.sh +++ b/hack/release-tools/tag-release.sh @@ -95,7 +95,7 @@ eval $(go run $DIR/chk_version.go) printf "To clarify, you've provided a version string of $VELERO_VERSION.\n" printf "Based on this, the following assumptions have been made: \n" -# $VELERO_PATCH gets populated by the chk_version.go scrip that parses and verifies the given version format +# $VELERO_PATCH gets populated by the chk_version.go script that parses and verifies the given version format # If we've got a patch release, we assume the tag is on release branch. if [[ "$VELERO_PATCH" != 0 ]]; then printf "*\t This is a patch release.\n" diff --git a/pkg/backup/testdata/v1/alertmanagers.monitoring.coreos.com.json b/pkg/backup/testdata/v1/alertmanagers.monitoring.coreos.com.json index 1077bc3cfd..bbbbe6e4d2 100644 --- a/pkg/backup/testdata/v1/alertmanagers.monitoring.coreos.com.json +++ b/pkg/backup/testdata/v1/alertmanagers.monitoring.coreos.com.json @@ -4,7 +4,7 @@ "metadata": { "annotations": { "helm.sh/hook": "crd-install", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"alertmanagers.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Alertmanager\",\"plural\":\"alertmanagers\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"AlertmanagerSpec is a specification of the desired behavior of the Alertmanager cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalPeers\":{\"description\":\"AdditionalPeers allows injecting a set of additional Alertmanagers to peer with to form a highly available cluster.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"]}}},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}}}},\"baseImage\":{\"description\":\"Base image that is used to deploy pods, without tag.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The ConfigMaps are mounted into /etc/alertmanager/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"configSecret\":{\"description\":\"ConfigSecret is the name of a Kubernetes Secret in the same namespace as the Alertmanager object, which contains configuration for this Alertmanager instance. Defaults to 'alertmanager-' The secret is mounted into /etc/alertmanager/config.\",\"type\":\"string\"},\"containers\":{\"description\":\"Containers allows injecting additional containers. This is meant to allow adding an authentication proxy to an Alertmanager pod.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"]},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"]},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]}}}},\"required\":[\"name\"]},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}}},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}}}}},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}}}},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"]},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}}}},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"]},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"]},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"externalUrl\":{\"description\":\"The external URL the Alertmanager instances will be available under. This is necessary to generate correct URLs. This is necessary if Alertmanager is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Alertmanager is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}}},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Alertmanager server listen on loopback, so that it does not bind against the Pod IP. Note this is only for the Alertmanager UI, not the gossip communication.\",\"type\":\"boolean\"},\"logLevel\":{\"description\":\"Log level for Alertmanager to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"If set to true all actions on the underlying managed objects are not goint to be performed, except for delete actions.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"replicas\":{\"description\":\"Size is the expected size of the alertmanager cluster. The controller will eventually make the size of the running cluster equal to the expected size.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"retention\":{\"description\":\"Time duration Alertmanager shall retain data for. Default is '120h', and must match the regular expression `[0-9]+(ms|s|m|h)` (milliseconds seconds minutes hours).\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Alertmanager registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The Secrets are mounted into /etc/alertmanager/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"}}},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"sha\":{\"description\":\"SHA of Alertmanager container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}}},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"]},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}}},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"]},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}}}}}}},\"tag\":{\"description\":\"Tag of Alertmanager container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}}},\"type\":\"array\"},\"version\":{\"description\":\"Version the cluster should be on.\",\"type\":\"string\"}}},\"status\":{\"description\":\"AlertmanagerStatus is the most recent observed status of the Alertmanager cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"]}}}},\"version\":\"v1\"}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"alertmanagers.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Alertmanager\",\"plural\":\"alertmanagers\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"AlertmanagerSpec is a specification of the desired behavior of the Alertmanager cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalPeers\":{\"description\":\"AdditionalPeers allows injecting a set of additional Alertmanagers to peer with to form a highly available cluster.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"]}}},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}}}},\"baseImage\":{\"description\":\"Base image that is used to deploy pods, without tag.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The ConfigMaps are mounted into /etc/alertmanager/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"configSecret\":{\"description\":\"ConfigSecret is the name of a Kubernetes Secret in the same namespace as the Alertmanager object, which contains configuration for this Alertmanager instance. Defaults to 'alertmanager-' The secret is mounted into /etc/alertmanager/config.\",\"type\":\"string\"},\"containers\":{\"description\":\"Containers allows injecting additional containers. This is meant to allow adding an authentication proxy to an Alertmanager pod.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"]},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"]},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]}}}},\"required\":[\"name\"]},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}}},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}}}}},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}}}},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"]},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}}}},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"]},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"]},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"externalUrl\":{\"description\":\"The external URL the Alertmanager instances will be available under. This is necessary to generate correct URLs. This is necessary if Alertmanager is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Alertmanager is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}}},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Alertmanager server listen on loopback, so that it does not bind against the Pod IP. Note this is only for the Alertmanager UI, not the gossip communication.\",\"type\":\"boolean\"},\"logLevel\":{\"description\":\"Log level for Alertmanager to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"If set to true all actions on the underlying managed objects are not goint to be performed, except for delete actions.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"replicas\":{\"description\":\"Size is the expected size of the alertmanager cluster. The controller will eventually make the size of the running cluster equal to the expected size.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"retention\":{\"description\":\"Time duration Alertmanager shall retain data for. Default is '120h', and must match the regular expression `[0-9]+(ms|s|m|h)` (milliseconds seconds minutes hours).\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Alertmanager registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The Secrets are mounted into /etc/alertmanager/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"}}},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"sha\":{\"description\":\"SHA of Alertmanager container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}}},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"]},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}}},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"]},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}}}}}}},\"tag\":{\"description\":\"Tag of Alertmanager container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}}},\"type\":\"array\"},\"version\":{\"description\":\"Version the cluster should be on.\",\"type\":\"string\"}}},\"status\":{\"description\":\"AlertmanagerStatus is the most recent observed status of the Alertmanager cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"]}}}},\"version\":\"v1\"}}\n" }, "creationTimestamp": "2020-05-05T16:51:39Z", "generation": 1, @@ -2040,7 +2040,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", diff --git a/pkg/backup/testdata/v1/elasticsearches.elasticsearch.k8s.elastic.co.json b/pkg/backup/testdata/v1/elasticsearches.elasticsearch.k8s.elastic.co.json index b6ac23bb7d..7c33bd593c 100644 --- a/pkg/backup/testdata/v1/elasticsearches.elasticsearch.k8s.elastic.co.json +++ b/pkg/backup/testdata/v1/elasticsearches.elasticsearch.k8s.elastic.co.json @@ -4,7 +4,7 @@ "metadata": { "annotations": { "controller-gen.kubebuilder.io/version": "v0.2.5", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.5\"},\"creationTimestamp\":null,\"name\":\"elasticsearches.elasticsearch.k8s.elastic.co\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".status.health\",\"name\":\"health\",\"type\":\"string\"},{\"JSONPath\":\".status.availableNodes\",\"description\":\"Available nodes\",\"name\":\"nodes\",\"type\":\"integer\"},{\"JSONPath\":\".spec.version\",\"description\":\"Elasticsearch version\",\"name\":\"version\",\"type\":\"string\"},{\"JSONPath\":\".status.phase\",\"name\":\"phase\",\"type\":\"string\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"age\",\"type\":\"date\"}],\"group\":\"elasticsearch.k8s.elastic.co\",\"names\":{\"categories\":[\"elastic\"],\"kind\":\"Elasticsearch\",\"listKind\":\"ElasticsearchList\",\"plural\":\"elasticsearches\",\"shortNames\":[\"es\"],\"singular\":\"elasticsearch\"},\"scope\":\"Namespaced\",\"subresources\":{\"status\":{}},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Elasticsearch represents an Elasticsearch resource in a Kubernetes cluster.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"ElasticsearchSpec holds the specification of an Elasticsearch cluster.\",\"properties\":{\"auth\":{\"description\":\"Auth contains user authentication and authorization security settings for Elasticsearch.\",\"properties\":{\"fileRealm\":{\"description\":\"FileRealm to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"FileRealmSource references users to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"roles\":{\"description\":\"Roles to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"RoleSource references roles to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"http\":{\"description\":\"HTTP holds HTTP layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tls\":{\"description\":\"TLS defines options for configuring TLS for HTTP.\",\"properties\":{\"certificate\":{\"description\":\"Certificate is a reference to a Kubernetes secret that contains the certificate and private key for enabling TLS. The referenced secret should contain the following: \\n - `ca.crt`: The certificate authority (optional). - `tls.crt`: The certificate (or a chain). - `tls.key`: The private key to the first certificate in the certificate chain.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"selfSignedCertificate\":{\"description\":\"SelfSignedCertificate allows configuring the self-signed certificate generated by the operator.\",\"properties\":{\"disabled\":{\"description\":\"Disabled indicates that the provisioning of the self-signed certificate should be disabled.\",\"type\":\"boolean\"},\"subjectAltNames\":{\"description\":\"SubjectAlternativeNames is a list of SANs to include in the generated HTTP TLS certificate.\",\"items\":{\"description\":\"SubjectAlternativeName represents a SAN entry in a x509 certificate.\",\"properties\":{\"dns\":{\"description\":\"DNS is the DNS name of the subject.\",\"type\":\"string\"},\"ip\":{\"description\":\"IP is the IP address of the subject.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image is the Elasticsearch Docker image to deploy.\",\"type\":\"string\"},\"nodeSets\":{\"description\":\"NodeSets allow specifying groups of Elasticsearch nodes sharing the same configuration and Pod templates. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-orchestration.html\",\"items\":{\"description\":\"NodeSet is the specification for a group of Elasticsearch nodes sharing the same configuration and a Pod template.\",\"properties\":{\"config\":{\"description\":\"Config holds the Elasticsearch configuration.\",\"type\":\"object\"},\"count\":{\"description\":\"Count of Elasticsearch nodes to deploy.\",\"format\":\"int32\",\"minimum\":1,\"type\":\"integer\"},\"name\":{\"description\":\"Name of this set of nodes. Becomes a part of the Elasticsearch node.name setting.\",\"maxLength\":23,\"pattern\":\"[a-zA-Z0-9-]+\",\"type\":\"string\"},\"podTemplate\":{\"description\":\"PodTemplate provides customisation options (labels, annotations, affinity rules, resource requests, and so on) for the Pods belonging to this NodeSet.\",\"type\":\"object\"},\"volumeClaimTemplates\":{\"description\":\"VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod in this NodeSet. Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate. Items defined here take precedence over any default claims added by the operator with the same name. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-volume-claim-templates.html\",\"items\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"count\",\"name\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"podDisruptionBudget\":{\"description\":\"PodDisruptionBudget provides access to the default pod disruption budget for the Elasticsearch cluster. The default budget selects all cluster pods and sets `maxUnavailable` to 1. To disable, set `PodDisruptionBudget` to the empty value (`{}` in YAML).\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the PDB. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the PDB.\",\"properties\":{\"maxUnavailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at most \\\"maxUnavailable\\\" pods selected by \\\"selector\\\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \\\"minAvailable\\\".\"},\"minAvailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at least \\\"minAvailable\\\" pods selected by \\\"selector\\\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \\\"100%\\\".\"},\"selector\":{\"description\":\"Label query over pods whose evictions are managed by the disruption budget.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"remoteClusters\":{\"description\":\"RemoteClusters enables you to establish uni-directional connections to a remote Elasticsearch cluster.\",\"items\":{\"description\":\"RemoteCluster declares a remote Elasticsearch cluster connection.\",\"properties\":{\"elasticsearchRef\":{\"description\":\"ElasticsearchRef is a reference to an Elasticsearch cluster running within the same k8s cluster.\",\"properties\":{\"name\":{\"description\":\"Name of the Kubernetes object.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of the Kubernetes object. If empty, defaults to the current namespace.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"name\":{\"description\":\"Name is the name of the remote cluster as it is set in the Elasticsearch settings. The name is expected to be unique for each remote clusters.\",\"minLength\":1,\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"secureSettings\":{\"description\":\"SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html\",\"items\":{\"description\":\"SecretSource defines a data source based on a Kubernetes Secret.\",\"properties\":{\"entries\":{\"description\":\"Entries define how to project each key-value pair in the secret to filesystem paths. If not defined, all keys will be projected to similarly named paths in the filesystem. If defined, only the specified keys will be projected to the corresponding paths.\",\"items\":{\"description\":\"KeyToPath defines how to map a key in a Secret object to a filesystem path.\",\"properties\":{\"key\":{\"description\":\"Key is the key contained in the secret.\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the relative file path to map the key to. Path must not be an absolute file path and must not contain any \\\"..\\\" components.\",\"type\":\"string\"}},\"required\":[\"key\"],\"type\":\"object\"},\"type\":\"array\"},\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"required\":[\"secretName\"],\"type\":\"object\"},\"type\":\"array\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace. Can only be used if ECK is enforcing RBAC on references.\",\"type\":\"string\"},\"transport\":{\"description\":\"Transport holds transport layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"updateStrategy\":{\"description\":\"UpdateStrategy specifies how updates to the cluster should be performed.\",\"properties\":{\"changeBudget\":{\"description\":\"ChangeBudget defines the constraints to consider when applying changes to the Elasticsearch cluster.\",\"properties\":{\"maxSurge\":{\"description\":\"MaxSurge is the maximum number of new pods that can be created exceeding the original number of pods defined in the specification. MaxSurge is only taken into consideration when scaling up. Setting a negative value will disable the restriction. Defaults to unbounded if not specified.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxUnavailable\":{\"description\":\"MaxUnavailable is the maximum number of pods that can be unavailable (not ready) during the update due to circumstances under the control of the operator. Setting a negative value will disable this restriction. Defaults to 1 if not specified.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"version\":{\"description\":\"Version of Elasticsearch.\",\"type\":\"string\"}},\"required\":[\"nodeSets\",\"version\"],\"type\":\"object\"},\"status\":{\"description\":\"ElasticsearchStatus defines the observed state of Elasticsearch\",\"properties\":{\"availableNodes\":{\"format\":\"int32\",\"type\":\"integer\"},\"health\":{\"description\":\"ElasticsearchHealth is the health of the cluster as returned by the health API.\",\"type\":\"string\"},\"phase\":{\"description\":\"ElasticsearchOrchestrationPhase is the phase Elasticsearch is in from the controller point of view.\",\"type\":\"string\"}},\"type\":\"object\"}}}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true},{\"name\":\"v1beta1\",\"served\":true,\"storage\":false},{\"name\":\"v1alpha1\",\"served\":false,\"storage\":false}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.5\"},\"creationTimestamp\":null,\"name\":\"elasticsearches.elasticsearch.k8s.elastic.co\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".status.health\",\"name\":\"health\",\"type\":\"string\"},{\"JSONPath\":\".status.availableNodes\",\"description\":\"Available nodes\",\"name\":\"nodes\",\"type\":\"integer\"},{\"JSONPath\":\".spec.version\",\"description\":\"Elasticsearch version\",\"name\":\"version\",\"type\":\"string\"},{\"JSONPath\":\".status.phase\",\"name\":\"phase\",\"type\":\"string\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"age\",\"type\":\"date\"}],\"group\":\"elasticsearch.k8s.elastic.co\",\"names\":{\"categories\":[\"elastic\"],\"kind\":\"Elasticsearch\",\"listKind\":\"ElasticsearchList\",\"plural\":\"elasticsearches\",\"shortNames\":[\"es\"],\"singular\":\"elasticsearch\"},\"scope\":\"Namespaced\",\"subresources\":{\"status\":{}},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Elasticsearch represents an Elasticsearch resource in a Kubernetes cluster.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"ElasticsearchSpec holds the specification of an Elasticsearch cluster.\",\"properties\":{\"auth\":{\"description\":\"Auth contains user authentication and authorization security settings for Elasticsearch.\",\"properties\":{\"fileRealm\":{\"description\":\"FileRealm to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"FileRealmSource references users to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"roles\":{\"description\":\"Roles to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"RoleSource references roles to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"http\":{\"description\":\"HTTP holds HTTP layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tls\":{\"description\":\"TLS defines options for configuring TLS for HTTP.\",\"properties\":{\"certificate\":{\"description\":\"Certificate is a reference to a Kubernetes secret that contains the certificate and private key for enabling TLS. The referenced secret should contain the following: \\n - `ca.crt`: The certificate authority (optional). - `tls.crt`: The certificate (or a chain). - `tls.key`: The private key to the first certificate in the certificate chain.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"selfSignedCertificate\":{\"description\":\"SelfSignedCertificate allows configuring the self-signed certificate generated by the operator.\",\"properties\":{\"disabled\":{\"description\":\"Disabled indicates that the provisioning of the self-signed certificate should be disabled.\",\"type\":\"boolean\"},\"subjectAltNames\":{\"description\":\"SubjectAlternativeNames is a list of SANs to include in the generated HTTP TLS certificate.\",\"items\":{\"description\":\"SubjectAlternativeName represents a SAN entry in a x509 certificate.\",\"properties\":{\"dns\":{\"description\":\"DNS is the DNS name of the subject.\",\"type\":\"string\"},\"ip\":{\"description\":\"IP is the IP address of the subject.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image is the Elasticsearch Docker image to deploy.\",\"type\":\"string\"},\"nodeSets\":{\"description\":\"NodeSets allow specifying groups of Elasticsearch nodes sharing the same configuration and Pod templates. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-orchestration.html\",\"items\":{\"description\":\"NodeSet is the specification for a group of Elasticsearch nodes sharing the same configuration and a Pod template.\",\"properties\":{\"config\":{\"description\":\"Config holds the Elasticsearch configuration.\",\"type\":\"object\"},\"count\":{\"description\":\"Count of Elasticsearch nodes to deploy.\",\"format\":\"int32\",\"minimum\":1,\"type\":\"integer\"},\"name\":{\"description\":\"Name of this set of nodes. Becomes a part of the Elasticsearch node.name setting.\",\"maxLength\":23,\"pattern\":\"[a-zA-Z0-9-]+\",\"type\":\"string\"},\"podTemplate\":{\"description\":\"PodTemplate provides customisation options (labels, annotations, affinity rules, resource requests, and so on) for the Pods belonging to this NodeSet.\",\"type\":\"object\"},\"volumeClaimTemplates\":{\"description\":\"VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod in this NodeSet. Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate. Items defined here take precedence over any default claims added by the operator with the same name. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-volume-claim-templates.html\",\"items\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"count\",\"name\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"podDisruptionBudget\":{\"description\":\"PodDisruptionBudget provides access to the default pod disruption budget for the Elasticsearch cluster. The default budget selects all cluster pods and sets `maxUnavailable` to 1. To disable, set `PodDisruptionBudget` to the empty value (`{}` in YAML).\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the PDB. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the PDB.\",\"properties\":{\"maxUnavailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at most \\\"maxUnavailable\\\" pods selected by \\\"selector\\\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \\\"minAvailable\\\".\"},\"minAvailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at least \\\"minAvailable\\\" pods selected by \\\"selector\\\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \\\"100%\\\".\"},\"selector\":{\"description\":\"Label query over pods whose evictions are managed by the disruption budget.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"remoteClusters\":{\"description\":\"RemoteClusters enables you to establish uni-directional connections to a remote Elasticsearch cluster.\",\"items\":{\"description\":\"RemoteCluster declares a remote Elasticsearch cluster connection.\",\"properties\":{\"elasticsearchRef\":{\"description\":\"ElasticsearchRef is a reference to an Elasticsearch cluster running within the same k8s cluster.\",\"properties\":{\"name\":{\"description\":\"Name of the Kubernetes object.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of the Kubernetes object. If empty, defaults to the current namespace.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"name\":{\"description\":\"Name is the name of the remote cluster as it is set in the Elasticsearch settings. The name is expected to be unique for each remote clusters.\",\"minLength\":1,\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"secureSettings\":{\"description\":\"SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html\",\"items\":{\"description\":\"SecretSource defines a data source based on a Kubernetes Secret.\",\"properties\":{\"entries\":{\"description\":\"Entries define how to project each key-value pair in the secret to filesystem paths. If not defined, all keys will be projected to similarly named paths in the filesystem. If defined, only the specified keys will be projected to the corresponding paths.\",\"items\":{\"description\":\"KeyToPath defines how to map a key in a Secret object to a filesystem path.\",\"properties\":{\"key\":{\"description\":\"Key is the key contained in the secret.\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the relative file path to map the key to. Path must not be an absolute file path and must not contain any \\\"..\\\" components.\",\"type\":\"string\"}},\"required\":[\"key\"],\"type\":\"object\"},\"type\":\"array\"},\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"required\":[\"secretName\"],\"type\":\"object\"},\"type\":\"array\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace. Can only be used if ECK is enforcing RBAC on references.\",\"type\":\"string\"},\"transport\":{\"description\":\"Transport holds transport layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"updateStrategy\":{\"description\":\"UpdateStrategy specifies how updates to the cluster should be performed.\",\"properties\":{\"changeBudget\":{\"description\":\"ChangeBudget defines the constraints to consider when applying changes to the Elasticsearch cluster.\",\"properties\":{\"maxSurge\":{\"description\":\"MaxSurge is the maximum number of new pods that can be created exceeding the original number of pods defined in the specification. MaxSurge is only taken into consideration when scaling up. Setting a negative value will disable the restriction. Defaults to unbounded if not specified.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxUnavailable\":{\"description\":\"MaxUnavailable is the maximum number of pods that can be unavailable (not ready) during the update due to circumstances under the control of the operator. Setting a negative value will disable this restriction. Defaults to 1 if not specified.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"version\":{\"description\":\"Version of Elasticsearch.\",\"type\":\"string\"}},\"required\":[\"nodeSets\",\"version\"],\"type\":\"object\"},\"status\":{\"description\":\"ElasticsearchStatus defines the observed state of Elasticsearch\",\"properties\":{\"availableNodes\":{\"format\":\"int32\",\"type\":\"integer\"},\"health\":{\"description\":\"ElasticsearchHealth is the health of the cluster as returned by the health API.\",\"type\":\"string\"},\"phase\":{\"description\":\"ElasticsearchOrchestrationPhase is the phase Elasticsearch is in from the controller point of view.\",\"type\":\"string\"}},\"type\":\"object\"}}}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true},{\"name\":\"v1beta1\",\"served\":true,\"storage\":false},{\"name\":\"v1alpha1\",\"served\":false,\"storage\":false}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" }, "creationTimestamp": "2020-04-28T23:31:51Z", "generation": 1, @@ -509,7 +509,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Last time we probed the condition.", @@ -1402,7 +1402,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Last time we probed the condition.", @@ -2295,7 +2295,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Last time we probed the condition.", diff --git a/pkg/backup/testdata/v1/pprometheuses.monitoring.coreos.com.json b/pkg/backup/testdata/v1/pprometheuses.monitoring.coreos.com.json index cda90b7f07..5f48ecd4be 100644 --- a/pkg/backup/testdata/v1/pprometheuses.monitoring.coreos.com.json +++ b/pkg/backup/testdata/v1/pprometheuses.monitoring.coreos.com.json @@ -4,7 +4,7 @@ "metadata": { "annotations": { "controller-gen.kubebuilder.io/version": "v0.2.4", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.4\"},\"creationTimestamp\":null,\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".spec.version\",\"description\":\"The version of Prometheus\",\"name\":\"Version\",\"type\":\"string\"},{\"JSONPath\":\".spec.replicas\",\"description\":\"The desired replicas number of Prometheuses\",\"name\":\"Replicas\",\"type\":\"integer\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"Age\",\"type\":\"date\"}],\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"listKind\":\"PrometheusList\",\"plural\":\"prometheuses\",\"singular\":\"prometheus\"},\"preserveUnknownFields\":false,\"scope\":\"Namespaced\",\"subresources\":{},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Prometheus defines a Prometheus deployment.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"Specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"AdditionalAlertManagerConfigs allows specifying a key of a Secret containing additional Prometheus AlertManager configurations. AlertManager configurations specified are appended to the configurations generated by the Prometheus Operator. Job configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config. As AlertManager configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible AlertManager configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"AdditionalAlertRelabelConfigs allows specifying a key of a Secret containing additional Prometheus alert relabel configurations. Alert relabel configurations specified are appended to the configurations generated by the Prometheus Operator. Alert relabel configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alert_relabel_configs. As alert relabel configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible alert relabel configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"AdditionalScrapeConfigs allows specifying a key of a Secret containing additional Prometheus scrape configurations. Scrape configurations specified are appended to the configurations generated by the Prometheus Operator. Job configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config. As scrape configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible scrape configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"If specified, the pod's scheduling constraints.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Describes node affinity scheduling rules for the pod.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A node selector term, associated with the corresponding weight.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"preference\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Required. A pod affinity term, associated with the corresponding weight.\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"podAffinityTerm\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Required. A pod affinity term, associated with the corresponding weight.\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"podAffinityTerm\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"Define details regarding alerting.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the Alertmanager API that Prometheus uses to send alerts. It can be \\\"v1\\\" or \\\"v2\\\".\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Port the Alertmanager API is exposed on.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for alertmanager connection.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"name\",\"namespace\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig allows specifying a host and auth methods to access apiserver. If left empty, Prometheus is assumed to run inside of the cluster and will discover API servers automatically and use the pod's CA certificate and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for accessing apiserver.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"arbitraryFSAccessThroughSMs\":{\"description\":\"ArbitraryFSAccessThroughSMs configures whether configuration based on a service monitor can access arbitrary files on the file system of the Prometheus container e.g. bearer token files.\",\"properties\":{\"deny\":{\"type\":\"boolean\"}},\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"Source for the environment variable's value. Cannot be used if value is not empty.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key of a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"Selects a key of a secret in the pod's namespace\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"The ConfigMap to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"The Secret to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Actions that the management system should take in response to container lifecycle events. Cannot be updated.\",\"properties\":{\"postStart\":{\"description\":\"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"startupProbe\":{\"description\":\"StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is a beta feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"devicePath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"disableCompaction\":{\"description\":\"Disable prometheus compaction.\",\"type\":\"boolean\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"enforcedNamespaceLabel\":{\"description\":\"EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert and metric that is user created. The label value will always be the namespace of the object that is being created.\",\"type\":\"string\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"ignoreNamespaceSelectors\":{\"description\":\"IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from the podmonitor and servicemonitor configs, and they will only discover endpoints within their current namespace. Defaults to false.\",\"type\":\"boolean\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"initContainers\":{\"description\":\"InitContainers allows adding initContainers to the pod definition. Those can be used to e.g. fetch secrets for injection into the Prometheus configuration from external sources. Any errors during the execution of an initContainer will lead to a restart of the Pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ Using initContainers for any use case other then secret fetching is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"Source for the environment variable's value. Cannot be used if value is not empty.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key of a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"Selects a key of a secret in the pod's namespace\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"The ConfigMap to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"The Secret to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Actions that the management system should take in response to container lifecycle events. Cannot be updated.\",\"properties\":{\"postStart\":{\"description\":\"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"startupProbe\":{\"description\":\"StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is a beta feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"devicePath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"overrideHonorLabels\":{\"description\":\"OverrideHonorLabels if set to true overrides all user configured honor_labels. If HonorLabels is set in ServiceMonitor or PodMonitor to true, this overrides honor_labels to false.\",\"type\":\"boolean\"},\"overrideHonorTimestamps\":{\"description\":\"OverrideHonorTimestamps allows to globally enforce honoring timestamps in all scrape configs.\",\"type\":\"boolean\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"PodMetadata configures Labels and Annotations which are propagated to the prometheus pods.\",\"properties\":{\"annotations\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"labels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"Namespaces to be selected for PodMonitor discovery. If nil, only check own namespace.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"*Experimental* PodMonitors to be selected for target discovery. *Deprecated:* if neither this nor serviceMonitorSelector are specified, configuration is unmanaged.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"portName\":{\"description\":\"Port name used for the pods and governing service. This defaults to web\",\"type\":\"string\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"queryLogFile\":{\"description\":\"QueryLogFile specifies the file to which PromQL queries are logged. Note that this location must be writable, and can be persisted using an attached volume. Alternatively, the location can be set to a stdout location such as `/dev/stdout` to log querie information to the default Prometheus log stream. This is only available in versions of Prometheus \\u003e= 2.16.0. For more details, see the Prometheus docs (https://prometheus.io/docs/guides/query-log/)\",\"type\":\"string\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth for the URL.\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"name\":{\"description\":\"The name of the remote read queue, must be unique if specified. The name is used in metrics and logging in order to differentiate read configurations. Only valid in Prometheus versions 2.15.0 and newer.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLS Config to use for remote read.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth for the URL.\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"name\":{\"description\":\"The name of the remote write queue, must be unique if specified. The name is used in metrics and logging in order to differentiate queues. Only valid in Prometheus versions 2.15.0 and newer.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows tuning of the remote write queue parameters.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for remote write.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. Default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"Define resources requests and limits for single Pods.\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"Namespaces to be selected for PrometheusRules discovery. If unspecified, only the same namespace as the Prometheus object is in is used.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A selector to select which PrometheusRules to mount for loading alerting/recording rules from. Until (excluding) Prometheus Operator v0.24.0 Prometheus Operator will migrate any legacy rule ConfigMaps to PrometheusRule custom resources selected by RuleSelector. Make sure it does not match any config maps that you do not want to be migrated.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments.\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"SecurityContext holds pod-level security attributes and common container settings. This defaults to the default PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: \\n 1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw---- \\n If unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"fsGroupChangePolicy\":{\"description\":\"fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are \\\"OnRootMismatch\\\" and \\\"Always\\\". If not specified defaults to \\\"Always\\\".\",\"type\":\"string\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"Namespaces to be selected for ServiceMonitor discovery. If nil, only check own namespace.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"ServiceMonitors to be selected for target discovery. *Deprecated:* if neither this nor podMonitorSelector are specified, configuration is unmanaged.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"Storage spec to specify how storage shall be used.\",\"properties\":{\"disableMountSubPath\":{\"description\":\"Deprecated: subPath usage will be disabled by default in a future release, this option will become unnecessary. DisableMountSubPath allows to remove any subPath usage in volume mounts.\",\"type\":\"boolean\"},\"emptyDir\":{\"description\":\"EmptyDirVolumeSource to be used by the Prometheus StatefulSets. If specified, used in place of any volumeClaimTemplate. More info: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{\"description\":\"Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"A PVC spec to be used by the Prometheus StatefulSets.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"EmbeddedMetadata contains metadata relevant to an EmbeddedResource.\",\"properties\":{\"annotations\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"labels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot - Beta) * An existing PVC (PersistentVolumeClaim) * An existing custom resource/object that implements data population (Alpha) In order to use VolumeSnapshot object types, the appropriate feature gate must be enabled (VolumeSnapshotDataSource or AnyVolumeDataSource) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the specified data source is not supported, the volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"Thanos configuration allows configuring various aspects of a Prometheus server in a Thanos environment. \\n This section is experimental, it may change significantly without deprecation notice in any release. \\n This is experimental and may change significantly without backward compatibility in any release.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"grpcServerTlsConfig\":{\"description\":\"GRPCServerTLSConfig configures the gRPC server from which Thanos Querier reads recorded rule data. Note: Currently only the CAFile, CertFile, and KeyFile fields are supported. Maps to the '--grpc-server-tls-*' CLI args.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Thanos sidecar listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"LogFormat for Thanos sidecar to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"LogLevel for Thanos sidecar to be configured with.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"ObjectStorageConfig configures object storage in Thanos.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources defines the resource requirements for the Thanos sidecar. If not provided, no requests/limits will be set\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tracingConfig\":{\"description\":\"TracingConfig configures tracing in Thanos. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"volumeMounts\":{\"description\":\"VolumeMounts allows configuration of additional VolumeMounts on the output StatefulSet definition. VolumeMounts specified will be appended to other VolumeMounts in the prometheus container, that are generated as a result of StorageSpec objects.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumes\":{\"description\":\"Volumes allows configuration of additional volumes on the output StatefulSet definition. Volumes specified will be appended to other volumes that are generated as a result of StorageSpec objects.\",\"items\":{\"description\":\"Volume represents a named volume in a pod that may be accessed by any container in the pod.\",\"properties\":{\"awsElasticBlockStore\":{\"description\":\"AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"partition\":{\"description\":\"The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \\\"1\\\". Similarly, the volume partition for /dev/sda is \\\"0\\\" (or you can leave the property empty).\",\"format\":\"int32\",\"type\":\"integer\"},\"readOnly\":{\"description\":\"Specify \\\"true\\\" to force and set the ReadOnly property in VolumeMounts to \\\"true\\\". If omitted, the default is \\\"false\\\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"type\":\"boolean\"},\"volumeID\":{\"description\":\"Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"azureDisk\":{\"description\":\"AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.\",\"properties\":{\"cachingMode\":{\"description\":\"Host Caching mode: None, Read Only, Read Write.\",\"type\":\"string\"},\"diskName\":{\"description\":\"The Name of the data disk in the blob storage\",\"type\":\"string\"},\"diskURI\":{\"description\":\"The URI the data disk in the blob storage\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"kind\":{\"description\":\"Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"}},\"required\":[\"diskName\",\"diskURI\"],\"type\":\"object\"},\"azureFile\":{\"description\":\"AzureFile represents an Azure File Service mount on the host and bind mount to the pod.\",\"properties\":{\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretName\":{\"description\":\"the name of secret that contains Azure Storage Account Name and Key\",\"type\":\"string\"},\"shareName\":{\"description\":\"Share Name\",\"type\":\"string\"}},\"required\":[\"secretName\",\"shareName\"],\"type\":\"object\"},\"cephfs\":{\"description\":\"CephFS represents a Ceph FS mount on the host that shares a pod's lifetime\",\"properties\":{\"monitors\":{\"description\":\"Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"path\":{\"description\":\"Optional: Used as the mounted root, rather than the full Ceph tree, default is /\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"boolean\"},\"secretFile\":{\"description\":\"Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"string\"},\"secretRef\":{\"description\":\"Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"user\":{\"description\":\"Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"string\"}},\"required\":[\"monitors\"],\"type\":\"object\"},\"cinder\":{\"description\":\"Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"Optional: points to a secret object containing parameters used to connect to OpenStack.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeID\":{\"description\":\"volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"configMap\":{\"description\":\"ConfigMap represents a configMap that should populate this volume\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its keys must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"csi\":{\"description\":\"CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).\",\"properties\":{\"driver\":{\"description\":\"Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.\",\"type\":\"string\"},\"nodePublishSecretRef\":{\"description\":\"NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"readOnly\":{\"description\":\"Specifies a read-only configuration for the volume. Defaults to false (read/write).\",\"type\":\"boolean\"},\"volumeAttributes\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.\",\"type\":\"object\"}},\"required\":[\"driver\"],\"type\":\"object\"},\"downwardAPI\":{\"description\":\"DownwardAPI represents downward API about the pod that should populate this volume\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"Items is a list of downward API volume file\",\"items\":{\"description\":\"DownwardAPIVolumeFile represents information to create the file containing the pod field\",\"properties\":{\"fieldRef\":{\"description\":\"Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'\",\"type\":\"string\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"}},\"required\":[\"path\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"emptyDir\":{\"description\":\"EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{\"description\":\"Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir\",\"type\":\"string\"}},\"type\":\"object\"},\"fc\":{\"description\":\"FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"lun\":{\"description\":\"Optional: FC target lun number\",\"format\":\"int32\",\"type\":\"integer\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"targetWWNs\":{\"description\":\"Optional: FC target worldwide names (WWNs)\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"wwids\":{\"description\":\"Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"flexVolume\":{\"description\":\"FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.\",\"properties\":{\"driver\":{\"description\":\"Driver is the name of the driver to use for this volume.\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". The default filesystem depends on FlexVolume script.\",\"type\":\"string\"},\"options\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Optional: Extra command options if any.\",\"type\":\"object\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"driver\"],\"type\":\"object\"},\"flocker\":{\"description\":\"Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running\",\"properties\":{\"datasetName\":{\"description\":\"Name of the dataset stored as metadata -\\u003e name on the dataset for Flocker should be considered as deprecated\",\"type\":\"string\"},\"datasetUUID\":{\"description\":\"UUID of the dataset. This is unique identifier of a Flocker dataset\",\"type\":\"string\"}},\"type\":\"object\"},\"gcePersistentDisk\":{\"description\":\"GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"partition\":{\"description\":\"The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \\\"1\\\". Similarly, the volume partition for /dev/sda is \\\"0\\\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"format\":\"int32\",\"type\":\"integer\"},\"pdName\":{\"description\":\"Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"type\":\"boolean\"}},\"required\":[\"pdName\"],\"type\":\"object\"},\"gitRepo\":{\"description\":\"GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.\",\"properties\":{\"directory\":{\"description\":\"Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.\",\"type\":\"string\"},\"repository\":{\"description\":\"Repository URL\",\"type\":\"string\"},\"revision\":{\"description\":\"Commit hash for the specified revision.\",\"type\":\"string\"}},\"required\":[\"repository\"],\"type\":\"object\"},\"glusterfs\":{\"description\":\"Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md\",\"properties\":{\"endpoints\":{\"description\":\"EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"boolean\"}},\"required\":[\"endpoints\",\"path\"],\"type\":\"object\"},\"hostPath\":{\"description\":\"HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.\",\"properties\":{\"path\":{\"description\":\"Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath\",\"type\":\"string\"},\"type\":{\"description\":\"Type for HostPath Volume Defaults to \\\"\\\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"},\"iscsi\":{\"description\":\"ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md\",\"properties\":{\"chapAuthDiscovery\":{\"description\":\"whether support iSCSI Discovery CHAP authentication\",\"type\":\"boolean\"},\"chapAuthSession\":{\"description\":\"whether support iSCSI Session CHAP authentication\",\"type\":\"boolean\"},\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"initiatorName\":{\"description\":\"Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \\u003ctarget portal\\u003e:\\u003cvolume name\\u003e will be created for the connection.\",\"type\":\"string\"},\"iqn\":{\"description\":\"Target iSCSI Qualified Name.\",\"type\":\"string\"},\"iscsiInterface\":{\"description\":\"iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).\",\"type\":\"string\"},\"lun\":{\"description\":\"iSCSI Target Lun number.\",\"format\":\"int32\",\"type\":\"integer\"},\"portals\":{\"description\":\"iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"CHAP Secret for iSCSI target and initiator authentication\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"targetPortal\":{\"description\":\"iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).\",\"type\":\"string\"}},\"required\":[\"iqn\",\"lun\",\"targetPortal\"],\"type\":\"object\"},\"name\":{\"description\":\"Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"nfs\":{\"description\":\"NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"properties\":{\"path\":{\"description\":\"Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"boolean\"},\"server\":{\"description\":\"Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"string\"}},\"required\":[\"path\",\"server\"],\"type\":\"object\"},\"persistentVolumeClaim\":{\"description\":\"PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"claimName\":{\"description\":\"ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Will force the ReadOnly setting in VolumeMounts. Default false.\",\"type\":\"boolean\"}},\"required\":[\"claimName\"],\"type\":\"object\"},\"photonPersistentDisk\":{\"description\":\"PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"pdID\":{\"description\":\"ID that identifies Photon Controller persistent disk\",\"type\":\"string\"}},\"required\":[\"pdID\"],\"type\":\"object\"},\"portworxVolume\":{\"description\":\"PortworxVolume represents a portworx volume attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"volumeID\":{\"description\":\"VolumeID uniquely identifies a Portworx volume\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"projected\":{\"description\":\"Items for all in one resources secrets, configmaps, and downward API\",\"properties\":{\"defaultMode\":{\"description\":\"Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"sources\":{\"description\":\"list of volume projections\",\"items\":{\"description\":\"Projection that may be projected along with other supported volume types\",\"properties\":{\"configMap\":{\"description\":\"information about the configMap data to project\",\"properties\":{\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its keys must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"downwardAPI\":{\"description\":\"information about the downwardAPI data to project\",\"properties\":{\"items\":{\"description\":\"Items is a list of DownwardAPIVolume file\",\"items\":{\"description\":\"DownwardAPIVolumeFile represents information to create the file containing the pod field\",\"properties\":{\"fieldRef\":{\"description\":\"Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'\",\"type\":\"string\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"}},\"required\":[\"path\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"secret\":{\"description\":\"information about the secret data to project\",\"properties\":{\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"serviceAccountToken\":{\"description\":\"information about the serviceAccountToken data to project\",\"properties\":{\"audience\":{\"description\":\"Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.\",\"type\":\"string\"},\"expirationSeconds\":{\"description\":\"ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.\",\"format\":\"int64\",\"type\":\"integer\"},\"path\":{\"description\":\"Path is the path relative to the mount point of the file to project the token into.\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"sources\"],\"type\":\"object\"},\"quobyte\":{\"description\":\"Quobyte represents a Quobyte mount on the host that shares a pod's lifetime\",\"properties\":{\"group\":{\"description\":\"Group to map volume access to Default is no group\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.\",\"type\":\"boolean\"},\"registry\":{\"description\":\"Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes\",\"type\":\"string\"},\"tenant\":{\"description\":\"Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin\",\"type\":\"string\"},\"user\":{\"description\":\"User to map volume access to Defaults to serivceaccount user\",\"type\":\"string\"},\"volume\":{\"description\":\"Volume is a string that references an already created Quobyte volume by name.\",\"type\":\"string\"}},\"required\":[\"registry\",\"volume\"],\"type\":\"object\"},\"rbd\":{\"description\":\"RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"image\":{\"description\":\"The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"keyring\":{\"description\":\"Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"monitors\":{\"description\":\"A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"pool\":{\"description\":\"The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"user\":{\"description\":\"The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"}},\"required\":[\"image\",\"monitors\"],\"type\":\"object\"},\"scaleIO\":{\"description\":\"ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Default is \\\"xfs\\\".\",\"type\":\"string\"},\"gateway\":{\"description\":\"The host address of the ScaleIO API Gateway.\",\"type\":\"string\"},\"protectionDomain\":{\"description\":\"The name of the ScaleIO Protection Domain for the configured storage.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"sslEnabled\":{\"description\":\"Flag to enable/disable SSL communication with Gateway, default false\",\"type\":\"boolean\"},\"storageMode\":{\"description\":\"Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.\",\"type\":\"string\"},\"storagePool\":{\"description\":\"The ScaleIO Storage Pool associated with the protection domain.\",\"type\":\"string\"},\"system\":{\"description\":\"The name of the storage system as configured in ScaleIO.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"The name of a volume already created in the ScaleIO system that is associated with this volume source.\",\"type\":\"string\"}},\"required\":[\"gateway\",\"secretRef\",\"system\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"optional\":{\"description\":\"Specify whether the Secret or its keys must be defined\",\"type\":\"boolean\"},\"secretName\":{\"description\":\"Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret\",\"type\":\"string\"}},\"type\":\"object\"},\"storageos\":{\"description\":\"StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeName\":{\"description\":\"VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.\",\"type\":\"string\"},\"volumeNamespace\":{\"description\":\"VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \\\"default\\\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.\",\"type\":\"string\"}},\"type\":\"object\"},\"vsphereVolume\":{\"description\":\"VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"storagePolicyID\":{\"description\":\"Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.\",\"type\":\"string\"},\"storagePolicyName\":{\"description\":\"Storage Policy Based Management (SPBM) profile name.\",\"type\":\"string\"},\"volumePath\":{\"description\":\"Path that identifies vSphere volume vmdk\",\"type\":\"string\"}},\"required\":[\"volumePath\"],\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy. This flag is only available in versions of Prometheus \\u003e= 2.11.0.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"Most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"availableReplicas\",\"paused\",\"replicas\",\"unavailableReplicas\",\"updatedReplicas\"],\"type\":\"object\"}},\"required\":[\"spec\"],\"type\":\"object\"}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.4\"},\"creationTimestamp\":null,\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".spec.version\",\"description\":\"The version of Prometheus\",\"name\":\"Version\",\"type\":\"string\"},{\"JSONPath\":\".spec.replicas\",\"description\":\"The desired replicas number of Prometheuses\",\"name\":\"Replicas\",\"type\":\"integer\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"Age\",\"type\":\"date\"}],\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"listKind\":\"PrometheusList\",\"plural\":\"prometheuses\",\"singular\":\"prometheus\"},\"preserveUnknownFields\":false,\"scope\":\"Namespaced\",\"subresources\":{},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Prometheus defines a Prometheus deployment.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"Specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"AdditionalAlertManagerConfigs allows specifying a key of a Secret containing additional Prometheus AlertManager configurations. AlertManager configurations specified are appended to the configurations generated by the Prometheus Operator. Job configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config. As AlertManager configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible AlertManager configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"AdditionalAlertRelabelConfigs allows specifying a key of a Secret containing additional Prometheus alert relabel configurations. Alert relabel configurations specified are appended to the configurations generated by the Prometheus Operator. Alert relabel configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alert_relabel_configs. As alert relabel configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible alert relabel configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"AdditionalScrapeConfigs allows specifying a key of a Secret containing additional Prometheus scrape configurations. Scrape configurations specified are appended to the configurations generated by the Prometheus Operator. Job configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config. As scrape configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible scrape configs are going to break Prometheus after the upgrade.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"If specified, the pod's scheduling constraints.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Describes node affinity scheduling rules for the pod.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A node selector term, associated with the corresponding weight.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"preference\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Required. A pod affinity term, associated with the corresponding weight.\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"podAffinityTerm\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Required. A pod affinity term, associated with the corresponding weight.\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"podAffinityTerm\",\"weight\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label query over a set of resources, in this case pods.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"Define details regarding alerting.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the Alertmanager API that Prometheus uses to send alerts. It can be \\\"v1\\\" or \\\"v2\\\".\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Port the Alertmanager API is exposed on.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for alertmanager connection.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"name\",\"namespace\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig allows specifying a host and auth methods to access apiserver. If left empty, Prometheus is assumed to run inside of the cluster and will discover API servers automatically and use the pod's CA certificate and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for accessing apiserver.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"arbitraryFSAccessThroughSMs\":{\"description\":\"ArbitraryFSAccessThroughSMs configures whether configuration based on a service monitor can access arbitrary files on the file system of the Prometheus container e.g. bearer token files.\",\"properties\":{\"deny\":{\"type\":\"boolean\"}},\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"Source for the environment variable's value. Cannot be used if value is not empty.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key of a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"Selects a key of a secret in the pod's namespace\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"The ConfigMap to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"The Secret to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Actions that the management system should take in response to container lifecycle events. Cannot be updated.\",\"properties\":{\"postStart\":{\"description\":\"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"startupProbe\":{\"description\":\"StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is a beta feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"devicePath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"disableCompaction\":{\"description\":\"Disable prometheus compaction.\",\"type\":\"boolean\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"enforcedNamespaceLabel\":{\"description\":\"EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert and metric that is user created. The label value will always be the namespace of the object that is being created.\",\"type\":\"string\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"ignoreNamespaceSelectors\":{\"description\":\"IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from the podmonitor and servicemonitor configs, and they will only discover endpoints within their current namespace. Defaults to false.\",\"type\":\"boolean\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"initContainers\":{\"description\":\"InitContainers allows adding initContainers to the pod definition. Those can be used to e.g. fetch secrets for injection into the Prometheus configuration from external sources. Any errors during the execution of an initContainer will lead to a restart of the Pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ Using initContainers for any use case other then secret fetching is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"Source for the environment variable's value. Cannot be used if value is not empty.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key of a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"Selects a key of a secret in the pod's namespace\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"The ConfigMap to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"The Secret to select from\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Actions that the management system should take in response to container lifecycle events. Cannot be updated.\",\"properties\":{\"postStart\":{\"description\":\"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"description\":\"Capability represent POSIX capabilities type\",\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"startupProbe\":{\"description\":\"StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is a beta feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"properties\":{\"exec\":{\"description\":\"One and only one of the following should be specified. Exec specifies the action to take.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGet specifies the http request to perform.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.\",\"x-kubernetes-int-or-string\":true}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"devicePath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"overrideHonorLabels\":{\"description\":\"OverrideHonorLabels if set to true overrides all user configured honor_labels. If HonorLabels is set in ServiceMonitor or PodMonitor to true, this overrides honor_labels to false.\",\"type\":\"boolean\"},\"overrideHonorTimestamps\":{\"description\":\"OverrideHonorTimestamps allows to globally enforce honoring timestamps in all scrape configs.\",\"type\":\"boolean\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"PodMetadata configures Labels and Annotations which are propagated to the prometheus pods.\",\"properties\":{\"annotations\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"labels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"Namespaces to be selected for PodMonitor discovery. If nil, only check own namespace.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"*Experimental* PodMonitors to be selected for target discovery. *Deprecated:* if neither this nor serviceMonitorSelector are specified, configuration is unmanaged.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"portName\":{\"description\":\"Port name used for the pods and governing service. This defaults to web\",\"type\":\"string\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"queryLogFile\":{\"description\":\"QueryLogFile specifies the file to which PromQL queries are logged. Note that this location must be writable, and can be persisted using an attached volume. Alternatively, the location can be set to a stdout location such as `/dev/stdout` to log querie information to the default Prometheus log stream. This is only available in versions of Prometheus \\u003e= 2.16.0. For more details, see the Prometheus docs (https://prometheus.io/docs/guides/query-log/)\",\"type\":\"string\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth for the URL.\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"name\":{\"description\":\"The name of the remote read queue, must be unique if specified. The name is used in metrics and logging in order to differentiate read configurations. Only valid in Prometheus versions 2.15.0 and newer.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLS Config to use for remote read.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth for the URL.\",\"properties\":{\"password\":{\"description\":\"The secret in the service monitor namespace that contains the password for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"The secret in the service monitor namespace that contains the username for authentication.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"name\":{\"description\":\"The name of the remote write queue, must be unique if specified. The name is used in metrics and logging in order to differentiate queues. Only valid in Prometheus versions 2.15.0 and newer.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows tuning of the remote write queue parameters.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLS Config to use for remote write.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. Default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"Define resources requests and limits for single Pods.\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"Namespaces to be selected for PrometheusRules discovery. If unspecified, only the same namespace as the Prometheus object is in is used.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A selector to select which PrometheusRules to mount for loading alerting/recording rules from. Until (excluding) Prometheus Operator v0.24.0 Prometheus Operator will migrate any legacy rule ConfigMaps to PrometheusRule custom resources selected by RuleSelector. Make sure it does not match any config maps that you do not want to be migrated.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments.\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"SecurityContext holds pod-level security attributes and common container settings. This defaults to the default PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: \\n 1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw---- \\n If unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"fsGroupChangePolicy\":{\"description\":\"fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are \\\"OnRootMismatch\\\" and \\\"Always\\\". If not specified defaults to \\\"Always\\\".\",\"type\":\"string\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"windowsOptions\":{\"description\":\"The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"properties\":{\"gmsaCredentialSpec\":{\"description\":\"GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.\",\"type\":\"string\"},\"gmsaCredentialSpecName\":{\"description\":\"GMSACredentialSpecName is the name of the GMSA credential spec to use.\",\"type\":\"string\"},\"runAsUserName\":{\"description\":\"The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"Namespaces to be selected for ServiceMonitor discovery. If nil, only check own namespace.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"ServiceMonitors to be selected for target discovery. *Deprecated:* if neither this nor podMonitorSelector are specified, configuration is unmanaged.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"Storage spec to specify how storage shall be used.\",\"properties\":{\"disableMountSubPath\":{\"description\":\"Deprecated: subPath usage will be disabled by default in a future release, this option will become unnecessary. DisableMountSubPath allows to remove any subPath usage in volume mounts.\",\"type\":\"boolean\"},\"emptyDir\":{\"description\":\"EmptyDirVolumeSource to be used by the Prometheus StatefulSets. If specified, used in place of any volumeClaimTemplate. More info: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{\"description\":\"Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"A PVC spec to be used by the Prometheus StatefulSets.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"EmbeddedMetadata contains metadata relevant to an EmbeddedResource.\",\"properties\":{\"annotations\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"labels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot - Beta) * An existing PVC (PersistentVolumeClaim) * An existing custom resource/object that implements data population (Alpha) In order to use VolumeSnapshot object types, the appropriate feature gate must be enabled (VolumeSnapshotDataSource or AnyVolumeDataSource) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the specified data source is not supported, the volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"Thanos configuration allows configuring various aspects of a Prometheus server in a Thanos environment. \\n This section is experimental, it may change significantly without deprecation notice in any release. \\n This is experimental and may change significantly without backward compatibility in any release.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"grpcServerTlsConfig\":{\"description\":\"GRPCServerTLSConfig configures the gRPC server from which Thanos Querier reads recorded rule data. Note: Currently only the CAFile, CertFile, and KeyFile fields are supported. Maps to the '--grpc-server-tls-*' CLI args.\",\"properties\":{\"ca\":{\"description\":\"Struct containing the CA cert to use for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"caFile\":{\"description\":\"Path to the CA cert in the Prometheus container to use for the targets.\",\"type\":\"string\"},\"cert\":{\"description\":\"Struct containing the client cert file for the targets.\",\"properties\":{\"configMap\":{\"description\":\"ConfigMap containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret containing data to use for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"certFile\":{\"description\":\"Path to the client cert file in the Prometheus container for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"Path to the client key file in the Prometheus container for the targets.\",\"type\":\"string\"},\"keySecret\":{\"description\":\"Secret containing the client key file for the targets.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Thanos sidecar listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"LogFormat for Thanos sidecar to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"LogLevel for Thanos sidecar to be configured with.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"ObjectStorageConfig configures object storage in Thanos.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources defines the resource requirements for the Thanos sidecar. If not provided, no requests/limits will be set\",\"properties\":{\"limits\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tracingConfig\":{\"description\":\"TracingConfig configures tracing in Thanos. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"volumeMounts\":{\"description\":\"VolumeMounts allows configuration of additional VolumeMounts on the output StatefulSet definition. VolumeMounts specified will be appended to other VolumeMounts in the prometheus container, that are generated as a result of StorageSpec objects.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive.\",\"type\":\"string\"}},\"required\":[\"mountPath\",\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"volumes\":{\"description\":\"Volumes allows configuration of additional volumes on the output StatefulSet definition. Volumes specified will be appended to other volumes that are generated as a result of StorageSpec objects.\",\"items\":{\"description\":\"Volume represents a named volume in a pod that may be accessed by any container in the pod.\",\"properties\":{\"awsElasticBlockStore\":{\"description\":\"AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"partition\":{\"description\":\"The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \\\"1\\\". Similarly, the volume partition for /dev/sda is \\\"0\\\" (or you can leave the property empty).\",\"format\":\"int32\",\"type\":\"integer\"},\"readOnly\":{\"description\":\"Specify \\\"true\\\" to force and set the ReadOnly property in VolumeMounts to \\\"true\\\". If omitted, the default is \\\"false\\\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"type\":\"boolean\"},\"volumeID\":{\"description\":\"Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"azureDisk\":{\"description\":\"AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.\",\"properties\":{\"cachingMode\":{\"description\":\"Host Caching mode: None, Read Only, Read Write.\",\"type\":\"string\"},\"diskName\":{\"description\":\"The Name of the data disk in the blob storage\",\"type\":\"string\"},\"diskURI\":{\"description\":\"The URI the data disk in the blob storage\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"kind\":{\"description\":\"Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"}},\"required\":[\"diskName\",\"diskURI\"],\"type\":\"object\"},\"azureFile\":{\"description\":\"AzureFile represents an Azure File Service mount on the host and bind mount to the pod.\",\"properties\":{\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretName\":{\"description\":\"the name of secret that contains Azure Storage Account Name and Key\",\"type\":\"string\"},\"shareName\":{\"description\":\"Share Name\",\"type\":\"string\"}},\"required\":[\"secretName\",\"shareName\"],\"type\":\"object\"},\"cephfs\":{\"description\":\"CephFS represents a Ceph FS mount on the host that shares a pod's lifetime\",\"properties\":{\"monitors\":{\"description\":\"Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"path\":{\"description\":\"Optional: Used as the mounted root, rather than the full Ceph tree, default is /\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"boolean\"},\"secretFile\":{\"description\":\"Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"string\"},\"secretRef\":{\"description\":\"Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"user\":{\"description\":\"Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it\",\"type\":\"string\"}},\"required\":[\"monitors\"],\"type\":\"object\"},\"cinder\":{\"description\":\"Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"Optional: points to a secret object containing parameters used to connect to OpenStack.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeID\":{\"description\":\"volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"configMap\":{\"description\":\"ConfigMap represents a configMap that should populate this volume\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its keys must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"csi\":{\"description\":\"CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).\",\"properties\":{\"driver\":{\"description\":\"Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.\",\"type\":\"string\"},\"nodePublishSecretRef\":{\"description\":\"NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"readOnly\":{\"description\":\"Specifies a read-only configuration for the volume. Defaults to false (read/write).\",\"type\":\"boolean\"},\"volumeAttributes\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.\",\"type\":\"object\"}},\"required\":[\"driver\"],\"type\":\"object\"},\"downwardAPI\":{\"description\":\"DownwardAPI represents downward API about the pod that should populate this volume\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"Items is a list of downward API volume file\",\"items\":{\"description\":\"DownwardAPIVolumeFile represents information to create the file containing the pod field\",\"properties\":{\"fieldRef\":{\"description\":\"Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'\",\"type\":\"string\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"}},\"required\":[\"path\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"emptyDir\":{\"description\":\"EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{\"description\":\"Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir\",\"type\":\"string\"}},\"type\":\"object\"},\"fc\":{\"description\":\"FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"lun\":{\"description\":\"Optional: FC target lun number\",\"format\":\"int32\",\"type\":\"integer\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"targetWWNs\":{\"description\":\"Optional: FC target worldwide names (WWNs)\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"wwids\":{\"description\":\"Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"flexVolume\":{\"description\":\"FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.\",\"properties\":{\"driver\":{\"description\":\"Driver is the name of the driver to use for this volume.\",\"type\":\"string\"},\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". The default filesystem depends on FlexVolume script.\",\"type\":\"string\"},\"options\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Optional: Extra command options if any.\",\"type\":\"object\"},\"readOnly\":{\"description\":\"Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"driver\"],\"type\":\"object\"},\"flocker\":{\"description\":\"Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running\",\"properties\":{\"datasetName\":{\"description\":\"Name of the dataset stored as metadata -\\u003e name on the dataset for Flocker should be considered as deprecated\",\"type\":\"string\"},\"datasetUUID\":{\"description\":\"UUID of the dataset. This is unique identifier of a Flocker dataset\",\"type\":\"string\"}},\"type\":\"object\"},\"gcePersistentDisk\":{\"description\":\"GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"partition\":{\"description\":\"The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \\\"1\\\". Similarly, the volume partition for /dev/sda is \\\"0\\\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"format\":\"int32\",\"type\":\"integer\"},\"pdName\":{\"description\":\"Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\",\"type\":\"boolean\"}},\"required\":[\"pdName\"],\"type\":\"object\"},\"gitRepo\":{\"description\":\"GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.\",\"properties\":{\"directory\":{\"description\":\"Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.\",\"type\":\"string\"},\"repository\":{\"description\":\"Repository URL\",\"type\":\"string\"},\"revision\":{\"description\":\"Commit hash for the specified revision.\",\"type\":\"string\"}},\"required\":[\"repository\"],\"type\":\"object\"},\"glusterfs\":{\"description\":\"Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md\",\"properties\":{\"endpoints\":{\"description\":\"EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod\",\"type\":\"boolean\"}},\"required\":[\"endpoints\",\"path\"],\"type\":\"object\"},\"hostPath\":{\"description\":\"HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.\",\"properties\":{\"path\":{\"description\":\"Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath\",\"type\":\"string\"},\"type\":{\"description\":\"Type for HostPath Volume Defaults to \\\"\\\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"},\"iscsi\":{\"description\":\"ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md\",\"properties\":{\"chapAuthDiscovery\":{\"description\":\"whether support iSCSI Discovery CHAP authentication\",\"type\":\"boolean\"},\"chapAuthSession\":{\"description\":\"whether support iSCSI Session CHAP authentication\",\"type\":\"boolean\"},\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"initiatorName\":{\"description\":\"Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \\u003ctarget portal\\u003e:\\u003cvolume name\\u003e will be created for the connection.\",\"type\":\"string\"},\"iqn\":{\"description\":\"Target iSCSI Qualified Name.\",\"type\":\"string\"},\"iscsiInterface\":{\"description\":\"iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).\",\"type\":\"string\"},\"lun\":{\"description\":\"iSCSI Target Lun number.\",\"format\":\"int32\",\"type\":\"integer\"},\"portals\":{\"description\":\"iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"CHAP Secret for iSCSI target and initiator authentication\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"targetPortal\":{\"description\":\"iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).\",\"type\":\"string\"}},\"required\":[\"iqn\",\"lun\",\"targetPortal\"],\"type\":\"object\"},\"name\":{\"description\":\"Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"nfs\":{\"description\":\"NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"properties\":{\"path\":{\"description\":\"Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"boolean\"},\"server\":{\"description\":\"Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs\",\"type\":\"string\"}},\"required\":[\"path\",\"server\"],\"type\":\"object\"},\"persistentVolumeClaim\":{\"description\":\"PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"claimName\":{\"description\":\"ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Will force the ReadOnly setting in VolumeMounts. Default false.\",\"type\":\"boolean\"}},\"required\":[\"claimName\"],\"type\":\"object\"},\"photonPersistentDisk\":{\"description\":\"PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"pdID\":{\"description\":\"ID that identifies Photon Controller persistent disk\",\"type\":\"string\"}},\"required\":[\"pdID\"],\"type\":\"object\"},\"portworxVolume\":{\"description\":\"PortworxVolume represents a portworx volume attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"volumeID\":{\"description\":\"VolumeID uniquely identifies a Portworx volume\",\"type\":\"string\"}},\"required\":[\"volumeID\"],\"type\":\"object\"},\"projected\":{\"description\":\"Items for all in one resources secrets, configmaps, and downward API\",\"properties\":{\"defaultMode\":{\"description\":\"Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"sources\":{\"description\":\"list of volume projections\",\"items\":{\"description\":\"Projection that may be projected along with other supported volume types\",\"properties\":{\"configMap\":{\"description\":\"information about the configMap data to project\",\"properties\":{\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or its keys must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"downwardAPI\":{\"description\":\"information about the downwardAPI data to project\",\"properties\":{\"items\":{\"description\":\"Items is a list of DownwardAPIVolume file\",\"items\":{\"description\":\"DownwardAPIVolumeFile represents information to create the file containing the pod field\",\"properties\":{\"fieldRef\":{\"description\":\"Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'\",\"type\":\"string\"},\"resourceFieldRef\":{\"description\":\"Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{\"description\":\"Specifies the output format of the exposed resources, defaults to \\\"1\\\"\",\"type\":\"string\"},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"}},\"required\":[\"path\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"secret\":{\"description\":\"information about the secret data to project\",\"properties\":{\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or its key must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"serviceAccountToken\":{\"description\":\"information about the serviceAccountToken data to project\",\"properties\":{\"audience\":{\"description\":\"Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.\",\"type\":\"string\"},\"expirationSeconds\":{\"description\":\"ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.\",\"format\":\"int64\",\"type\":\"integer\"},\"path\":{\"description\":\"Path is the path relative to the mount point of the file to project the token into.\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"sources\"],\"type\":\"object\"},\"quobyte\":{\"description\":\"Quobyte represents a Quobyte mount on the host that shares a pod's lifetime\",\"properties\":{\"group\":{\"description\":\"Group to map volume access to Default is no group\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.\",\"type\":\"boolean\"},\"registry\":{\"description\":\"Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes\",\"type\":\"string\"},\"tenant\":{\"description\":\"Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin\",\"type\":\"string\"},\"user\":{\"description\":\"User to map volume access to Defaults to serivceaccount user\",\"type\":\"string\"},\"volume\":{\"description\":\"Volume is a string that references an already created Quobyte volume by name.\",\"type\":\"string\"}},\"required\":[\"registry\",\"volume\"],\"type\":\"object\"},\"rbd\":{\"description\":\"RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine\",\"type\":\"string\"},\"image\":{\"description\":\"The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"keyring\":{\"description\":\"Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"monitors\":{\"description\":\"A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"pool\":{\"description\":\"The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"},\"readOnly\":{\"description\":\"ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"user\":{\"description\":\"The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it\",\"type\":\"string\"}},\"required\":[\"image\",\"monitors\"],\"type\":\"object\"},\"scaleIO\":{\"description\":\"ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Default is \\\"xfs\\\".\",\"type\":\"string\"},\"gateway\":{\"description\":\"The host address of the ScaleIO API Gateway.\",\"type\":\"string\"},\"protectionDomain\":{\"description\":\"The name of the ScaleIO Protection Domain for the configured storage.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"sslEnabled\":{\"description\":\"Flag to enable/disable SSL communication with Gateway, default false\",\"type\":\"boolean\"},\"storageMode\":{\"description\":\"Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.\",\"type\":\"string\"},\"storagePool\":{\"description\":\"The ScaleIO Storage Pool associated with the protection domain.\",\"type\":\"string\"},\"system\":{\"description\":\"The name of the storage system as configured in ScaleIO.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"The name of a volume already created in the ScaleIO system that is associated with this volume source.\",\"type\":\"string\"}},\"required\":[\"gateway\",\"secretRef\",\"system\"],\"type\":\"object\"},\"secret\":{\"description\":\"Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret\",\"properties\":{\"defaultMode\":{\"description\":\"Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"items\":{\"description\":\"If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.\",\"items\":{\"description\":\"Maps a string key to a path within a volume.\",\"properties\":{\"key\":{\"description\":\"The key to project.\",\"type\":\"string\"},\"mode\":{\"description\":\"Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.\",\"format\":\"int32\",\"type\":\"integer\"},\"path\":{\"description\":\"The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.\",\"type\":\"string\"}},\"required\":[\"key\",\"path\"],\"type\":\"object\"},\"type\":\"array\"},\"optional\":{\"description\":\"Specify whether the Secret or its keys must be defined\",\"type\":\"boolean\"},\"secretName\":{\"description\":\"Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret\",\"type\":\"string\"}},\"type\":\"object\"},\"storageos\":{\"description\":\"StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.\",\"type\":\"boolean\"},\"secretRef\":{\"description\":\"SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?\",\"type\":\"string\"}},\"type\":\"object\"},\"volumeName\":{\"description\":\"VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.\",\"type\":\"string\"},\"volumeNamespace\":{\"description\":\"VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \\\"default\\\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.\",\"type\":\"string\"}},\"type\":\"object\"},\"vsphereVolume\":{\"description\":\"VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine\",\"properties\":{\"fsType\":{\"description\":\"Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \\\"ext4\\\", \\\"xfs\\\", \\\"ntfs\\\". Implicitly inferred to be \\\"ext4\\\" if unspecified.\",\"type\":\"string\"},\"storagePolicyID\":{\"description\":\"Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.\",\"type\":\"string\"},\"storagePolicyName\":{\"description\":\"Storage Policy Based Management (SPBM) profile name.\",\"type\":\"string\"},\"volumePath\":{\"description\":\"Path that identifies vSphere volume vmdk\",\"type\":\"string\"}},\"required\":[\"volumePath\"],\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy. This flag is only available in versions of Prometheus \\u003e= 2.11.0.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"Most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"availableReplicas\",\"paused\",\"replicas\",\"unavailableReplicas\",\"updatedReplicas\"],\"type\":\"object\"}},\"required\":[\"spec\"],\"type\":\"object\"}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" }, "creationTimestamp": "2020-05-04T19:47:40Z", "generation": 1, @@ -4354,7 +4354,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Last time we probed the condition.", diff --git a/pkg/backup/testdata/v1/prometheuses.monitoring.coreos.com.json b/pkg/backup/testdata/v1/prometheuses.monitoring.coreos.com.json index befdf5ce38..0d0d6ae69c 100644 --- a/pkg/backup/testdata/v1/prometheuses.monitoring.coreos.com.json +++ b/pkg/backup/testdata/v1/prometheuses.monitoring.coreos.com.json @@ -4,7 +4,7 @@ "metadata": { "annotations": { "helm.sh/hook": "crd-install", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"plural\":\"prometheuses\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"PrometheusSpec is a specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"AlertingSpec defines parameters for alerting configuration of Prometheus servers.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"namespace\",\"name\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig defines a host and auth methods to access apiserver. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\n\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\n\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is alpha in 1.14.\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows the tuning of remote_write queue_config parameters. This object is referenced in the RemoteWriteSpec object.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\n\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"ThanosSpec defines parameters for a Prometheus server within a Thanos deployment.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"PrometheusStatus is the most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"],\"type\":\"object\"}},\"type\":\"object\"}},\"version\":\"v1\"}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"plural\":\"prometheuses\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"PrometheusSpec is a specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"AlertingSpec defines parameters for alerting configuration of Prometheus servers.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"namespace\",\"name\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig defines a host and auth methods to access apiserver. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\n\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\n\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is alpha in 1.14.\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows the tuning of remote_write queue_config parameters. This object is referenced in the RemoteWriteSpec object.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\n\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"ThanosSpec defines parameters for a Prometheus server within a Thanos deployment.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"PrometheusStatus is the most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"],\"type\":\"object\"}},\"type\":\"object\"}},\"version\":\"v1\"}}\n" }, "creationTimestamp": "2020-05-05T16:58:10Z", "generation": 1, @@ -3045,7 +3045,7 @@ "conditions": { "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "properties": { "lastProbeTime": { "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", diff --git a/pkg/backup/testdata/v1beta1/alertmanagers.monitoring.coreos.com.json b/pkg/backup/testdata/v1beta1/alertmanagers.monitoring.coreos.com.json index fdfb30ee49..8f8ac2caa6 100644 --- a/pkg/backup/testdata/v1beta1/alertmanagers.monitoring.coreos.com.json +++ b/pkg/backup/testdata/v1beta1/alertmanagers.monitoring.coreos.com.json @@ -13,7 +13,7 @@ }, "annotations": { "helm.sh/hook": "crd-install", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"alertmanagers.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Alertmanager\",\"plural\":\"alertmanagers\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"AlertmanagerSpec is a specification of the desired behavior of the Alertmanager cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalPeers\":{\"description\":\"AdditionalPeers allows injecting a set of additional Alertmanagers to peer with to form a highly available cluster.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"]}}},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}}}},\"baseImage\":{\"description\":\"Base image that is used to deploy pods, without tag.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The ConfigMaps are mounted into /etc/alertmanager/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"configSecret\":{\"description\":\"ConfigSecret is the name of a Kubernetes Secret in the same namespace as the Alertmanager object, which contains configuration for this Alertmanager instance. Defaults to 'alertmanager-' The secret is mounted into /etc/alertmanager/config.\",\"type\":\"string\"},\"containers\":{\"description\":\"Containers allows injecting additional containers. This is meant to allow adding an authentication proxy to an Alertmanager pod.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"]},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"]},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]}}}},\"required\":[\"name\"]},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}}},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}}}}},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}}}},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"]},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}}}},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"]},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"]},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"externalUrl\":{\"description\":\"The external URL the Alertmanager instances will be available under. This is necessary to generate correct URLs. This is necessary if Alertmanager is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Alertmanager is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}}},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Alertmanager server listen on loopback, so that it does not bind against the Pod IP. Note this is only for the Alertmanager UI, not the gossip communication.\",\"type\":\"boolean\"},\"logLevel\":{\"description\":\"Log level for Alertmanager to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"If set to true all actions on the underlying managed objects are not goint to be performed, except for delete actions.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"replicas\":{\"description\":\"Size is the expected size of the alertmanager cluster. The controller will eventually make the size of the running cluster equal to the expected size.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"retention\":{\"description\":\"Time duration Alertmanager shall retain data for. Default is '120h', and must match the regular expression `[0-9]+(ms|s|m|h)` (milliseconds seconds minutes hours).\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Alertmanager registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The Secrets are mounted into /etc/alertmanager/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"}}},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"sha\":{\"description\":\"SHA of Alertmanager container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}}},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"]},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}}},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"]},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}}}}}}},\"tag\":{\"description\":\"Tag of Alertmanager container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}}},\"type\":\"array\"},\"version\":{\"description\":\"Version the cluster should be on.\",\"type\":\"string\"}}},\"status\":{\"description\":\"AlertmanagerStatus is the most recent observed status of the Alertmanager cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"]}}}},\"version\":\"v1\"}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"alertmanagers.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Alertmanager\",\"plural\":\"alertmanagers\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"AlertmanagerSpec is a specification of the desired behavior of the Alertmanager cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalPeers\":{\"description\":\"AdditionalPeers allows injecting a set of additional Alertmanagers to peer with to form a highly available cluster.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"}}},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"]}}},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"]},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"]},\"type\":\"array\"}}}}},\"baseImage\":{\"description\":\"Base image that is used to deploy pods, without tag.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The ConfigMaps are mounted into /etc/alertmanager/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"configSecret\":{\"description\":\"ConfigSecret is the name of a Kubernetes Secret in the same namespace as the Alertmanager object, which contains configuration for this Alertmanager instance. Defaults to 'alertmanager-' The secret is mounted into /etc/alertmanager/config.\",\"type\":\"string\"},\"containers\":{\"description\":\"Containers allows injecting additional containers. This is meant to allow adding an authentication proxy to an Alertmanager pod.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"]},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"]},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"]}}}},\"required\":[\"name\"]},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}}},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}}}}},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]}}}}},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"]},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"]},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"]},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}}},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}}},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}}}},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"]},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"]},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"externalUrl\":{\"description\":\"The external URL the Alertmanager instances will be available under. This is necessary to generate correct URLs. This is necessary if Alertmanager is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Alertmanager is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}}},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Alertmanager server listen on loopback, so that it does not bind against the Pod IP. Note this is only for the Alertmanager UI, not the gossip communication.\",\"type\":\"boolean\"},\"logLevel\":{\"description\":\"Log level for Alertmanager to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"If set to true all actions on the underlying managed objects are not goint to be performed, except for delete actions.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"replicas\":{\"description\":\"Size is the expected size of the alertmanager cluster. The controller will eventually make the size of the running cluster equal to the expected size.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"retention\":{\"description\":\"Time duration Alertmanager shall retain data for. Default is '120h', and must match the regular expression `[0-9]+(ms|s|m|h)` (milliseconds seconds minutes hours).\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Alertmanager registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Alertmanager object, which shall be mounted into the Alertmanager Pods. The Secrets are mounted into /etc/alertmanager/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}}},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"]},\"type\":\"array\"}}},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"sha\":{\"description\":\"SHA of Alertmanager container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}}},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"]},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}}},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}}},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}}}},\"required\":[\"pending\"]},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"]},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}}},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"]},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}}},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"]},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}}},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}}},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"]},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}}}}}}},\"tag\":{\"description\":\"Tag of Alertmanager container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}}},\"type\":\"array\"},\"version\":{\"description\":\"Version the cluster should be on.\",\"type\":\"string\"}}},\"status\":{\"description\":\"AlertmanagerStatus is the most recent observed status of the Alertmanager cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Alertmanager cluster.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Alertmanager cluster that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"]}}}},\"version\":\"v1\"}}\n" } }, "spec": { @@ -2035,7 +2035,7 @@ "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "type": "array", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "required": [ "type", "status" diff --git a/pkg/backup/testdata/v1beta1/elasticsearches.elasticsearch.k8s.elastic.co.json b/pkg/backup/testdata/v1beta1/elasticsearches.elasticsearch.k8s.elastic.co.json index 6cdd7ca8f6..b42f1c9a19 100644 --- a/pkg/backup/testdata/v1beta1/elasticsearches.elasticsearch.k8s.elastic.co.json +++ b/pkg/backup/testdata/v1beta1/elasticsearches.elasticsearch.k8s.elastic.co.json @@ -14,7 +14,7 @@ }, "annotations": { "controller-gen.kubebuilder.io/version": "v0.2.5", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.5\"},\"creationTimestamp\":null,\"name\":\"elasticsearches.elasticsearch.k8s.elastic.co\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".status.health\",\"name\":\"health\",\"type\":\"string\"},{\"JSONPath\":\".status.availableNodes\",\"description\":\"Available nodes\",\"name\":\"nodes\",\"type\":\"integer\"},{\"JSONPath\":\".spec.version\",\"description\":\"Elasticsearch version\",\"name\":\"version\",\"type\":\"string\"},{\"JSONPath\":\".status.phase\",\"name\":\"phase\",\"type\":\"string\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"age\",\"type\":\"date\"}],\"group\":\"elasticsearch.k8s.elastic.co\",\"names\":{\"categories\":[\"elastic\"],\"kind\":\"Elasticsearch\",\"listKind\":\"ElasticsearchList\",\"plural\":\"elasticsearches\",\"shortNames\":[\"es\"],\"singular\":\"elasticsearch\"},\"scope\":\"Namespaced\",\"subresources\":{\"status\":{}},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Elasticsearch represents an Elasticsearch resource in a Kubernetes cluster.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"ElasticsearchSpec holds the specification of an Elasticsearch cluster.\",\"properties\":{\"auth\":{\"description\":\"Auth contains user authentication and authorization security settings for Elasticsearch.\",\"properties\":{\"fileRealm\":{\"description\":\"FileRealm to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"FileRealmSource references users to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"roles\":{\"description\":\"Roles to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"RoleSource references roles to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"http\":{\"description\":\"HTTP holds HTTP layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tls\":{\"description\":\"TLS defines options for configuring TLS for HTTP.\",\"properties\":{\"certificate\":{\"description\":\"Certificate is a reference to a Kubernetes secret that contains the certificate and private key for enabling TLS. The referenced secret should contain the following: \\n - `ca.crt`: The certificate authority (optional). - `tls.crt`: The certificate (or a chain). - `tls.key`: The private key to the first certificate in the certificate chain.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"selfSignedCertificate\":{\"description\":\"SelfSignedCertificate allows configuring the self-signed certificate generated by the operator.\",\"properties\":{\"disabled\":{\"description\":\"Disabled indicates that the provisioning of the self-signed certificate should be disabled.\",\"type\":\"boolean\"},\"subjectAltNames\":{\"description\":\"SubjectAlternativeNames is a list of SANs to include in the generated HTTP TLS certificate.\",\"items\":{\"description\":\"SubjectAlternativeName represents a SAN entry in a x509 certificate.\",\"properties\":{\"dns\":{\"description\":\"DNS is the DNS name of the subject.\",\"type\":\"string\"},\"ip\":{\"description\":\"IP is the IP address of the subject.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image is the Elasticsearch Docker image to deploy.\",\"type\":\"string\"},\"nodeSets\":{\"description\":\"NodeSets allow specifying groups of Elasticsearch nodes sharing the same configuration and Pod templates. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-orchestration.html\",\"items\":{\"description\":\"NodeSet is the specification for a group of Elasticsearch nodes sharing the same configuration and a Pod template.\",\"properties\":{\"config\":{\"description\":\"Config holds the Elasticsearch configuration.\",\"type\":\"object\"},\"count\":{\"description\":\"Count of Elasticsearch nodes to deploy.\",\"format\":\"int32\",\"minimum\":1,\"type\":\"integer\"},\"name\":{\"description\":\"Name of this set of nodes. Becomes a part of the Elasticsearch node.name setting.\",\"maxLength\":23,\"pattern\":\"[a-zA-Z0-9-]+\",\"type\":\"string\"},\"podTemplate\":{\"description\":\"PodTemplate provides customisation options (labels, annotations, affinity rules, resource requests, and so on) for the Pods belonging to this NodeSet.\",\"type\":\"object\"},\"volumeClaimTemplates\":{\"description\":\"VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod in this NodeSet. Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate. Items defined here take precedence over any default claims added by the operator with the same name. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-volume-claim-templates.html\",\"items\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"count\",\"name\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"podDisruptionBudget\":{\"description\":\"PodDisruptionBudget provides access to the default pod disruption budget for the Elasticsearch cluster. The default budget selects all cluster pods and sets `maxUnavailable` to 1. To disable, set `PodDisruptionBudget` to the empty value (`{}` in YAML).\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the PDB. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the PDB.\",\"properties\":{\"maxUnavailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at most \\\"maxUnavailable\\\" pods selected by \\\"selector\\\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \\\"minAvailable\\\".\"},\"minAvailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at least \\\"minAvailable\\\" pods selected by \\\"selector\\\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \\\"100%\\\".\"},\"selector\":{\"description\":\"Label query over pods whose evictions are managed by the disruption budget.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"remoteClusters\":{\"description\":\"RemoteClusters enables you to establish uni-directional connections to a remote Elasticsearch cluster.\",\"items\":{\"description\":\"RemoteCluster declares a remote Elasticsearch cluster connection.\",\"properties\":{\"elasticsearchRef\":{\"description\":\"ElasticsearchRef is a reference to an Elasticsearch cluster running within the same k8s cluster.\",\"properties\":{\"name\":{\"description\":\"Name of the Kubernetes object.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of the Kubernetes object. If empty, defaults to the current namespace.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"name\":{\"description\":\"Name is the name of the remote cluster as it is set in the Elasticsearch settings. The name is expected to be unique for each remote clusters.\",\"minLength\":1,\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"secureSettings\":{\"description\":\"SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html\",\"items\":{\"description\":\"SecretSource defines a data source based on a Kubernetes Secret.\",\"properties\":{\"entries\":{\"description\":\"Entries define how to project each key-value pair in the secret to filesystem paths. If not defined, all keys will be projected to similarly named paths in the filesystem. If defined, only the specified keys will be projected to the corresponding paths.\",\"items\":{\"description\":\"KeyToPath defines how to map a key in a Secret object to a filesystem path.\",\"properties\":{\"key\":{\"description\":\"Key is the key contained in the secret.\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the relative file path to map the key to. Path must not be an absolute file path and must not contain any \\\"..\\\" components.\",\"type\":\"string\"}},\"required\":[\"key\"],\"type\":\"object\"},\"type\":\"array\"},\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"required\":[\"secretName\"],\"type\":\"object\"},\"type\":\"array\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace. Can only be used if ECK is enforcing RBAC on references.\",\"type\":\"string\"},\"transport\":{\"description\":\"Transport holds transport layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"updateStrategy\":{\"description\":\"UpdateStrategy specifies how updates to the cluster should be performed.\",\"properties\":{\"changeBudget\":{\"description\":\"ChangeBudget defines the constraints to consider when applying changes to the Elasticsearch cluster.\",\"properties\":{\"maxSurge\":{\"description\":\"MaxSurge is the maximum number of new pods that can be created exceeding the original number of pods defined in the specification. MaxSurge is only taken into consideration when scaling up. Setting a negative value will disable the restriction. Defaults to unbounded if not specified.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxUnavailable\":{\"description\":\"MaxUnavailable is the maximum number of pods that can be unavailable (not ready) during the update due to circumstances under the control of the operator. Setting a negative value will disable this restriction. Defaults to 1 if not specified.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"version\":{\"description\":\"Version of Elasticsearch.\",\"type\":\"string\"}},\"required\":[\"nodeSets\",\"version\"],\"type\":\"object\"},\"status\":{\"description\":\"ElasticsearchStatus defines the observed state of Elasticsearch\",\"properties\":{\"availableNodes\":{\"format\":\"int32\",\"type\":\"integer\"},\"health\":{\"description\":\"ElasticsearchHealth is the health of the cluster as returned by the health API.\",\"type\":\"string\"},\"phase\":{\"description\":\"ElasticsearchOrchestrationPhase is the phase Elasticsearch is in from the controller point of view.\",\"type\":\"string\"}},\"type\":\"object\"}}}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true},{\"name\":\"v1beta1\",\"served\":true,\"storage\":false},{\"name\":\"v1alpha1\",\"served\":false,\"storage\":false}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"controller-gen.kubebuilder.io/version\":\"v0.2.5\"},\"creationTimestamp\":null,\"name\":\"elasticsearches.elasticsearch.k8s.elastic.co\"},\"spec\":{\"additionalPrinterColumns\":[{\"JSONPath\":\".status.health\",\"name\":\"health\",\"type\":\"string\"},{\"JSONPath\":\".status.availableNodes\",\"description\":\"Available nodes\",\"name\":\"nodes\",\"type\":\"integer\"},{\"JSONPath\":\".spec.version\",\"description\":\"Elasticsearch version\",\"name\":\"version\",\"type\":\"string\"},{\"JSONPath\":\".status.phase\",\"name\":\"phase\",\"type\":\"string\"},{\"JSONPath\":\".metadata.creationTimestamp\",\"name\":\"age\",\"type\":\"date\"}],\"group\":\"elasticsearch.k8s.elastic.co\",\"names\":{\"categories\":[\"elastic\"],\"kind\":\"Elasticsearch\",\"listKind\":\"ElasticsearchList\",\"plural\":\"elasticsearches\",\"shortNames\":[\"es\"],\"singular\":\"elasticsearch\"},\"scope\":\"Namespaced\",\"subresources\":{\"status\":{}},\"validation\":{\"openAPIV3Schema\":{\"description\":\"Elasticsearch represents an Elasticsearch resource in a Kubernetes cluster.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"type\":\"object\"},\"spec\":{\"description\":\"ElasticsearchSpec holds the specification of an Elasticsearch cluster.\",\"properties\":{\"auth\":{\"description\":\"Auth contains user authentication and authorization security settings for Elasticsearch.\",\"properties\":{\"fileRealm\":{\"description\":\"FileRealm to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"FileRealmSource references users to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"roles\":{\"description\":\"Roles to propagate to the Elasticsearch cluster.\",\"items\":{\"description\":\"RoleSource references roles to create in the Elasticsearch cluster.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"http\":{\"description\":\"HTTP holds HTTP layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tls\":{\"description\":\"TLS defines options for configuring TLS for HTTP.\",\"properties\":{\"certificate\":{\"description\":\"Certificate is a reference to a Kubernetes secret that contains the certificate and private key for enabling TLS. The referenced secret should contain the following: \\n - `ca.crt`: The certificate authority (optional). - `tls.crt`: The certificate (or a chain). - `tls.key`: The private key to the first certificate in the certificate chain.\",\"properties\":{\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"type\":\"object\"},\"selfSignedCertificate\":{\"description\":\"SelfSignedCertificate allows configuring the self-signed certificate generated by the operator.\",\"properties\":{\"disabled\":{\"description\":\"Disabled indicates that the provisioning of the self-signed certificate should be disabled.\",\"type\":\"boolean\"},\"subjectAltNames\":{\"description\":\"SubjectAlternativeNames is a list of SANs to include in the generated HTTP TLS certificate.\",\"items\":{\"description\":\"SubjectAlternativeName represents a SAN entry in a x509 certificate.\",\"properties\":{\"dns\":{\"description\":\"DNS is the DNS name of the subject.\",\"type\":\"string\"},\"ip\":{\"description\":\"IP is the IP address of the subject.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"image\":{\"description\":\"Image is the Elasticsearch Docker image to deploy.\",\"type\":\"string\"},\"nodeSets\":{\"description\":\"NodeSets allow specifying groups of Elasticsearch nodes sharing the same configuration and Pod templates. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-orchestration.html\",\"items\":{\"description\":\"NodeSet is the specification for a group of Elasticsearch nodes sharing the same configuration and a Pod template.\",\"properties\":{\"config\":{\"description\":\"Config holds the Elasticsearch configuration.\",\"type\":\"object\"},\"count\":{\"description\":\"Count of Elasticsearch nodes to deploy.\",\"format\":\"int32\",\"minimum\":1,\"type\":\"integer\"},\"name\":{\"description\":\"Name of this set of nodes. Becomes a part of the Elasticsearch node.name setting.\",\"maxLength\":23,\"pattern\":\"[a-zA-Z0-9-]+\",\"type\":\"string\"},\"podTemplate\":{\"description\":\"PodTemplate provides customisation options (labels, annotations, affinity rules, resource requests, and so on) for the Pods belonging to this NodeSet.\",\"type\":\"object\"},\"volumeClaimTemplates\":{\"description\":\"VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod in this NodeSet. Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate. Items defined here take precedence over any default claims added by the operator with the same name. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-volume-claim-templates.html\",\"items\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources\",\"properties\":{\"limits\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label query over volumes to consider for binding.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"additionalProperties\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"pattern\":\"^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$\"},\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Last time we probed the condition.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Last time the condition transitioned from one status to another.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"description\":\"PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type\",\"type\":\"string\"}},\"required\":[\"status\",\"type\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"count\",\"name\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"podDisruptionBudget\":{\"description\":\"PodDisruptionBudget provides access to the default pod disruption budget for the Elasticsearch cluster. The default budget selects all cluster pods and sets `maxUnavailable` to 1. To disable, set `PodDisruptionBudget` to the empty value (`{}` in YAML).\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the PDB. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the PDB.\",\"properties\":{\"maxUnavailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at most \\\"maxUnavailable\\\" pods selected by \\\"selector\\\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \\\"minAvailable\\\".\"},\"minAvailable\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"An eviction is allowed if at least \\\"minAvailable\\\" pods selected by \\\"selector\\\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \\\"100%\\\".\"},\"selector\":{\"description\":\"Label query over pods whose evictions are managed by the disruption budget.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"remoteClusters\":{\"description\":\"RemoteClusters enables you to establish uni-directional connections to a remote Elasticsearch cluster.\",\"items\":{\"description\":\"RemoteCluster declares a remote Elasticsearch cluster connection.\",\"properties\":{\"elasticsearchRef\":{\"description\":\"ElasticsearchRef is a reference to an Elasticsearch cluster running within the same k8s cluster.\",\"properties\":{\"name\":{\"description\":\"Name of the Kubernetes object.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of the Kubernetes object. If empty, defaults to the current namespace.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"name\":{\"description\":\"Name is the name of the remote cluster as it is set in the Elasticsearch settings. The name is expected to be unique for each remote clusters.\",\"minLength\":1,\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"secureSettings\":{\"description\":\"SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch. See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html\",\"items\":{\"description\":\"SecretSource defines a data source based on a Kubernetes Secret.\",\"properties\":{\"entries\":{\"description\":\"Entries define how to project each key-value pair in the secret to filesystem paths. If not defined, all keys will be projected to similarly named paths in the filesystem. If defined, only the specified keys will be projected to the corresponding paths.\",\"items\":{\"description\":\"KeyToPath defines how to map a key in a Secret object to a filesystem path.\",\"properties\":{\"key\":{\"description\":\"Key is the key contained in the secret.\",\"type\":\"string\"},\"path\":{\"description\":\"Path is the relative file path to map the key to. Path must not be an absolute file path and must not contain any \\\"..\\\" components.\",\"type\":\"string\"}},\"required\":[\"key\"],\"type\":\"object\"},\"type\":\"array\"},\"secretName\":{\"description\":\"SecretName is the name of the secret.\",\"type\":\"string\"}},\"required\":[\"secretName\"],\"type\":\"object\"},\"type\":\"array\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace. Can only be used if ECK is enforcing RBAC on references.\",\"type\":\"string\"},\"transport\":{\"description\":\"Transport holds transport layer settings for Elasticsearch.\",\"properties\":{\"service\":{\"description\":\"Service defines the template for the associated Kubernetes Service object.\",\"properties\":{\"metadata\":{\"description\":\"ObjectMeta is the metadata of the service. The name and namespace provided here are managed by ECK and will be ignored.\",\"type\":\"object\"},\"spec\":{\"description\":\"Spec is the specification of the service.\",\"properties\":{\"clusterIP\":{\"description\":\"clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \\\"None\\\", empty string (\\\"\\\"), or a valid IP address. \\\"None\\\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"externalIPs\":{\"description\":\"externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"externalName\":{\"description\":\"externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.\",\"type\":\"string\"},\"externalTrafficPolicy\":{\"description\":\"externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \\\"Local\\\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \\\"Cluster\\\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.\",\"type\":\"string\"},\"healthCheckNodePort\":{\"description\":\"healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.\",\"format\":\"int32\",\"type\":\"integer\"},\"ipFamily\":{\"description\":\"ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.\",\"type\":\"string\"},\"loadBalancerIP\":{\"description\":\"Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.\",\"type\":\"string\"},\"loadBalancerSourceRanges\":{\"description\":\"If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\\\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"ports\":{\"description\":\"The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"items\":{\"description\":\"ServicePort contains information on service's port.\",\"properties\":{\"name\":{\"description\":\"The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.\",\"type\":\"string\"},\"nodePort\":{\"description\":\"The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport\",\"format\":\"int32\",\"type\":\"integer\"},\"port\":{\"description\":\"The port that will be exposed by this service.\",\"format\":\"int32\",\"type\":\"integer\"},\"protocol\":{\"description\":\"The IP protocol for this port. Supports \\\"TCP\\\", \\\"UDP\\\", and \\\"SCTP\\\". Default is TCP.\",\"type\":\"string\"},\"targetPort\":{\"anyOf\":[{\"type\":\"integer\"},{\"type\":\"string\"}],\"description\":\"Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service\"}},\"required\":[\"port\"],\"type\":\"object\"},\"type\":\"array\"},\"publishNotReadyAddresses\":{\"description\":\"publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.\",\"type\":\"boolean\"},\"selector\":{\"additionalProperties\":{\"type\":\"string\"},\"description\":\"Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/\",\"type\":\"object\"},\"sessionAffinity\":{\"description\":\"Supports \\\"ClientIP\\\" and \\\"None\\\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\",\"type\":\"string\"},\"sessionAffinityConfig\":{\"description\":\"sessionAffinityConfig contains the configurations of session affinity.\",\"properties\":{\"clientIP\":{\"description\":\"clientIP contains the configurations of Client IP based session affinity.\",\"properties\":{\"timeoutSeconds\":{\"description\":\"timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be \\u003e0 \\u0026\\u0026 \\u003c=86400(for 1 day) if ServiceAffinity == \\\"ClientIP\\\". Default value is 10800(for 3 hours).\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"topologyKeys\":{\"description\":\"topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \\\"*\\\" may be used to mean \\\"any topology\\\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"type\":{\"description\":\"type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \\\"ExternalName\\\" maps to the specified externalName. \\\"ClusterIP\\\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \\\"None\\\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \\\"NodePort\\\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \\\"LoadBalancer\\\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"updateStrategy\":{\"description\":\"UpdateStrategy specifies how updates to the cluster should be performed.\",\"properties\":{\"changeBudget\":{\"description\":\"ChangeBudget defines the constraints to consider when applying changes to the Elasticsearch cluster.\",\"properties\":{\"maxSurge\":{\"description\":\"MaxSurge is the maximum number of new pods that can be created exceeding the original number of pods defined in the specification. MaxSurge is only taken into consideration when scaling up. Setting a negative value will disable the restriction. Defaults to unbounded if not specified.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxUnavailable\":{\"description\":\"MaxUnavailable is the maximum number of pods that can be unavailable (not ready) during the update due to circumstances under the control of the operator. Setting a negative value will disable this restriction. Defaults to 1 if not specified.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"}},\"type\":\"object\"},\"version\":{\"description\":\"Version of Elasticsearch.\",\"type\":\"string\"}},\"required\":[\"nodeSets\",\"version\"],\"type\":\"object\"},\"status\":{\"description\":\"ElasticsearchStatus defines the observed state of Elasticsearch\",\"properties\":{\"availableNodes\":{\"format\":\"int32\",\"type\":\"integer\"},\"health\":{\"description\":\"ElasticsearchHealth is the health of the cluster as returned by the health API.\",\"type\":\"string\"},\"phase\":{\"description\":\"ElasticsearchOrchestrationPhase is the phase Elasticsearch is in from the controller point of view.\",\"type\":\"string\"}},\"type\":\"object\"}}}},\"version\":\"v1\",\"versions\":[{\"name\":\"v1\",\"served\":true,\"storage\":true},{\"name\":\"v1beta1\",\"served\":true,\"storage\":false},{\"name\":\"v1alpha1\",\"served\":false,\"storage\":false}]},\"status\":{\"acceptedNames\":{\"kind\":\"\",\"plural\":\"\"},\"conditions\":[],\"storedVersions\":[]}}\n" } }, "spec": { @@ -490,7 +490,7 @@ "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "type": "array", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "type": "object", "required": [ "status", diff --git a/pkg/backup/testdata/v1beta1/prometheuses.monitoring.coreos.com.json b/pkg/backup/testdata/v1beta1/prometheuses.monitoring.coreos.com.json index dc146e202a..9a10ec4d04 100644 --- a/pkg/backup/testdata/v1beta1/prometheuses.monitoring.coreos.com.json +++ b/pkg/backup/testdata/v1beta1/prometheuses.monitoring.coreos.com.json @@ -13,7 +13,7 @@ }, "annotations": { "helm.sh/hook": "crd-install", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"plural\":\"prometheuses\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"PrometheusSpec is a specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"AlertingSpec defines parameters for alerting configuration of Prometheus servers.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"namespace\",\"name\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig defines a host and auth methods to access apiserver. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\n\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\n\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is alpha in 1.14.\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows the tuning of remote_write queue_config parameters. This object is referenced in the RemoteWriteSpec object.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\n\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contails details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"ThanosSpec defines parameters for a Prometheus server within a Thanos deployment.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"PrometheusStatus is the most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"],\"type\":\"object\"}},\"type\":\"object\"}},\"version\":\"v1\"}}\n" + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"crd-install\"},\"creationTimestamp\":null,\"labels\":{\"app\":\"prometheus-operator\"},\"name\":\"prometheuses.monitoring.coreos.com\"},\"spec\":{\"group\":\"monitoring.coreos.com\",\"names\":{\"kind\":\"Prometheus\",\"plural\":\"prometheuses\"},\"scope\":\"Namespaced\",\"validation\":{\"openAPIV3Schema\":{\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"spec\":{\"description\":\"PrometheusSpec is a specification of the desired behavior of the Prometheus cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"additionalAlertManagerConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalAlertRelabelConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"additionalScrapeConfigs\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"affinity\":{\"description\":\"Affinity is a group of affinity scheduling rules.\",\"properties\":{\"nodeAffinity\":{\"description\":\"Node affinity is a group of node affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).\",\"properties\":{\"preference\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"weight\":{\"description\":\"Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"preference\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.\",\"properties\":{\"nodeSelectorTerms\":{\"description\":\"Required. A list of node selector terms. The terms are ORed.\",\"items\":{\"description\":\"A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.\",\"properties\":{\"matchExpressions\":{\"description\":\"A list of node selector requirements by node's labels.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchFields\":{\"description\":\"A list of node selector requirements by node's fields.\",\"items\":{\"description\":\"A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"The label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.\",\"type\":\"string\"},\"values\":{\"description\":\"An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"nodeSelectorTerms\"],\"type\":\"object\"}},\"type\":\"object\"},\"podAffinity\":{\"description\":\"Pod affinity is a group of inter pod affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"podAntiAffinity\":{\"description\":\"Pod anti affinity is a group of inter pod anti affinity scheduling rules.\",\"properties\":{\"preferredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \\\"weight\\\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.\",\"items\":{\"description\":\"The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)\",\"properties\":{\"podAffinityTerm\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"weight\":{\"description\":\"weight associated with matching the corresponding podAffinityTerm, in the range 1-100.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"weight\",\"podAffinityTerm\"],\"type\":\"object\"},\"type\":\"array\"},\"requiredDuringSchedulingIgnoredDuringExecution\":{\"description\":\"If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.\",\"items\":{\"description\":\"Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \\u003ctopologyKey\\u003e matches that of any node on which a pod of the set of pods is running\",\"properties\":{\"labelSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"namespaces\":{\"description\":\"namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \\\"this pod's namespace\\\"\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"topologyKey\":{\"description\":\"This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.\",\"type\":\"string\"}},\"required\":[\"topologyKey\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"}},\"type\":\"object\"},\"alerting\":{\"description\":\"AlertingSpec defines parameters for alerting configuration of Prometheus servers.\",\"properties\":{\"alertmanagers\":{\"description\":\"AlertmanagerEndpoints Prometheus should fire alerts against.\",\"items\":{\"description\":\"AlertmanagerEndpoints defines a selection of a single Endpoints object containing alertmanager IPs to fire alerts against.\",\"properties\":{\"bearerTokenFile\":{\"description\":\"BearerTokenFile to read from filesystem to use when authenticating to Alertmanager.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of Endpoints object in Namespace.\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace of Endpoints object.\",\"type\":\"string\"},\"pathPrefix\":{\"description\":\"Prefix for the HTTP path alerts are pushed to.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use when firing alerts.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"namespace\",\"name\",\"port\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"alertmanagers\"],\"type\":\"object\"},\"apiserverConfig\":{\"description\":\"APIServerConfig defines a host and auth methods to access apiserver. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"Bearer token for accessing apiserver.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for accessing apiserver.\",\"type\":\"string\"},\"host\":{\"description\":\"Host of apiserver. A valid string consisting of a hostname or IP followed by an optional port number\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"host\"],\"type\":\"object\"},\"baseImage\":{\"description\":\"Base image to use for a Prometheus deployment.\",\"type\":\"string\"},\"configMaps\":{\"description\":\"ConfigMaps is a list of ConfigMaps in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The ConfigMaps are mounted into /etc/prometheus/configmaps/\\u003cconfigmap-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"containers\":{\"description\":\"Containers allows injecting additional containers or modifying operator generated containers. This can be used to allow adding an authentication proxy to a Prometheus pod or to change the behavior of an operator generated container. Containers described here modify an operator generated container if they share the same name and modifications are done via a strategic merge patch. The current container names are: `prometheus`, `prometheus-config-reloader`, `rules-configmap-reloader`, and `thanos-sidecar`. Overriding containers is entirely outside the scope of what the maintainers will support and by doing so, you accept that this behaviour may break at any time without notice.\",\"items\":{\"description\":\"A single application container that you want to run within a pod.\",\"properties\":{\"args\":{\"description\":\"Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"command\":{\"description\":\"Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"env\":{\"description\":\"List of environment variables to set in the container. Cannot be updated.\",\"items\":{\"description\":\"EnvVar represents an environment variable present in a Container.\",\"properties\":{\"name\":{\"description\":\"Name of the environment variable. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"value\":{\"description\":\"Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \\\"\\\".\",\"type\":\"string\"},\"valueFrom\":{\"description\":\"EnvVarSource represents a source for the value of an EnvVar.\",\"properties\":{\"configMapKeyRef\":{\"description\":\"Selects a key from a ConfigMap.\",\"properties\":{\"key\":{\"description\":\"The key to select.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"fieldRef\":{\"description\":\"ObjectFieldSelector selects an APIVersioned field of an object.\",\"properties\":{\"apiVersion\":{\"description\":\"Version of the schema the FieldPath is written in terms of, defaults to \\\"v1\\\".\",\"type\":\"string\"},\"fieldPath\":{\"description\":\"Path of the field to select in the specified API version.\",\"type\":\"string\"}},\"required\":[\"fieldPath\"],\"type\":\"object\"},\"resourceFieldRef\":{\"description\":\"ResourceFieldSelector represents container resources (cpu, memory) and their output format\",\"properties\":{\"containerName\":{\"description\":\"Container name: required for volumes, optional for env vars\",\"type\":\"string\"},\"divisor\":{},\"resource\":{\"description\":\"Required: resource to select\",\"type\":\"string\"}},\"required\":[\"resource\"],\"type\":\"object\"},\"secretKeyRef\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"envFrom\":{\"description\":\"List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.\",\"items\":{\"description\":\"EnvFromSource represents the source of a set of ConfigMaps\",\"properties\":{\"configMapRef\":{\"description\":\"ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\\n\\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the ConfigMap must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"},\"prefix\":{\"description\":\"An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.\",\"type\":\"string\"},\"secretRef\":{\"description\":\"SecretEnvSource selects a Secret to populate the environment variables with.\\n\\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret must be defined\",\"type\":\"boolean\"}},\"type\":\"object\"}},\"type\":\"object\"},\"type\":\"array\"},\"image\":{\"description\":\"Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.\",\"type\":\"string\"},\"imagePullPolicy\":{\"description\":\"Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images\",\"type\":\"string\"},\"lifecycle\":{\"description\":\"Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.\",\"properties\":{\"postStart\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"},\"preStop\":{\"description\":\"Handler defines a specific action that should be taken\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"livenessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"name\":{\"description\":\"Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.\",\"type\":\"string\"},\"ports\":{\"description\":\"List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \\\"0.0.0.0\\\" address inside a container will be accessible from the network. Cannot be updated.\",\"items\":{\"description\":\"ContainerPort represents a network port in a single container.\",\"properties\":{\"containerPort\":{\"description\":\"Number of port to expose on the pod's IP address. This must be a valid port number, 0 \\u003c x \\u003c 65536.\",\"format\":\"int32\",\"type\":\"integer\"},\"hostIP\":{\"description\":\"What host IP to bind the external port to.\",\"type\":\"string\"},\"hostPort\":{\"description\":\"Number of port to expose on the host. If specified, this must be a valid port number, 0 \\u003c x \\u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.\",\"format\":\"int32\",\"type\":\"integer\"},\"name\":{\"description\":\"If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.\",\"type\":\"string\"},\"protocol\":{\"description\":\"Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \\\"TCP\\\".\",\"type\":\"string\"}},\"required\":[\"containerPort\"],\"type\":\"object\"},\"type\":\"array\"},\"readinessProbe\":{\"description\":\"Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.\",\"properties\":{\"exec\":{\"description\":\"ExecAction describes a \\\"run in container\\\" action.\",\"properties\":{\"command\":{\"description\":\"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"failureThreshold\":{\"description\":\"Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"httpGet\":{\"description\":\"HTTPGetAction describes an action based on HTTP Get requests.\",\"properties\":{\"host\":{\"description\":\"Host name to connect to, defaults to the pod IP. You probably want to set \\\"Host\\\" in httpHeaders instead.\",\"type\":\"string\"},\"httpHeaders\":{\"description\":\"Custom headers to set in the request. HTTP allows repeated headers.\",\"items\":{\"description\":\"HTTPHeader describes a custom header to be used in HTTP probes\",\"properties\":{\"name\":{\"description\":\"The header field name\",\"type\":\"string\"},\"value\":{\"description\":\"The header field value\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to access on the HTTP server.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]},\"scheme\":{\"description\":\"Scheme to use for connecting to the host. Defaults to HTTP.\",\"type\":\"string\"}},\"required\":[\"port\"],\"type\":\"object\"},\"initialDelaySeconds\":{\"description\":\"Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"},\"periodSeconds\":{\"description\":\"How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"successThreshold\":{\"description\":\"Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.\",\"format\":\"int32\",\"type\":\"integer\"},\"tcpSocket\":{\"description\":\"TCPSocketAction describes an action based on opening a socket\",\"properties\":{\"host\":{\"description\":\"Optional: Host name to connect to, defaults to the pod IP.\",\"type\":\"string\"},\"port\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"integer\"}]}},\"required\":[\"port\"],\"type\":\"object\"},\"timeoutSeconds\":{\"description\":\"Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"securityContext\":{\"description\":\"SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.\",\"properties\":{\"allowPrivilegeEscalation\":{\"description\":\"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN\",\"type\":\"boolean\"},\"capabilities\":{\"description\":\"Adds and removes POSIX capabilities from running containers.\",\"properties\":{\"add\":{\"description\":\"Added capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"drop\":{\"description\":\"Removed capabilities\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"type\":\"object\"},\"privileged\":{\"description\":\"Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.\",\"type\":\"boolean\"},\"procMount\":{\"description\":\"procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.\",\"type\":\"string\"},\"readOnlyRootFilesystem\":{\"description\":\"Whether this container has a read-only root filesystem. Default is false.\",\"type\":\"boolean\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"stdin\":{\"description\":\"Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.\",\"type\":\"boolean\"},\"stdinOnce\":{\"description\":\"Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false\",\"type\":\"boolean\"},\"terminationMessagePath\":{\"description\":\"Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.\",\"type\":\"string\"},\"terminationMessagePolicy\":{\"description\":\"Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.\",\"type\":\"string\"},\"tty\":{\"description\":\"Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.\",\"type\":\"boolean\"},\"volumeDevices\":{\"description\":\"volumeDevices is the list of block devices to be used by the container. This is a beta feature.\",\"items\":{\"description\":\"volumeDevice describes a mapping of a raw block device within a container.\",\"properties\":{\"devicePath\":{\"description\":\"devicePath is the path inside of the container that the device will be mapped to.\",\"type\":\"string\"},\"name\":{\"description\":\"name must match the name of a persistentVolumeClaim in the pod\",\"type\":\"string\"}},\"required\":[\"name\",\"devicePath\"],\"type\":\"object\"},\"type\":\"array\"},\"volumeMounts\":{\"description\":\"Pod volumes to mount into the container's filesystem. Cannot be updated.\",\"items\":{\"description\":\"VolumeMount describes a mounting of a Volume within a container.\",\"properties\":{\"mountPath\":{\"description\":\"Path within the container at which the volume should be mounted. Must not contain ':'.\",\"type\":\"string\"},\"mountPropagation\":{\"description\":\"mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.\",\"type\":\"string\"},\"name\":{\"description\":\"This must match the Name of a Volume.\",\"type\":\"string\"},\"readOnly\":{\"description\":\"Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.\",\"type\":\"boolean\"},\"subPath\":{\"description\":\"Path within the volume from which the container's volume should be mounted. Defaults to \\\"\\\" (volume's root).\",\"type\":\"string\"},\"subPathExpr\":{\"description\":\"Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \\\"\\\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is alpha in 1.14.\",\"type\":\"string\"}},\"required\":[\"name\",\"mountPath\"],\"type\":\"object\"},\"type\":\"array\"},\"workingDir\":{\"description\":\"Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"enableAdminAPI\":{\"description\":\"Enable access to prometheus web admin API. Defaults to the value of `false`. WARNING: Enabling the admin APIs enables mutating endpoints, to delete data, shutdown Prometheus, and more. Enabling this should be done with care and the user is advised to add additional authentication authorization via a proxy to ensure only clients authorized to perform these actions can do so. For more information see https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis\",\"type\":\"boolean\"},\"evaluationInterval\":{\"description\":\"Interval between consecutive evaluations.\",\"type\":\"string\"},\"externalLabels\":{\"description\":\"The labels to add to any time series or alerts when communicating with external systems (federation, remote storage, Alertmanager).\",\"type\":\"object\"},\"externalUrl\":{\"description\":\"The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Prometheus is being configured.\",\"type\":\"string\"},\"imagePullSecrets\":{\"description\":\"An optional list of references to secrets in the same namespace to use for pulling prometheus and alertmanager images from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod\",\"items\":{\"description\":\"LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.\",\"properties\":{\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"listenLocal\":{\"description\":\"ListenLocal makes the Prometheus server listen on loopback, so that it does not bind against the Pod IP.\",\"type\":\"boolean\"},\"logFormat\":{\"description\":\"Log format for Prometheus to be configured with.\",\"type\":\"string\"},\"logLevel\":{\"description\":\"Log level for Prometheus to be configured with.\",\"type\":\"string\"},\"nodeSelector\":{\"description\":\"Define which Nodes the Pods are scheduled on.\",\"type\":\"object\"},\"paused\":{\"description\":\"When a Prometheus deployment is paused, no actions except for deletion will be performed on the underlying objects.\",\"type\":\"boolean\"},\"podMetadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"podMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"podMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"priorityClassName\":{\"description\":\"Priority class assigned to the Pods\",\"type\":\"string\"},\"prometheusExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote Prometheus instance name. Defaults to the value of `prometheus`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"query\":{\"description\":\"QuerySpec defines the query command line flags when starting Prometheus.\",\"properties\":{\"lookbackDelta\":{\"description\":\"The delta difference allowed for retrieving metrics during expression evaluations.\",\"type\":\"string\"},\"maxConcurrency\":{\"description\":\"Number of concurrent queries that can be run at once.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamples\":{\"description\":\"Maximum number of samples a single query can load into memory. Note that queries will fail if they would load more samples than this into memory, so this also limits the number of samples a query can return.\",\"format\":\"int32\",\"type\":\"integer\"},\"timeout\":{\"description\":\"Maximum time a query may take before being aborted.\",\"type\":\"string\"}},\"type\":\"object\"},\"remoteRead\":{\"description\":\"If specified, the remote_read spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteReadSpec defines the remote_read configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"bearer token for remote read.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote read.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"readRecent\":{\"description\":\"Whether reads should be made for queries for time ranges that the local storage should have complete data for.\",\"type\":\"boolean\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote read endpoint.\",\"type\":\"string\"},\"requiredMatchers\":{\"description\":\"An optional list of equality matchers which have to be present in a selector to query the remote read endpoint.\",\"type\":\"object\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"remoteWrite\":{\"description\":\"If specified, the remote_write spec. This is an experimental feature, it may change in any upcoming release in a breaking way.\",\"items\":{\"description\":\"RemoteWriteSpec defines the remote_write configuration for prometheus.\",\"properties\":{\"basicAuth\":{\"description\":\"BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints\",\"properties\":{\"password\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"username\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"}},\"type\":\"object\"},\"bearerToken\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"bearerTokenFile\":{\"description\":\"File to read bearer token for remote write.\",\"type\":\"string\"},\"proxyUrl\":{\"description\":\"Optional ProxyURL\",\"type\":\"string\"},\"queueConfig\":{\"description\":\"QueueConfig allows the tuning of remote_write queue_config parameters. This object is referenced in the RemoteWriteSpec object.\",\"properties\":{\"batchSendDeadline\":{\"description\":\"BatchSendDeadline is the maximum time a sample will wait in buffer.\",\"type\":\"string\"},\"capacity\":{\"description\":\"Capacity is the number of samples to buffer per shard before we start dropping them.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxBackoff\":{\"description\":\"MaxBackoff is the maximum retry delay.\",\"type\":\"string\"},\"maxRetries\":{\"description\":\"MaxRetries is the maximum number of times to retry a batch on recoverable errors.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxSamplesPerSend\":{\"description\":\"MaxSamplesPerSend is the maximum number of samples per send.\",\"format\":\"int32\",\"type\":\"integer\"},\"maxShards\":{\"description\":\"MaxShards is the maximum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"},\"minBackoff\":{\"description\":\"MinBackoff is the initial retry delay. Gets doubled for every retry.\",\"type\":\"string\"},\"minShards\":{\"description\":\"MinShards is the minimum number of shards, i.e. amount of concurrency.\",\"format\":\"int32\",\"type\":\"integer\"}},\"type\":\"object\"},\"remoteTimeout\":{\"description\":\"Timeout for requests to the remote write endpoint.\",\"type\":\"string\"},\"tlsConfig\":{\"description\":\"TLSConfig specifies TLS configuration parameters.\",\"properties\":{\"caFile\":{\"description\":\"The CA cert to use for the targets.\",\"type\":\"string\"},\"certFile\":{\"description\":\"The client cert file for the targets.\",\"type\":\"string\"},\"insecureSkipVerify\":{\"description\":\"Disable target certificate validation.\",\"type\":\"boolean\"},\"keyFile\":{\"description\":\"The client key file for the targets.\",\"type\":\"string\"},\"serverName\":{\"description\":\"Used to verify the hostname for the targets.\",\"type\":\"string\"}},\"type\":\"object\"},\"url\":{\"description\":\"The URL of the endpoint to send samples to.\",\"type\":\"string\"},\"writeRelabelConfigs\":{\"description\":\"The list of remote write relabel configurations.\",\"items\":{\"description\":\"RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. It defines `\\u003cmetric_relabel_configs\\u003e`-section of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs\",\"properties\":{\"action\":{\"description\":\"Action to perform based on regex matching. Default is 'replace'\",\"type\":\"string\"},\"modulus\":{\"description\":\"Modulus to take of the hash of the source label values.\",\"format\":\"int64\",\"type\":\"integer\"},\"regex\":{\"description\":\"Regular expression against which the extracted value is matched. default is '(.*)'\",\"type\":\"string\"},\"replacement\":{\"description\":\"Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is '$1'\",\"type\":\"string\"},\"separator\":{\"description\":\"Separator placed between concatenated source label values. default is ';'.\",\"type\":\"string\"},\"sourceLabels\":{\"description\":\"The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"targetLabel\":{\"description\":\"Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"url\"],\"type\":\"object\"},\"type\":\"array\"},\"replicaExternalLabelName\":{\"description\":\"Name of Prometheus external label used to denote replica name. Defaults to the value of `prometheus_replica`. External label will _not_ be added when value is set to empty string (`\\\"\\\"`).\",\"type\":\"string\"},\"replicas\":{\"description\":\"Number of instances to deploy for a Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"retention\":{\"description\":\"Time duration Prometheus shall retain data for. Default is '24h', and must match the regular expression `[0-9]+(ms|s|m|h|d|w|y)` (milliseconds seconds minutes hours days weeks years).\",\"type\":\"string\"},\"retentionSize\":{\"description\":\"Maximum amount of disk space used by blocks.\",\"type\":\"string\"},\"routePrefix\":{\"description\":\"The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`.\",\"type\":\"string\"},\"ruleNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"ruleSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"rules\":{\"description\":\"/--rules.*/ command-line arguments\",\"properties\":{\"alert\":{\"description\":\"/--rules.alert.*/ command-line arguments\",\"properties\":{\"forGracePeriod\":{\"description\":\"Minimum duration between alert and restored 'for' state. This is maintained only for alerts with configured 'for' time greater than grace period.\",\"type\":\"string\"},\"forOutageTolerance\":{\"description\":\"Max time to tolerate prometheus outage for restoring 'for' state of alert.\",\"type\":\"string\"},\"resendDelay\":{\"description\":\"Minimum amount of time to wait before resending an alert to Alertmanager.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"},\"scrapeInterval\":{\"description\":\"Interval between consecutive scrapes.\",\"type\":\"string\"},\"secrets\":{\"description\":\"Secrets is a list of Secrets in the same namespace as the Prometheus object, which shall be mounted into the Prometheus Pods. The Secrets are mounted into /etc/prometheus/secrets/\\u003csecret-name\\u003e.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"securityContext\":{\"description\":\"PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.\",\"properties\":{\"fsGroup\":{\"description\":\"A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\\n\\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\\n\\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsGroup\":{\"description\":\"The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"runAsNonRoot\":{\"description\":\"Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.\",\"type\":\"boolean\"},\"runAsUser\":{\"description\":\"The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.\",\"format\":\"int64\",\"type\":\"integer\"},\"seLinuxOptions\":{\"description\":\"SELinuxOptions are the labels to be applied to the container\",\"properties\":{\"level\":{\"description\":\"Level is SELinux level label that applies to the container.\",\"type\":\"string\"},\"role\":{\"description\":\"Role is a SELinux role label that applies to the container.\",\"type\":\"string\"},\"type\":{\"description\":\"Type is a SELinux type label that applies to the container.\",\"type\":\"string\"},\"user\":{\"description\":\"User is a SELinux user label that applies to the container.\",\"type\":\"string\"}},\"type\":\"object\"},\"supplementalGroups\":{\"description\":\"A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.\",\"items\":{\"format\":\"int64\",\"type\":\"integer\"},\"type\":\"array\"},\"sysctls\":{\"description\":\"Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.\",\"items\":{\"description\":\"Sysctl defines a kernel parameter to be set\",\"properties\":{\"name\":{\"description\":\"Name of a property to set\",\"type\":\"string\"},\"value\":{\"description\":\"Value of a property to set\",\"type\":\"string\"}},\"required\":[\"name\",\"value\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\"},\"serviceAccountName\":{\"description\":\"ServiceAccountName is the name of the ServiceAccount to use to run the Prometheus Pods.\",\"type\":\"string\"},\"serviceMonitorNamespaceSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"serviceMonitorSelector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Prometheus container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"storage\":{\"description\":\"StorageSpec defines the configured storage for a group Prometheus servers. If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used.\",\"properties\":{\"emptyDir\":{\"description\":\"Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.\",\"properties\":{\"medium\":{\"description\":\"What type of storage medium should back this directory. The default is \\\"\\\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir\",\"type\":\"string\"},\"sizeLimit\":{}},\"type\":\"object\"},\"volumeClaimTemplate\":{\"description\":\"PersistentVolumeClaim is a user's request for and claim to a persistent volume\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"metadata\":{\"description\":\"ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.\",\"properties\":{\"annotations\":{\"description\":\"Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations\",\"type\":\"object\"},\"clusterName\":{\"description\":\"The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.\",\"type\":\"string\"},\"creationTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"deletionGracePeriodSeconds\":{\"description\":\"Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"deletionTimestamp\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"finalizers\":{\"description\":\"Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"generateName\":{\"description\":\"GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\\n\\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\\n\\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency\",\"type\":\"string\"},\"generation\":{\"description\":\"A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.\",\"format\":\"int64\",\"type\":\"integer\"},\"initializers\":{\"description\":\"Initializers tracks the progress of initialization.\",\"properties\":{\"pending\":{\"description\":\"Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.\",\"items\":{\"description\":\"Initializer is information about an initializer that has not yet completed.\",\"properties\":{\"name\":{\"description\":\"name of the process that is responsible for initializing this object.\",\"type\":\"string\"}},\"required\":[\"name\"],\"type\":\"object\"},\"type\":\"array\"},\"result\":{\"description\":\"Status is a return value for calls that don't return other objects.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources\",\"type\":\"string\"},\"code\":{\"description\":\"Suggested HTTP return code for this status, 0 if not set.\",\"format\":\"int32\",\"type\":\"integer\"},\"details\":{\"description\":\"StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.\",\"properties\":{\"causes\":{\"description\":\"The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.\",\"items\":{\"description\":\"StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.\",\"properties\":{\"field\":{\"description\":\"The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\\n\\nExamples:\\n \\\"name\\\" - the field \\\"name\\\" on the current resource\\n \\\"items[0].name\\\" - the field \\\"name\\\" on the first array entry in \\\"items\\\"\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the cause of the error. This field may be presented as-is to a reader.\",\"type\":\"string\"},\"reason\":{\"description\":\"A machine-readable description of the cause of the error. If this value is empty there is no information available.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"group\":{\"description\":\"The group attribute of the resource associated with the status StatusReason.\",\"type\":\"string\"},\"kind\":{\"description\":\"The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).\",\"type\":\"string\"},\"retryAfterSeconds\":{\"description\":\"If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.\",\"format\":\"int32\",\"type\":\"integer\"},\"uid\":{\"description\":\"UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"kind\":{\"description\":\"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"message\":{\"description\":\"A human-readable description of the status of this operation.\",\"type\":\"string\"},\"metadata\":{\"description\":\"ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.\",\"properties\":{\"continue\":{\"description\":\"continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.\",\"type\":\"string\"},\"resourceVersion\":{\"description\":\"String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"selfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"}},\"type\":\"object\"},\"reason\":{\"description\":\"A machine-readable description of why this operation is in the \\\"Failure\\\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.\",\"type\":\"string\"},\"status\":{\"description\":\"Status of the operation. One of: \\\"Success\\\" or \\\"Failure\\\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status\",\"type\":\"string\"}},\"type\":\"object\"}},\"required\":[\"pending\"],\"type\":\"object\"},\"labels\":{\"description\":\"Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels\",\"type\":\"object\"},\"managedFields\":{\"description\":\"ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \\\"ci-cd\\\". The set of fields is always in the version that the workflow used when modifying the object.\\n\\nThis field is alpha and can be changed or removed without notice.\",\"items\":{\"description\":\"ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.\",\"properties\":{\"apiVersion\":{\"description\":\"APIVersion defines the version of this resource that this field set applies to. The format is \\\"group/version\\\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.\",\"type\":\"string\"},\"fields\":{\"description\":\"Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff\",\"type\":\"object\"},\"manager\":{\"description\":\"Manager is an identifier of the workflow managing these fields.\",\"type\":\"string\"},\"operation\":{\"description\":\"Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.\",\"type\":\"string\"},\"time\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"name\":{\"description\":\"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"namespace\":{\"description\":\"Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \\\"default\\\" namespace, but \\\"default\\\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\\n\\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces\",\"type\":\"string\"},\"ownerReferences\":{\"description\":\"List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.\",\"items\":{\"description\":\"OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.\",\"properties\":{\"apiVersion\":{\"description\":\"API version of the referent.\",\"type\":\"string\"},\"blockOwnerDeletion\":{\"description\":\"If true, AND if the owner has the \\\"foregroundDeletion\\\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \\\"delete\\\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.\",\"type\":\"boolean\"},\"controller\":{\"description\":\"If true, this reference points to the managing controller.\",\"type\":\"boolean\"},\"kind\":{\"description\":\"Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names\",\"type\":\"string\"},\"uid\":{\"description\":\"UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"required\":[\"apiVersion\",\"kind\",\"name\",\"uid\"],\"type\":\"object\"},\"type\":\"array\"},\"resourceVersion\":{\"description\":\"An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\\n\\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency\",\"type\":\"string\"},\"selfLink\":{\"description\":\"SelfLink is a URL representing this object. Populated by the system. Read-only.\",\"type\":\"string\"},\"uid\":{\"description\":\"UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\\n\\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids\",\"type\":\"string\"}},\"type\":\"object\"},\"spec\":{\"description\":\"PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"dataSource\":{\"description\":\"TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.\",\"properties\":{\"apiGroup\":{\"description\":\"APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.\",\"type\":\"string\"},\"kind\":{\"description\":\"Kind is the type of resource being referenced\",\"type\":\"string\"},\"name\":{\"description\":\"Name is the name of resource being referenced\",\"type\":\"string\"}},\"required\":[\"kind\",\"name\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"selector\":{\"description\":\"A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.\",\"properties\":{\"matchExpressions\":{\"description\":\"matchExpressions is a list of label selector requirements. The requirements are ANDed.\",\"items\":{\"description\":\"A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\",\"properties\":{\"key\":{\"description\":\"key is the label key that the selector applies to.\",\"type\":\"string\"},\"operator\":{\"description\":\"operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.\",\"type\":\"string\"},\"values\":{\"description\":\"values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"key\",\"operator\"],\"type\":\"object\"},\"type\":\"array\"},\"matchLabels\":{\"description\":\"matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \\\"key\\\", the operator is \\\"In\\\", and the values array contains only \\\"value\\\". The requirements are ANDed.\",\"type\":\"object\"}},\"type\":\"object\"},\"storageClassName\":{\"description\":\"Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\",\"type\":\"string\"},\"volumeMode\":{\"description\":\"volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.\",\"type\":\"string\"},\"volumeName\":{\"description\":\"VolumeName is the binding reference to the PersistentVolume backing this claim.\",\"type\":\"string\"}},\"type\":\"object\"},\"status\":{\"description\":\"PersistentVolumeClaimStatus is the current status of a persistent volume claim.\",\"properties\":{\"accessModes\":{\"description\":\"AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"capacity\":{\"description\":\"Represents the actual resources of the underlying volume.\",\"type\":\"object\"},\"conditions\":{\"description\":\"Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.\",\"items\":{\"description\":\"PersistentVolumeClaimCondition contains details about state of pvc\",\"properties\":{\"lastProbeTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"lastTransitionTime\":{\"description\":\"Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.\",\"format\":\"date-time\",\"type\":\"string\"},\"message\":{\"description\":\"Human-readable message indicating details about last transition.\",\"type\":\"string\"},\"reason\":{\"description\":\"Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \\\"ResizeStarted\\\" that means the underlying persistent volume is being resized.\",\"type\":\"string\"},\"status\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"}},\"required\":[\"type\",\"status\"],\"type\":\"object\"},\"type\":\"array\"},\"phase\":{\"description\":\"Phase represents the current phase of PersistentVolumeClaim.\",\"type\":\"string\"}},\"type\":\"object\"}},\"type\":\"object\"}},\"type\":\"object\"},\"tag\":{\"description\":\"Tag of Prometheus container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"thanos\":{\"description\":\"ThanosSpec defines parameters for a Prometheus server within a Thanos deployment.\",\"properties\":{\"baseImage\":{\"description\":\"Thanos base image if other than default.\",\"type\":\"string\"},\"image\":{\"description\":\"Image if specified has precedence over baseImage, tag and sha combinations. Specifying the version is still necessary to ensure the Prometheus Operator knows what version of Thanos is being configured.\",\"type\":\"string\"},\"objectStorageConfig\":{\"description\":\"SecretKeySelector selects a key of a Secret.\",\"properties\":{\"key\":{\"description\":\"The key of the secret to select from. Must be a valid secret key.\",\"type\":\"string\"},\"name\":{\"description\":\"Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\",\"type\":\"string\"},\"optional\":{\"description\":\"Specify whether the Secret or it's key must be defined\",\"type\":\"boolean\"}},\"required\":[\"key\"],\"type\":\"object\"},\"resources\":{\"description\":\"ResourceRequirements describes the compute resource requirements.\",\"properties\":{\"limits\":{\"description\":\"Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"},\"requests\":{\"description\":\"Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/\",\"type\":\"object\"}},\"type\":\"object\"},\"sha\":{\"description\":\"SHA of Thanos container image to be deployed. Defaults to the value of `version`. Similar to a tag, but the SHA explicitly deploys an immutable container image. Version and Tag are ignored if SHA is set.\",\"type\":\"string\"},\"tag\":{\"description\":\"Tag of Thanos sidecar container image to be deployed. Defaults to the value of `version`. Version is ignored if Tag is set.\",\"type\":\"string\"},\"version\":{\"description\":\"Version describes the version of Thanos to use.\",\"type\":\"string\"}},\"type\":\"object\"},\"tolerations\":{\"description\":\"If specified, the pod's tolerations.\",\"items\":{\"description\":\"The pod this Toleration is attached to tolerates any taint that matches the triple \\u003ckey,value,effect\\u003e using the matching operator \\u003coperator\\u003e.\",\"properties\":{\"effect\":{\"description\":\"Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.\",\"type\":\"string\"},\"key\":{\"description\":\"Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.\",\"type\":\"string\"},\"operator\":{\"description\":\"Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.\",\"type\":\"string\"},\"tolerationSeconds\":{\"description\":\"TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.\",\"format\":\"int64\",\"type\":\"integer\"},\"value\":{\"description\":\"Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.\",\"type\":\"string\"}},\"type\":\"object\"},\"type\":\"array\"},\"version\":{\"description\":\"Version of Prometheus to be deployed.\",\"type\":\"string\"},\"walCompression\":{\"description\":\"Enable compression of the write-ahead log using Snappy.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"status\":{\"description\":\"PrometheusStatus is the most recent observed status of the Prometheus cluster. Read-only. Not included when requesting from the apiserver, only from the Prometheus Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\",\"properties\":{\"availableReplicas\":{\"description\":\"Total number of available pods (ready for at least minReadySeconds) targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"paused\":{\"description\":\"Represents whether any actions on the underlying managed objects are being performed. Only delete actions will be performed.\",\"type\":\"boolean\"},\"replicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment (their labels match the selector).\",\"format\":\"int32\",\"type\":\"integer\"},\"unavailableReplicas\":{\"description\":\"Total number of unavailable pods targeted by this Prometheus deployment.\",\"format\":\"int32\",\"type\":\"integer\"},\"updatedReplicas\":{\"description\":\"Total number of non-terminated pods targeted by this Prometheus deployment that have the desired version spec.\",\"format\":\"int32\",\"type\":\"integer\"}},\"required\":[\"paused\",\"replicas\",\"updatedReplicas\",\"availableReplicas\",\"unavailableReplicas\"],\"type\":\"object\"}},\"type\":\"object\"}},\"version\":\"v1\"}}\n" } }, "spec": { @@ -3045,7 +3045,7 @@ "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", "type": "array", "items": { - "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "description": "PersistentVolumeClaimCondition contains details about state of pvc", "type": "object", "required": [ "type", diff --git a/pkg/repository/udmrepo/repo-options.go b/pkg/repository/udmrepo/repo-options.go index b5fd48cf79..f11c0d9424 100644 --- a/pkg/repository/udmrepo/repo-options.go +++ b/pkg/repository/udmrepo/repo-options.go @@ -89,42 +89,42 @@ type StoreOptionsGetter interface { GetStoreOptions(param interface{}) (map[string]string, error) } -func NewRepoOptions(options ...func(*RepoOptions) error) (*RepoOptions, error) { - ro := &RepoOptions{} - for _, o := range options { - err := o(ro) +func NewRepoOptions(optionFuncs ...func(*RepoOptions) error) (*RepoOptions, error) { + options := &RepoOptions{} + for _, optionFunc := range optionFuncs { + err := optionFunc(options) if err != nil { return nil, err } } - return ro, nil + return options, nil } func WithPassword(getter PasswordGetter, param interface{}) func(*RepoOptions) error { - return func(ro *RepoOptions) error { + return func(options *RepoOptions) error { password, err := getter.GetPassword(param) if err != nil { return err } - ro.RepoPassword = password + options.RepoPassword = password return nil } } func WithConfigFile(workPath string, repoID string) func(*RepoOptions) error { - return func(ro *RepoOptions) error { - ro.ConfigFilePath = getRepoConfigFile(workPath, repoID) + return func(options *RepoOptions) error { + options.ConfigFilePath = getRepoConfigFile(workPath, repoID) return nil } } func WithGenOptions(genOptions map[string]string) func(*RepoOptions) error { - return func(ro *RepoOptions) error { + return func(options *RepoOptions) error { for k, v := range genOptions { - ro.GeneralOptions[k] = v + options.GeneralOptions[k] = v } return nil @@ -132,7 +132,7 @@ func WithGenOptions(genOptions map[string]string) func(*RepoOptions) error { } func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOptions) error { - return func(ro *RepoOptions) error { + return func(options *RepoOptions) error { storeType, err := getter.GetStoreType(param) if err != nil { return err @@ -143,10 +143,10 @@ func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOp return err } - ro.StorageType = storeType + options.StorageType = storeType for k, v := range storeOptions { - ro.StorageOptions[k] = v + options.StorageOptions[k] = v } return nil @@ -154,8 +154,8 @@ func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOp } func WithDescription(desc string) func(*RepoOptions) error { - return func(ro *RepoOptions) error { - ro.Description = desc + return func(options *RepoOptions) error { + options.Description = desc return nil } } diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 61bf7b7db0..9b3890c96a 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -157,7 +157,7 @@ func GetKubeconfigContext() error { func TestE2e(t *testing.T) { // Skip running E2E tests when running only "short" tests because: // 1. E2E tests are long running tests involving installation of Velero and performing backup and restore operations. - // 2. E2E tests require a kubernetes cluster to install and run velero which further requires ore configuration. See above referenced command line flags. + // 2. E2E tests require a kubernetes cluster to install and run velero which further requires more configuration. See above referenced command line flags. if testing.Short() { t.Skip("Skipping E2E tests") } From d58abb247751dd400cee1e5c6a96c63407259205 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 18 Aug 2022 19:38:48 +0800 Subject: [PATCH 253/366] repo init Signed-off-by: Lyndon-Li --- changelogs/unreleased/5231-lyndon | 1 + go.mod | 9 + go.sum | 10 + pkg/repository/provider/provider.go | 1 + pkg/repository/provider/unified_repo.go | 44 +++- pkg/repository/provider/unified_repo_test.go | 2 +- .../udmrepo/kopialib/backend/azure.go | 60 +++++ .../udmrepo/kopialib/backend/azure_test.go | 102 ++++++++ .../udmrepo/kopialib/backend/backend.go | 33 +++ .../udmrepo/kopialib/backend/common.go | 83 ++++++ .../udmrepo/kopialib/backend/file_system.go | 62 +++++ .../udmrepo/kopialib/backend/gcs.go | 54 ++++ .../udmrepo/kopialib/backend/gcs_test.go | 61 +++++ .../udmrepo/kopialib/backend/mocks/Logger.go | 65 +++++ .../udmrepo/kopialib/backend/mocks/Storage.go | 185 ++++++++++++++ .../udmrepo/kopialib/backend/mocks/Store.go | 68 +++++ pkg/repository/udmrepo/kopialib/backend/s3.go | 63 +++++ .../udmrepo/kopialib/backend/s3_test.go | 69 +++++ .../udmrepo/kopialib/backend/utils.go | 89 +++++++ .../udmrepo/kopialib/backend/utils_test.go | 87 +++++++ pkg/repository/udmrepo/kopialib/repo_init.go | 160 ++++++++++++ .../udmrepo/kopialib/repo_init_test.go | 237 ++++++++++++++++++ .../{repo-options.go => repo_options.go} | 42 +++- pkg/repository/udmrepo/service/service.go | 10 +- 24 files changed, 1578 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/5231-lyndon create mode 100644 pkg/repository/udmrepo/kopialib/backend/azure.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/azure_test.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/backend.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/common.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/file_system.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/gcs.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/gcs_test.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/mocks/Storage.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/mocks/Store.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/s3.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/s3_test.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/utils.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/utils_test.go create mode 100644 pkg/repository/udmrepo/kopialib/repo_init.go create mode 100644 pkg/repository/udmrepo/kopialib/repo_init_test.go rename pkg/repository/udmrepo/{repo-options.go => repo_options.go} (76%) diff --git a/changelogs/unreleased/5231-lyndon b/changelogs/unreleased/5231-lyndon new file mode 100644 index 0000000000..c6faa07e34 --- /dev/null +++ b/changelogs/unreleased/5231-lyndon @@ -0,0 +1 @@ +Add changes for Kopia Integration: Kopia Lib - initialize Kopia repo \ No newline at end of file diff --git a/go.mod b/go.mod index 158ce888ad..d01c71d58f 100644 --- a/go.mod +++ b/go.mod @@ -57,6 +57,9 @@ require ( cloud.google.com/go v0.100.2 // indirect cloud.google.com/go/compute v1.5.0 // indirect cloud.google.com/go/iam v0.1.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect @@ -69,6 +72,7 @@ require ( github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect @@ -93,6 +97,9 @@ require ( github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio-go/v7 v7.0.23 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -106,6 +113,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rs/xid v1.3.0 // indirect github.com/stretchr/objx v0.2.0 // indirect github.com/vladimirvivien/gexe v0.1.1 // indirect github.com/zeebo/blake3 v0.2.3 // indirect @@ -126,6 +134,7 @@ require ( google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index 58f63b5f24..4a1ebe18c9 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,11 @@ github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVt github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v61.4.0+incompatible h1:BF2Pm3aQWIa6q9KmxyF1JYKYXtVw67vtvu2Wd54NGuY= github.com/Azure/azure-sdk-for-go v61.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 h1:qoVeMsc9/fh/yhxVaA0obYjVH/oI/ihrOoMwsLS9KSA= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 h1:E+m3SkZCN0Bf5q7YdTs5lSm2CYY3CK4spn5OmUIiQtk= github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= @@ -231,10 +234,12 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -586,9 +591,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.23 h1:NleyGQvAn9VQMU+YHVrgV4CX+EPtxPt/78lHOOTncy4= github.com/minio/minio-go/v7 v7.0.23/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -736,6 +744,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1439,6 +1448,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216/go.mod h1:zJ2QpyDCYo1KvLXlmdnFlQAyF/Qfth0fB8239Qg7BIE= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/pkg/repository/provider/provider.go b/pkg/repository/provider/provider.go index 8e6a639a4a..2d78d64263 100644 --- a/pkg/repository/provider/provider.go +++ b/pkg/repository/provider/provider.go @@ -28,6 +28,7 @@ type RepoParam struct { BackupRepo *velerov1api.BackupRepository } +// Provider defines the methods to manipulate a backup repository type Provider interface { //InitRepo is to initialize a repository from a new storage place InitRepo(ctx context.Context, param RepoParam) error diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 994d0a5ca2..4cf2897cfc 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -62,6 +62,8 @@ const ( repoOpDescFullMaintain = "full maintenance" repoOpDescQuickMaintain = "quick maintenance" repoOpDescForget = "forget" + + repoConnectDesc = "unfied repo" ) // NewUnifiedRepoProvider creates the service provider for Unified Repo @@ -92,8 +94,14 @@ func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) e repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithGenOptions( + map[string]string{ + udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), + udmrepo.GenOptionOwnerDomain: udmrepo.GetRepoDomain(), + }, + ), udmrepo.WithStoreOptions(urp, param), - udmrepo.WithDescription(repoOpDescFullMaintain), + udmrepo.WithDescription(repoConnectDesc), ) if err != nil { @@ -121,8 +129,14 @@ func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoPar repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithGenOptions( + map[string]string{ + udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), + udmrepo.GenOptionOwnerDomain: udmrepo.GetRepoDomain(), + }, + ), udmrepo.WithStoreOptions(urp, param), - udmrepo.WithDescription(repoOpDescFullMaintain), + udmrepo.WithDescription(repoConnectDesc), ) if err != nil { @@ -150,8 +164,14 @@ func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithGenOptions( + map[string]string{ + udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), + udmrepo.GenOptionOwnerDomain: udmrepo.GetRepoDomain(), + }, + ), udmrepo.WithStoreOptions(urp, param), - udmrepo.WithDescription(repoOpDescFullMaintain), + udmrepo.WithDescription(repoConnectDesc), ) if err != nil { @@ -185,7 +205,11 @@ func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), - udmrepo.WithGenOptions(map[string]string{udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainFull}), + udmrepo.WithGenOptions( + map[string]string{ + udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainFull, + }, + ), udmrepo.WithDescription(repoOpDescFullMaintain), ) @@ -214,7 +238,11 @@ func (urp *unifiedRepoProvider) PruneRepoQuick(ctx context.Context, param RepoPa repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), - udmrepo.WithGenOptions(map[string]string{udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainQuick}), + udmrepo.WithGenOptions( + map[string]string{ + udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainQuick, + }, + ), udmrepo.WithDescription(repoOpDescQuickMaintain), ) @@ -280,7 +308,7 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p func (urp *unifiedRepoProvider) GetPassword(param interface{}) (string, error) { repoParam, ok := param.(RepoParam) if !ok { - return "", errors.New("invalid parameter") + return "", errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param) } repoPassword, err := getRepoPassword(urp.credentialGetter.FromSecret, repoParam) @@ -294,7 +322,7 @@ func (urp *unifiedRepoProvider) GetPassword(param interface{}) (string, error) { func (urp *unifiedRepoProvider) GetStoreType(param interface{}) (string, error) { repoParam, ok := param.(RepoParam) if !ok { - return "", errors.New("invalid parameter") + return "", errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param) } return getStorageType(repoParam.BackupLocation), nil @@ -303,7 +331,7 @@ func (urp *unifiedRepoProvider) GetStoreType(param interface{}) (string, error) func (urp *unifiedRepoProvider) GetStoreOptions(param interface{}) (map[string]string, error) { repoParam, ok := param.(RepoParam) if !ok { - return map[string]string{}, errors.New("invalid parameter") + return map[string]string{}, errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param) } storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, repoParam.BackupRepo.Spec.VolumeNamespace) diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index 8e41b9b41b..fe8d03abfd 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -503,7 +503,7 @@ func TestGetStoreOptions(t *testing.T) { name: "wrong param type", repoParam: struct{}{}, expected: map[string]string{}, - expectedErr: "invalid parameter", + expectedErr: "invalid parameter, expect provider.RepoParam, actual struct {}", }, { name: "get storage variable fail", diff --git a/pkg/repository/udmrepo/kopialib/backend/azure.go b/pkg/repository/udmrepo/kopialib/backend/azure.go new file mode 100644 index 0000000000..7aedc33d8f --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/azure.go @@ -0,0 +1,60 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + + "github.com/kopia/kopia/repo/blob" + "github.com/kopia/kopia/repo/blob/azure" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +type AzureBackend struct { + options azure.Options +} + +func (c *AzureBackend) Setup(ctx context.Context, flags map[string]string) error { + var err error + c.options.Container, err = mustHaveString(udmrepo.StoreOptionOssBucket, flags) + if err != nil { + return err + } + + c.options.StorageAccount, err = mustHaveString(udmrepo.StoreOptionAzureStorageAccount, flags) + if err != nil { + return err + } + + c.options.StorageKey, err = mustHaveString(udmrepo.StoreOptionAzureKey, flags) + if err != nil { + return err + } + + c.options.Prefix = optionalHaveString(udmrepo.StoreOptionPrefix, flags) + c.options.SASToken = optionalHaveString(udmrepo.StoreOptionAzureToken, flags) + c.options.StorageDomain = optionalHaveString(udmrepo.StoreOptionAzureDomain, flags) + + c.options.Limits = setupLimits(ctx, flags) + + return nil +} + +func (c *AzureBackend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { + return azure.New(ctx, &c.options) +} diff --git a/pkg/repository/udmrepo/kopialib/backend/azure_test.go b/pkg/repository/udmrepo/kopialib/backend/azure_test.go new file mode 100644 index 0000000000..bc4997fbe7 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/azure_test.go @@ -0,0 +1,102 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + + "github.com/kopia/kopia/repo/blob/azure" + "github.com/kopia/kopia/repo/blob/throttling" +) + +func TestAzureSetup(t *testing.T) { + testCases := []struct { + name string + flags map[string]string + expected azure.Options + expectedErr string + }{ + { + name: "must have bucket name", + flags: map[string]string{}, + expectedErr: "key " + udmrepo.StoreOptionOssBucket + " not found", + }, + { + name: "must have storage account", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + }, + expected: azure.Options{ + Container: "fake-bucket", + }, + expectedErr: "key " + udmrepo.StoreOptionAzureStorageAccount + " not found", + }, + { + name: "must have secret key", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + udmrepo.StoreOptionAzureStorageAccount: "fake-account", + }, + expected: azure.Options{ + Container: "fake-bucket", + StorageAccount: "fake-account", + }, + expectedErr: "key " + udmrepo.StoreOptionAzureKey + " not found", + }, + { + name: "with limits", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + udmrepo.StoreOptionAzureStorageAccount: "fake-account", + udmrepo.StoreOptionAzureKey: "fake-key", + udmrepo.ThrottleOptionReadOps: "100", + udmrepo.ThrottleOptionUploadBytes: "200", + }, + expected: azure.Options{ + Container: "fake-bucket", + StorageAccount: "fake-account", + StorageKey: "fake-key", + Limits: throttling.Limits{ + ReadsPerSecond: 100, + UploadBytesPerSecond: 200, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + azFlags := AzureBackend{} + + err := azFlags.Setup(context.Background(), tc.flags) + + require.Equal(t, tc.expected, azFlags.options) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/kopialib/backend/backend.go b/pkg/repository/udmrepo/kopialib/backend/backend.go new file mode 100644 index 0000000000..9993138636 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/backend.go @@ -0,0 +1,33 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + + "github.com/kopia/kopia/repo/blob" +) + +// Store defines the methods for Kopia to establish a connection to +// the backend storage +type Store interface { + // Setup setups the variables to a specific backend storage + Setup(ctx context.Context, flags map[string]string) error + + // Connect connects to a specific backend storage with the storage variables + Connect(ctx context.Context, isCreate bool) (blob.Storage, error) +} diff --git a/pkg/repository/udmrepo/kopialib/backend/common.go b/pkg/repository/udmrepo/kopialib/backend/common.go new file mode 100644 index 0000000000..7aa888e3a9 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/common.go @@ -0,0 +1,83 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "time" + + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/blob" + "github.com/kopia/kopia/repo/blob/throttling" + "github.com/kopia/kopia/repo/content" + "github.com/kopia/kopia/repo/encryption" + "github.com/kopia/kopia/repo/hashing" + "github.com/kopia/kopia/repo/object" + "github.com/kopia/kopia/repo/splitter" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +const ( + maxDataCacheMB = 2000 + maxMetadataCacheMB = 2000 + maxCacheDurationSecond = 30 +) + +func setupLimits(ctx context.Context, flags map[string]string) throttling.Limits { + return throttling.Limits{ + DownloadBytesPerSecond: optionalHaveFloat64(ctx, udmrepo.ThrottleOptionDownloadBytes, flags), + ListsPerSecond: optionalHaveFloat64(ctx, udmrepo.ThrottleOptionListOps, flags), + ReadsPerSecond: optionalHaveFloat64(ctx, udmrepo.ThrottleOptionReadOps, flags), + UploadBytesPerSecond: optionalHaveFloat64(ctx, udmrepo.ThrottleOptionUploadBytes, flags), + WritesPerSecond: optionalHaveFloat64(ctx, udmrepo.ThrottleOptionWriteOps, flags), + } +} + +// SetupNewRepositoryOptions setups the options when creating a new Kopia repository +func SetupNewRepositoryOptions(ctx context.Context, flags map[string]string) repo.NewRepositoryOptions { + return repo.NewRepositoryOptions{ + BlockFormat: content.FormattingOptions{ + Hash: optionalHaveStringWithDefault(udmrepo.StoreOptionGenHashAlgo, flags, hashing.DefaultAlgorithm), + Encryption: optionalHaveStringWithDefault(udmrepo.StoreOptionGenEncryptAlgo, flags, encryption.DefaultAlgorithm), + }, + + ObjectFormat: object.Format{ + Splitter: optionalHaveStringWithDefault(udmrepo.StoreOptionGenSplitAlgo, flags, splitter.DefaultAlgorithm), + }, + + RetentionMode: blob.RetentionMode(optionalHaveString(udmrepo.StoreOptionGenRetentionMode, flags)), + RetentionPeriod: optionalHaveDuration(ctx, udmrepo.StoreOptionGenRetentionPeriod, flags), + } +} + +// SetupConnectOptions setups the options when connecting to an existing Kopia repository +func SetupConnectOptions(ctx context.Context, repoOptions udmrepo.RepoOptions) repo.ConnectOptions { + return repo.ConnectOptions{ + CachingOptions: content.CachingOptions{ + MaxCacheSizeBytes: maxDataCacheMB << 20, + MaxMetadataCacheSizeBytes: maxMetadataCacheMB << 20, + MaxListCacheDuration: content.DurationSeconds(time.Duration(maxCacheDurationSecond) * time.Second), + }, + ClientOptions: repo.ClientOptions{ + Hostname: optionalHaveString(udmrepo.GenOptionOwnerDomain, repoOptions.GeneralOptions), + Username: optionalHaveString(udmrepo.GenOptionOwnerName, repoOptions.GeneralOptions), + ReadOnly: optionalHaveBool(ctx, udmrepo.StoreOptionGenReadOnly, repoOptions.GeneralOptions), + Description: repoOptions.Description, + }, + } +} diff --git a/pkg/repository/udmrepo/kopialib/backend/file_system.go b/pkg/repository/udmrepo/kopialib/backend/file_system.go new file mode 100644 index 0000000000..cf314ff598 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/file_system.go @@ -0,0 +1,62 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "path/filepath" + + "github.com/kopia/kopia/repo/blob" + "github.com/kopia/kopia/repo/blob/filesystem" + "github.com/pkg/errors" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +type FsBackend struct { + options filesystem.Options +} + +const ( + defaultFileMode = 0o600 + defaultDirMode = 0o700 +) + +func (c *FsBackend) Setup(ctx context.Context, flags map[string]string) error { + path, err := mustHaveString(udmrepo.StoreOptionFsPath, flags) + if err != nil { + return err + } + + prefix := optionalHaveString(udmrepo.StoreOptionPrefix, flags) + + c.options.Path = filepath.Join(path, prefix) + c.options.FileMode = defaultFileMode + c.options.DirectoryMode = defaultDirMode + + c.options.Limits = setupLimits(ctx, flags) + + return nil +} + +func (c *FsBackend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { + if !filepath.IsAbs(c.options.Path) { + return nil, errors.Errorf("filesystem repository path is not absolute, path: %s", c.options.Path) + } + + return filesystem.New(ctx, &c.options, isCreate) +} diff --git a/pkg/repository/udmrepo/kopialib/backend/gcs.go b/pkg/repository/udmrepo/kopialib/backend/gcs.go new file mode 100644 index 0000000000..b998c97028 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/gcs.go @@ -0,0 +1,54 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + + "github.com/kopia/kopia/repo/blob" + "github.com/kopia/kopia/repo/blob/gcs" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +type GCSBackend struct { + options gcs.Options +} + +func (c *GCSBackend) Setup(ctx context.Context, flags map[string]string) error { + var err error + c.options.BucketName, err = mustHaveString(udmrepo.StoreOptionOssBucket, flags) + if err != nil { + return err + } + + c.options.ServiceAccountCredentialsFile, err = mustHaveString(udmrepo.StoreOptionCredentialFile, flags) + if err != nil { + return err + } + + c.options.Prefix = optionalHaveString(udmrepo.StoreOptionPrefix, flags) + c.options.ReadOnly = optionalHaveBool(ctx, udmrepo.StoreOptionGcsReadonly, flags) + + c.options.Limits = setupLimits(ctx, flags) + + return nil +} + +func (c *GCSBackend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { + return gcs.New(ctx, &c.options) +} diff --git a/pkg/repository/udmrepo/kopialib/backend/gcs_test.go b/pkg/repository/udmrepo/kopialib/backend/gcs_test.go new file mode 100644 index 0000000000..7abdcab3e1 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/gcs_test.go @@ -0,0 +1,61 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +func TestGcsSetup(t *testing.T) { + testCases := []struct { + name string + flags map[string]string + expectedErr string + }{ + { + name: "must have bucket name", + flags: map[string]string{}, + expectedErr: "key " + udmrepo.StoreOptionOssBucket + " not found", + }, + { + name: "must have credential file", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + }, + expectedErr: "key " + udmrepo.StoreOptionCredentialFile + " not found", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gcsFlags := GCSBackend{} + + err := gcsFlags.Setup(context.Background(), tc.flags) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go b/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go new file mode 100644 index 0000000000..90a2199104 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go @@ -0,0 +1,65 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Logger is an autogenerated mock type for the Logger type +type Logger struct { + mock.Mock +} + +// Debugf provides a mock function with given fields: msg, args +func (_m *Logger) Debugf(msg string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, msg) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Debugw provides a mock function with given fields: msg, keyValuePairs +func (_m *Logger) Debugw(msg string, keyValuePairs ...interface{}) { + var _ca []interface{} + _ca = append(_ca, msg) + _ca = append(_ca, keyValuePairs...) + _m.Called(_ca...) +} + +// Errorf provides a mock function with given fields: msg, args +func (_m *Logger) Errorf(msg string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, msg) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Infof provides a mock function with given fields: msg, args +func (_m *Logger) Infof(msg string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, msg) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Warnf provides a mock function with given fields: msg, args +func (_m *Logger) Warnf(msg string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, msg) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +type mockConstructorTestingTNewLogger interface { + mock.TestingT + Cleanup(func()) +} + +// NewLogger creates a new instance of Logger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewLogger(t mockConstructorTestingTNewLogger) *Logger { + mock := &Logger{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/Storage.go b/pkg/repository/udmrepo/kopialib/backend/mocks/Storage.go new file mode 100644 index 0000000000..de49e75ff1 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/Storage.go @@ -0,0 +1,185 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + blob "github.com/kopia/kopia/repo/blob" + + mock "github.com/stretchr/testify/mock" +) + +// Storage is an autogenerated mock type for the Storage type +type Storage struct { + mock.Mock +} + +// Close provides a mock function with given fields: ctx +func (_m *Storage) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectionInfo provides a mock function with given fields: +func (_m *Storage) ConnectionInfo() blob.ConnectionInfo { + ret := _m.Called() + + var r0 blob.ConnectionInfo + if rf, ok := ret.Get(0).(func() blob.ConnectionInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(blob.ConnectionInfo) + } + + return r0 +} + +// DeleteBlob provides a mock function with given fields: ctx, blobID +func (_m *Storage) DeleteBlob(ctx context.Context, blobID blob.ID) error { + ret := _m.Called(ctx, blobID) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, blob.ID) error); ok { + r0 = rf(ctx, blobID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DisplayName provides a mock function with given fields: +func (_m *Storage) DisplayName() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// FlushCaches provides a mock function with given fields: ctx +func (_m *Storage) FlushCaches(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetBlob provides a mock function with given fields: ctx, blobID, offset, length, output +func (_m *Storage) GetBlob(ctx context.Context, blobID blob.ID, offset int64, length int64, output blob.OutputBuffer) error { + ret := _m.Called(ctx, blobID, offset, length, output) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, blob.ID, int64, int64, blob.OutputBuffer) error); ok { + r0 = rf(ctx, blobID, offset, length, output) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetCapacity provides a mock function with given fields: ctx +func (_m *Storage) GetCapacity(ctx context.Context) (blob.Capacity, error) { + ret := _m.Called(ctx) + + var r0 blob.Capacity + if rf, ok := ret.Get(0).(func(context.Context) blob.Capacity); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(blob.Capacity) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetMetadata provides a mock function with given fields: ctx, blobID +func (_m *Storage) GetMetadata(ctx context.Context, blobID blob.ID) (blob.Metadata, error) { + ret := _m.Called(ctx, blobID) + + var r0 blob.Metadata + if rf, ok := ret.Get(0).(func(context.Context, blob.ID) blob.Metadata); ok { + r0 = rf(ctx, blobID) + } else { + r0 = ret.Get(0).(blob.Metadata) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, blob.ID) error); ok { + r1 = rf(ctx, blobID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListBlobs provides a mock function with given fields: ctx, blobIDPrefix, cb +func (_m *Storage) ListBlobs(ctx context.Context, blobIDPrefix blob.ID, cb func(blob.Metadata) error) error { + ret := _m.Called(ctx, blobIDPrefix, cb) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, blob.ID, func(blob.Metadata) error) error); ok { + r0 = rf(ctx, blobIDPrefix, cb) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PutBlob provides a mock function with given fields: ctx, blobID, data, opts +func (_m *Storage) PutBlob(ctx context.Context, blobID blob.ID, data blob.Bytes, opts blob.PutOptions) error { + ret := _m.Called(ctx, blobID, data, opts) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, blob.ID, blob.Bytes, blob.PutOptions) error); ok { + r0 = rf(ctx, blobID, data, opts) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewStorage interface { + mock.TestingT + Cleanup(func()) +} + +// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewStorage(t mockConstructorTestingTNewStorage) *Storage { + mock := &Storage{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/Store.go b/pkg/repository/udmrepo/kopialib/backend/mocks/Store.go new file mode 100644 index 0000000000..7c09aa9efb --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/Store.go @@ -0,0 +1,68 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + blob "github.com/kopia/kopia/repo/blob" + + mock "github.com/stretchr/testify/mock" +) + +// Store is an autogenerated mock type for the Store type +type Store struct { + mock.Mock +} + +// Connect provides a mock function with given fields: ctx, isCreate +func (_m *Store) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { + ret := _m.Called(ctx, isCreate) + + var r0 blob.Storage + if rf, ok := ret.Get(0).(func(context.Context, bool) blob.Storage); ok { + r0 = rf(ctx, isCreate) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Storage) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { + r1 = rf(ctx, isCreate) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Setup provides a mock function with given fields: ctx, flags +func (_m *Store) Setup(ctx context.Context, flags map[string]string) error { + ret := _m.Called(ctx, flags) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) error); ok { + r0 = rf(ctx, flags) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewStore interface { + mock.TestingT + Cleanup(func()) +} + +// NewStore creates a new instance of Store. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewStore(t mockConstructorTestingTNewStore) *Store { + mock := &Store{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/kopialib/backend/s3.go b/pkg/repository/udmrepo/kopialib/backend/s3.go new file mode 100644 index 0000000000..38eeab1066 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/s3.go @@ -0,0 +1,63 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + + "github.com/kopia/kopia/repo/blob" + "github.com/kopia/kopia/repo/blob/s3" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +type S3Backend struct { + options s3.Options +} + +func (c *S3Backend) Setup(ctx context.Context, flags map[string]string) error { + var err error + c.options.BucketName, err = mustHaveString(udmrepo.StoreOptionOssBucket, flags) + if err != nil { + return err + } + + c.options.AccessKeyID, err = mustHaveString(udmrepo.StoreOptionS3KeyId, flags) + if err != nil { + return err + } + + c.options.SecretAccessKey, err = mustHaveString(udmrepo.StoreOptionS3SecretKey, flags) + if err != nil { + return err + } + + c.options.Endpoint = optionalHaveString(udmrepo.StoreOptionS3Endpoint, flags) + c.options.Region = optionalHaveString(udmrepo.StoreOptionOssRegion, flags) + c.options.Prefix = optionalHaveString(udmrepo.StoreOptionPrefix, flags) + c.options.DoNotUseTLS = optionalHaveBool(ctx, udmrepo.StoreOptionS3DisableTls, flags) + c.options.DoNotVerifyTLS = optionalHaveBool(ctx, udmrepo.StoreOptionS3DisableTlsVerify, flags) + c.options.SessionToken = optionalHaveString(udmrepo.StoreOptionS3Token, flags) + + c.options.Limits = setupLimits(ctx, flags) + + return nil +} + +func (c *S3Backend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { + return s3.New(ctx, &c.options) +} diff --git a/pkg/repository/udmrepo/kopialib/backend/s3_test.go b/pkg/repository/udmrepo/kopialib/backend/s3_test.go new file mode 100644 index 0000000000..493c1e904a --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/s3_test.go @@ -0,0 +1,69 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" +) + +func TestS3Setup(t *testing.T) { + testCases := []struct { + name string + flags map[string]string + expectedErr string + }{ + { + name: "must have bucket name", + flags: map[string]string{}, + expectedErr: "key " + udmrepo.StoreOptionOssBucket + " not found", + }, + { + name: "must have access key Id", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + }, + expectedErr: "key " + udmrepo.StoreOptionS3KeyId + " not found", + }, + { + name: "must have access key", + flags: map[string]string{ + udmrepo.StoreOptionOssBucket: "fake-bucket", + udmrepo.StoreOptionS3KeyId: "fake-key-id", + }, + expectedErr: "key " + udmrepo.StoreOptionS3SecretKey + " not found", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + s3Flags := S3Backend{} + + err := s3Flags.Setup(context.Background(), tc.flags) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/kopialib/backend/utils.go b/pkg/repository/udmrepo/kopialib/backend/utils.go new file mode 100644 index 0000000000..5aab1f1afb --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/utils.go @@ -0,0 +1,89 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "strconv" + "time" + + "github.com/kopia/kopia/repo/logging" + "github.com/pkg/errors" +) + +func mustHaveString(key string, flags map[string]string) (string, error) { + if value, exist := flags[key]; exist { + return value, nil + } else { + return "", errors.New("key " + key + " not found") + } +} + +func optionalHaveString(key string, flags map[string]string) string { + return optionalHaveStringWithDefault(key, flags, "") +} + +func optionalHaveBool(ctx context.Context, key string, flags map[string]string) bool { + if value, exist := flags[key]; exist { + ret, err := strconv.ParseBool(value) + if err == nil { + return ret + } + + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } + + return false +} + +func optionalHaveFloat64(ctx context.Context, key string, flags map[string]string) float64 { + if value, exist := flags[key]; exist { + ret, err := strconv.ParseFloat(value, 64) + if err == nil { + return ret + } + + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } + + return 0 +} + +func optionalHaveStringWithDefault(key string, flags map[string]string, defValue string) string { + if value, exist := flags[key]; exist { + return value + } else { + return defValue + } +} + +func optionalHaveDuration(ctx context.Context, key string, flags map[string]string) time.Duration { + if value, exist := flags[key]; exist { + ret, err := time.ParseDuration(value) + if err == nil { + return ret + } + + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } + + return 0 +} + +func backendLog() func(ctx context.Context) logging.Logger { + return logging.Module("kopialib-bd") +} diff --git a/pkg/repository/udmrepo/kopialib/backend/utils_test.go b/pkg/repository/udmrepo/kopialib/backend/utils_test.go new file mode 100644 index 0000000000..cb33236258 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/utils_test.go @@ -0,0 +1,87 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backend + +import ( + "context" + "fmt" + "testing" + + "github.com/kopia/kopia/repo/logging" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + storagemocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib/backend/mocks" +) + +func TestOptionalHaveBool(t *testing.T) { + var expectMsg string + testCases := []struct { + name string + key string + flags map[string]string + logger *storagemocks.Logger + retFuncErrorf func(mock.Arguments) + expectMsg string + retValue bool + }{ + { + name: "key not exist", + key: "fake-key", + flags: map[string]string{}, + retValue: false, + }, + { + name: "value valid", + key: "fake-key", + flags: map[string]string{ + "fake-key": "true", + }, + retValue: true, + }, + { + name: "value invalid", + key: "fake-key", + flags: map[string]string{ + "fake-key": "fake-value", + }, + logger: new(storagemocks.Logger), + retFuncErrorf: func(args mock.Arguments) { + expectMsg = fmt.Sprintf(args[0].(string), args[1].(string), args[2].(string), args[3].(error)) + }, + expectMsg: "Ignore fake-key, value [fake-value] is invalid, err strconv.ParseBool: parsing \"fake-value\": invalid syntax", + retValue: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.logger != nil { + tc.logger.On("Errorf", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(tc.retFuncErrorf) + } + + ctx := logging.WithLogger(context.Background(), func(module string) logging.Logger { + return tc.logger + }) + + retValue := optionalHaveBool(ctx, tc.key, tc.flags) + + require.Equal(t, retValue, tc.retValue) + require.Equal(t, tc.expectMsg, expectMsg) + }) + } +} diff --git a/pkg/repository/udmrepo/kopialib/repo_init.go b/pkg/repository/udmrepo/kopialib/repo_init.go new file mode 100644 index 0000000000..c6407bde47 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/repo_init.go @@ -0,0 +1,160 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopialib + +import ( + "context" + "strings" + + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/blob" + "github.com/pkg/errors" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib/backend" +) + +type kopiaBackendStore struct { + name string + description string + store backend.Store +} + +// backendStores lists the supported backend storages at present +var backendStores []kopiaBackendStore = []kopiaBackendStore{ + {udmrepo.StorageTypeAzure, "an Azure blob storage", &backend.AzureBackend{}}, + {udmrepo.StorageTypeFs, "a filesystem", &backend.FsBackend{}}, + {udmrepo.StorageTypeGcs, "a Google Cloud Storage bucket", &backend.GCSBackend{}}, + {udmrepo.StorageTypeS3, "an S3 bucket", &backend.S3Backend{}}, +} + +// CreateBackupRepo creates a Kopia repository and then connect to it. +// The storage must be empty, otherwise, it will fail +func CreateBackupRepo(ctx context.Context, repoOption udmrepo.RepoOptions) error { + if repoOption.ConfigFilePath == "" { + return errors.New("invalid config file path") + } + + backendStore, err := setupBackendStore(ctx, repoOption.StorageType, repoOption.StorageOptions) + if err != nil { + return errors.Wrap(err, "error to setup backend storage") + } + + st, err := backendStore.store.Connect(ctx, true) + if err != nil { + return errors.Wrap(err, "error to connect to storage") + } + + err = createWithStorage(ctx, st, repoOption) + if err != nil { + return errors.Wrap(err, "error to create repo with storage") + } + + err = connectWithStorage(ctx, st, repoOption) + if err != nil { + return errors.Wrap(err, "error to connect repo with storage") + } + + return nil +} + +// ConnectBackupRepo connects to an existing Kopia repository. +// If the repository doesn't exist, it will fail +func ConnectBackupRepo(ctx context.Context, repoOption udmrepo.RepoOptions) error { + if repoOption.ConfigFilePath == "" { + return errors.New("invalid config file path") + } + + backendStore, err := setupBackendStore(ctx, repoOption.StorageType, repoOption.StorageOptions) + if err != nil { + return errors.Wrap(err, "error to setup backend storage") + } + + st, err := backendStore.store.Connect(ctx, false) + if err != nil { + return errors.Wrap(err, "error to connect to storage") + } + + err = connectWithStorage(ctx, st, repoOption) + if err != nil { + return errors.Wrap(err, "error to connect repo with storage") + } + + return nil +} + +func findBackendStore(storage string) *kopiaBackendStore { + for _, options := range backendStores { + if strings.EqualFold(options.name, storage) { + return &options + } + } + + return nil +} + +func setupBackendStore(ctx context.Context, storageType string, storageOptions map[string]string) (*kopiaBackendStore, error) { + backendStore := findBackendStore(storageType) + if backendStore == nil { + return nil, errors.New("error to find storage type") + } + + err := backendStore.store.Setup(ctx, storageOptions) + if err != nil { + return nil, errors.Wrap(err, "error to setup storage") + } + + return backendStore, nil +} + +func createWithStorage(ctx context.Context, st blob.Storage, repoOption udmrepo.RepoOptions) error { + err := ensureEmpty(ctx, st) + if err != nil { + return errors.Wrap(err, "error to ensure repository storage empty") + } + + options := backend.SetupNewRepositoryOptions(ctx, repoOption.GeneralOptions) + + if err := repo.Initialize(ctx, st, &options, repoOption.RepoPassword); err != nil { + return errors.Wrap(err, "error to initialize repository") + } + + return nil +} + +func connectWithStorage(ctx context.Context, st blob.Storage, repoOption udmrepo.RepoOptions) error { + options := backend.SetupConnectOptions(ctx, repoOption) + if err := repo.Connect(ctx, repoOption.ConfigFilePath, st, repoOption.RepoPassword, &options); err != nil { + return errors.Wrap(err, "error to connect to repository") + } + + return nil +} + +func ensureEmpty(ctx context.Context, s blob.Storage) error { + hasDataError := errors.Errorf("has data") + + err := s.ListBlobs(ctx, "", func(cb blob.Metadata) error { + return hasDataError + }) + + if errors.Is(err, hasDataError) { + return errors.New("found existing data in storage location") + } + + return errors.Wrap(err, "error to list blobs") +} diff --git a/pkg/repository/udmrepo/kopialib/repo_init_test.go b/pkg/repository/udmrepo/kopialib/repo_init_test.go new file mode 100644 index 0000000000..f91296d1f3 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/repo_init_test.go @@ -0,0 +1,237 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopialib + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + storagemocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib/backend/mocks" + + "github.com/pkg/errors" +) + +type comparableError struct { + message string +} + +func (ce *comparableError) Error() string { + return ce.message +} + +func (ce *comparableError) Is(err error) bool { + return err.Error() == ce.message +} + +func TestCreateBackupRepo(t *testing.T) { + testCases := []struct { + name string + backendStore *storagemocks.Store + repoOptions udmrepo.RepoOptions + connectErr error + setupError error + returnStore *storagemocks.Storage + storeListErr error + getBlobErr error + listBlobErr error + expectedErr string + }{ + { + name: "invalid config file", + expectedErr: "invalid config file path", + }, + { + name: "storage setup fail, invalid type", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + }, + expectedErr: "error to setup backend storage: error to find storage type", + }, + { + name: "storage setup fail, backend store steup fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + setupError: errors.New("fake-setup-error"), + expectedErr: "error to setup backend storage: error to setup storage: fake-setup-error", + }, + { + name: "storage connect fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + connectErr: errors.New("fake-connect-error"), + expectedErr: "error to connect to storage: fake-connect-error", + }, + { + name: "create repository error, exist blobs", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + returnStore: new(storagemocks.Storage), + listBlobErr: &comparableError{ + message: "has data", + }, + expectedErr: "error to create repo with storage: error to ensure repository storage empty: found existing data in storage location", + }, + { + name: "create repository error, error list blobs", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + returnStore: new(storagemocks.Storage), + listBlobErr: errors.New("fake-list-blob-error"), + expectedErr: "error to create repo with storage: error to ensure repository storage empty: error to list blobs: fake-list-blob-error", + }, + { + name: "create repository error, initialize error", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + returnStore: new(storagemocks.Storage), + getBlobErr: errors.New("fake-list-blob-error-01"), + expectedErr: "error to create repo with storage: error to initialize repository: unexpected error when checking for format blob: fake-list-blob-error-01", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + backendStores = []kopiaBackendStore{ + {udmrepo.StorageTypeAzure, "fake store", tc.backendStore}, + {udmrepo.StorageTypeFs, "fake store", tc.backendStore}, + {udmrepo.StorageTypeGcs, "fake store", tc.backendStore}, + {udmrepo.StorageTypeS3, "fake store", tc.backendStore}, + } + + if tc.backendStore != nil { + tc.backendStore.On("Connect", mock.Anything, mock.Anything, mock.Anything).Return(tc.returnStore, tc.connectErr) + tc.backendStore.On("Setup", mock.Anything, mock.Anything, mock.Anything).Return(tc.setupError) + } + + if tc.returnStore != nil { + tc.returnStore.On("ListBlobs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.listBlobErr) + tc.returnStore.On("GetBlob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.getBlobErr) + } + + err := CreateBackupRepo(context.Background(), tc.repoOptions) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestConnectBackupRepo(t *testing.T) { + testCases := []struct { + name string + backendStore *storagemocks.Store + repoOptions udmrepo.RepoOptions + connectErr error + setupError error + returnStore *storagemocks.Storage + getBlobErr error + expectedErr string + }{ + { + name: "invalid config file", + expectedErr: "invalid config file path", + }, + { + name: "storage setup fail, invalid type", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + }, + expectedErr: "error to setup backend storage: error to find storage type", + }, + { + name: "storage setup fail, backend store steup fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + setupError: errors.New("fake-setup-error"), + expectedErr: "error to setup backend storage: error to setup storage: fake-setup-error", + }, + { + name: "storage connect fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + connectErr: errors.New("fake-connect-error"), + expectedErr: "error to connect to storage: fake-connect-error", + }, + { + name: "connect repository error", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + StorageType: udmrepo.StorageTypeAzure, + }, + backendStore: new(storagemocks.Store), + returnStore: new(storagemocks.Storage), + getBlobErr: errors.New("fake-get-blob-error"), + expectedErr: "error to connect repo with storage: error to connect to repository: unable to read format blob: fake-get-blob-error", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + backendStores = []kopiaBackendStore{ + {udmrepo.StorageTypeAzure, "fake store", tc.backendStore}, + {udmrepo.StorageTypeFs, "fake store", tc.backendStore}, + {udmrepo.StorageTypeGcs, "fake store", tc.backendStore}, + {udmrepo.StorageTypeS3, "fake store", tc.backendStore}, + } + + if tc.backendStore != nil { + tc.backendStore.On("Connect", mock.Anything, mock.Anything, mock.Anything).Return(tc.returnStore, tc.connectErr) + tc.backendStore.On("Setup", mock.Anything, mock.Anything, mock.Anything).Return(tc.setupError) + } + + if tc.returnStore != nil { + tc.returnStore.On("GetBlob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.getBlobErr) + } + + err := ConnectBackupRepo(context.Background(), tc.repoOptions) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/repo-options.go b/pkg/repository/udmrepo/repo_options.go similarity index 76% rename from pkg/repository/udmrepo/repo-options.go rename to pkg/repository/udmrepo/repo_options.go index f11c0d9424..f4a043ff2a 100644 --- a/pkg/repository/udmrepo/repo-options.go +++ b/pkg/repository/udmrepo/repo_options.go @@ -32,6 +32,9 @@ const ( GenOptionMaintainFull = "full" GenOptionMaintainQuick = "quick" + GenOptionOwnerName = "username" + GenOptionOwnerDomain = "domainname" + StoreOptionS3KeyId = "accessKeyID" StoreOptionS3Provider = "providerName" StoreOptionS3SecretKey = "secretAccessKey" @@ -56,6 +59,14 @@ const ( StoreOptionPrefix = "prefix" StoreOptionPrefixName = "unified-repo" + StoreOptionGenHashAlgo = "hashAlgo" + StoreOptionGenEncryptAlgo = "encryptAlgo" + StoreOptionGenSplitAlgo = "splitAlgo" + + StoreOptionGenRetentionMode = "retentionMode" + StoreOptionGenRetentionPeriod = "retentionPeriod" + StoreOptionGenReadOnly = "readOnly" + ThrottleOptionReadOps = "readOPS" ThrottleOptionWriteOps = "writeOPS" ThrottleOptionListOps = "listOPS" @@ -63,6 +74,11 @@ const ( ThrottleOptionDownloadBytes = "downloadBytes" ) +const ( + defaultUsername = "default" + defaultDomain = "default" +) + type RepoOptions struct { // StorageType is a repository specific string to identify a backup storage, i.e., "s3", "filesystem" StorageType string @@ -80,17 +96,24 @@ type RepoOptions struct { Description string } +// PasswordGetter defines the method to get a repository password. type PasswordGetter interface { GetPassword(param interface{}) (string, error) } +// StoreOptionsGetter defines the methods to get the storage related options. type StoreOptionsGetter interface { GetStoreType(param interface{}) (string, error) GetStoreOptions(param interface{}) (map[string]string, error) } +// NewRepoOptions creates a new RepoOptions for different purpose func NewRepoOptions(optionFuncs ...func(*RepoOptions) error) (*RepoOptions, error) { - options := &RepoOptions{} + options := &RepoOptions{ + GeneralOptions: make(map[string]string), + StorageOptions: make(map[string]string), + } + for _, optionFunc := range optionFuncs { err := optionFunc(options) if err != nil { @@ -101,6 +124,8 @@ func NewRepoOptions(optionFuncs ...func(*RepoOptions) error) (*RepoOptions, erro return options, nil } +// WithPassword sets the RepoPassword to RepoOptions, the password is acquired through +// the provided interface func WithPassword(getter PasswordGetter, param interface{}) func(*RepoOptions) error { return func(options *RepoOptions) error { password, err := getter.GetPassword(param) @@ -114,6 +139,7 @@ func WithPassword(getter PasswordGetter, param interface{}) func(*RepoOptions) e } } +// WithConfigFile sets the ConfigFilePath to RepoOptions func WithConfigFile(workPath string, repoID string) func(*RepoOptions) error { return func(options *RepoOptions) error { options.ConfigFilePath = getRepoConfigFile(workPath, repoID) @@ -121,6 +147,7 @@ func WithConfigFile(workPath string, repoID string) func(*RepoOptions) error { } } +// WithGenOptions sets the GeneralOptions to RepoOptions func WithGenOptions(genOptions map[string]string) func(*RepoOptions) error { return func(options *RepoOptions) error { for k, v := range genOptions { @@ -131,6 +158,8 @@ func WithGenOptions(genOptions map[string]string) func(*RepoOptions) error { } } +// WithStoreOptions sets the StorageOptions to RepoOptions, the store options are acquired through +// the provided interface func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOptions) error { return func(options *RepoOptions) error { storeType, err := getter.GetStoreType(param) @@ -153,6 +182,7 @@ func WithStoreOptions(getter StoreOptionsGetter, param interface{}) func(*RepoOp } } +// WithDescription sets the Description to RepoOptions func WithDescription(desc string) func(*RepoOptions) error { return func(options *RepoOptions) error { options.Description = desc @@ -160,6 +190,16 @@ func WithDescription(desc string) func(*RepoOptions) error { } } +// GetRepoUser returns the default username that is used to manipulate the Unified Repo +func GetRepoUser() string { + return defaultUsername +} + +// GetRepoDomain returns the default user domain that is used to manipulate the Unified Repo +func GetRepoDomain() string { + return defaultDomain +} + func getRepoConfigFile(workPath string, repoID string) string { if workPath == "" { workPath = filepath.Join(os.Getenv("HOME"), "udmrepo") diff --git a/pkg/repository/udmrepo/service/service.go b/pkg/repository/udmrepo/service/service.go index 55fbb03c8f..445063ff04 100644 --- a/pkg/repository/udmrepo/service/service.go +++ b/pkg/repository/udmrepo/service/service.go @@ -22,16 +22,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" ) -const ( - defaultUsername = "default" - defaultDomain = "default" -) - +// Create creates an instance of BackupRepoService func Create(logger logrus.FieldLogger) udmrepo.BackupRepoService { ///TODO: create from kopiaLib return nil } - -func GetRepoUser() (username, domain string) { - return defaultUsername, defaultDomain -} From 6e8061266c1df11cc3e7b323e609e33536c68cf7 Mon Sep 17 00:00:00 2001 From: danfengl Date: Mon, 15 Aug 2022 07:35:30 +0000 Subject: [PATCH 254/366] Clean backups after each test and fix exlude label test issue 1. Clean backups after each test to avoid exceeding limitation of storage capability during E2E test; 2. Fix exlude label test issue that namespace should not be included and excluded at the same time no matter by which way to config. Signed-off-by: danfengl --- test/e2e/Makefile | 3 + test/e2e/backup/backup.go | 7 +- test/e2e/backups/deletion.go | 8 +- test/e2e/backups/sync_backups.go | 18 ++- test/e2e/backups/ttl.go | 11 +- test/e2e/basic/enable_api_group_versions.go | 31 +++-- test/e2e/bsl-mgmt/deletion.go | 16 ++- test/e2e/migration/migration.go | 16 ++- test/e2e/resource-filtering/exclude_label.go | 138 +++++++++++-------- test/e2e/test/test.go | 22 ++- test/e2e/upgrade/upgrade.go | 13 +- test/e2e/util/velero/velero_utils.go | 18 +++ 12 files changed, 195 insertions(+), 106 deletions(-) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index f676fabd6c..53d52364e7 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -89,6 +89,9 @@ ADDITIONAL_BSL_CONFIG ?= FEATURES ?= DEBUG_E2E_TEST ?= false +# Parameters to run migration tests along with all other E2E tests, and both of them should +# be provided or left them all empty to skip migration tests with no influence to other +# E2E tests. DEFAULT_CLUSTER ?= STANDBY_CLUSTER ?= diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index e4e62db593..5507737180 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -59,8 +59,11 @@ func BackupRestoreTest(useVolumeSnapshots bool) { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + if VeleroCfg.InstallVelero { err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) Expect(err).To(Succeed()) } diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 0663e554ad..59944d6aa6 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -64,12 +64,16 @@ func backup_deletion_test(useVolumeSnapshots bool) { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + if VeleroCfg.InstallVelero { err = VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) Expect(err).To(Succeed()) } } + }) When("kibishii is the sample workload", func() { diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index db062e67d3..fc2c273373 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -64,11 +64,15 @@ func BackupsSyncTest() { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + if VeleroCfg.InstallVelero { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) } } + }) It("Backups in object storage should be synced to a new Velero successfully", func() { @@ -76,10 +80,11 @@ func BackupsSyncTest() { By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS)) Expect(CreateNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) - - defer func() { - Expect(DeleteNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) - }() + if !VeleroCfg.Debug { + defer func() { + Expect(DeleteNamespace(test.ctx, *VeleroCfg.ClientToInstallVelero, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) + }() + } var BackupCfg BackupConfig BackupCfg.BackupName = test.backupName @@ -120,7 +125,6 @@ func BackupsSyncTest() { fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) }() } - var BackupCfg BackupConfig BackupCfg.BackupName = test.backupName BackupCfg.Namespace = test.testNS diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index 78f1915a11..b2e05a17ba 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -76,12 +76,15 @@ func TTLTest() { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - VeleroCfg.GCFrequency = "" - if !VeleroCfg.Debug { + VeleroCfg.GCFrequency = "" + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + if VeleroCfg.InstallVelero { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) - Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) } + Expect(DeleteNamespace(test.ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) } }) diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 86afaf8926..220f6a0298 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -63,22 +63,25 @@ func APIGropuVersionsTest() { }) AfterEach(func() { - fmt.Printf("Clean up resource: kubectl delete crd %s.%s\n", resource, group) - cmd := exec.CommandContext(ctx, "kubectl", "delete", "crd", resource+"."+group) - _, stderr, err := veleroexec.RunCommand(cmd) - if strings.Contains(stderr, "NotFound") { - fmt.Printf("Ignore error: %v\n", stderr) - err = nil - } - Expect(err).NotTo(HaveOccurred()) - - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { - err = VeleroUninstall(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace) - Expect(err).NotTo(HaveOccurred()) + if !VeleroCfg.Debug { + fmt.Printf("Clean up resource: kubectl delete crd %s.%s\n", resource, group) + cmd := exec.CommandContext(ctx, "kubectl", "delete", "crd", resource+"."+group) + _, stderr, err := veleroexec.RunCommand(cmd) + if strings.Contains(stderr, "NotFound") { + fmt.Printf("Ignore error: %v\n", stderr) + err = nil + } + Expect(err).NotTo(HaveOccurred()) + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + if VeleroCfg.InstallVelero { + + By("Uninstall Velero", func() { + Expect(VeleroUninstall(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).NotTo(HaveOccurred()) + }) } } - }) Context("When EnableAPIGroupVersions flag is set", func() { diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index 9800237d42..d7504342a6 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -71,15 +71,23 @@ func BslDeletionTest(useVolumeSnapshots bool) { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.DefaultClient) + }) + By(fmt.Sprintf("Delete sample workload namespace %s", bslDeletionTestNs), func() { Expect(DeleteNamespace(context.Background(), *VeleroCfg.ClientToInstallVelero, bslDeletionTestNs, true)).To(Succeed(), fmt.Sprintf("failed to delete the namespace %q", bslDeletionTestNs)) - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace)).To(Succeed()) + }) + if VeleroCfg.InstallVelero { + By("Uninstall Velero", func() { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + }) } } + }) When("kibishii is the sample workload", func() { diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index f0f376f611..5c31ca9b78 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -68,8 +68,11 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) } }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.DefaultClient) + }) + if VeleroCfg.InstallVelero { By(fmt.Sprintf("Uninstall Velero and delete sample workload namespace %s", migrationNamespace), func() { Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, @@ -81,12 +84,13 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) VeleroCfg.VeleroNamespace)).To(Succeed()) DeleteNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace, true) }) - By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() { - Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) - VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient - }) } + By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient + }) } + }) When("kibishii is the sample workload", func() { It("should be successfully backed up and restored to the default BackupStorageLocation", func() { diff --git a/test/e2e/resource-filtering/exclude_label.go b/test/e2e/resource-filtering/exclude_label.go index 72307fe3fa..2147221c93 100644 --- a/test/e2e/resource-filtering/exclude_label.go +++ b/test/e2e/resource-filtering/exclude_label.go @@ -19,12 +19,14 @@ package filtering import ( "context" "fmt" - "strings" "time" - "github.com/pkg/errors" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/pkg/errors" + . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/test" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" @@ -62,7 +64,7 @@ func (e *ExcludeFromBackup) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, - "--include-namespaces", strings.Join(*e.NSIncluded, ","), + "--include-namespaces", e.NSBaseName, "--default-volumes-to-restic", "--wait", } @@ -75,63 +77,87 @@ func (e *ExcludeFromBackup) Init() error { func (e *ExcludeFromBackup) CreateResources() error { e.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) - for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) - fmt.Printf("Creating resources in namespace ...%s\n", namespace) - labels := e.labels - if nsNum%2 == 0 { - labels = map[string]string{ - "velero.io/exclude-from-backup": "false", - } - } - if err := CreateNamespaceWithLabel(e.Ctx, e.Client, namespace, labels); err != nil { - return errors.Wrapf(err, "Failed to create namespace %s", namespace) - } - serviceAccountName := "default" - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(e.Ctx, e.Client, namespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, namespace) - } - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(e.Ctx, e.Client, namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, namespace) - } - //Create deployment - fmt.Printf("Creating deployment in namespaces ...%s\n", namespace) - - deployment := NewDeployment(e.NSBaseName, namespace, e.replica, labels) - deployment, err := CreateDeployment(e.Client.ClientGo, namespace, deployment) - if err != nil { - return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) - } - err = WaitForReadyDeployment(e.Client.ClientGo, namespace, deployment.Name) - if err != nil { - return errors.Wrap(err, fmt.Sprintf("failed to ensure deployment completion in namespace: %q", namespace)) - } + namespace := e.NSBaseName + // These 2 labels for resources to be included + label1 := map[string]string{ + "meaningless-label-resource-to-include": "true", + } + label2 := map[string]string{ + "velero.io/exclude-from-backup": "false", + } + fmt.Printf("Creating resources in namespace ...%s\n", namespace) + if err := CreateNamespace(e.Ctx, e.Client, namespace); err != nil { + return errors.Wrapf(err, "Failed to create namespace %s", namespace) + } + serviceAccountName := "default" + // wait until the service account is created before patch the image pull secret + if err := WaitUntilServiceAccountCreated(e.Ctx, e.Client, namespace, serviceAccountName, 10*time.Minute); err != nil { + return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, namespace) + } + // add the image pull secret to avoid the image pull limit issue of Docker Hub + if err := PatchServiceAccountWithImagePullSecret(e.Ctx, e.Client, namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { + return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, namespace) + } + //Create deployment: to be included + fmt.Printf("Creating deployment in namespaces ...%s\n", namespace) + deployment := NewDeployment(e.NSBaseName, namespace, e.replica, label2) + deployment, err := CreateDeployment(e.Client.ClientGo, namespace, deployment) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) + } + err = WaitForReadyDeployment(e.Client.ClientGo, namespace, deployment.Name) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", namespace)) + } + //Create Secret + secretName := e.NSBaseName + fmt.Printf("Creating secret %s in namespaces ...%s\n", secretName, namespace) + _, err = CreateSecret(e.Client.ClientGo, namespace, secretName, e.labels) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to create secret in the namespace %q", namespace)) + } + err = WaitForSecretsComplete(e.Client.ClientGo, namespace, secretName) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", namespace)) + } + By(fmt.Sprintf("Checking secret %s should exists in namespaces ...%s\n", secretName, namespace), func() { + _, err = GetSecret(e.Client.ClientGo, namespace, e.NSBaseName) + Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) + }) + //Create Configmap: to be included + configmaptName := e.NSBaseName + fmt.Printf("Creating configmap %s in namespaces ...%s\n", configmaptName, namespace) + _, err = CreateConfigMap(e.Client.ClientGo, namespace, configmaptName, label1) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to create configmap in the namespace %q", namespace)) + } + err = WaitForConfigMapComplete(e.Client.ClientGo, namespace, configmaptName) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", namespace)) } return nil } func (e *ExcludeFromBackup) Verify() error { - for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) - fmt.Printf("Checking resources in namespaces ...%s\n", namespace) - //Check deployment - _, err := GetDeployment(e.Client.ClientGo, namespace, e.NSBaseName) - if nsNum%2 == 0 { //include - if err != nil { - return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) - } - } else { //exclude - if err == nil { - return fmt.Errorf("failed to exclude deployment in namespaces %q", namespace) - } else { - if apierrors.IsNotFound(err) { //resource should be excluded - return nil - } - return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) - } - } - } + namespace := e.NSBaseName + By(fmt.Sprintf("Checking resources in namespaces ...%s\n", namespace), func() { + //Check namespace + checkNS, err := GetNamespace(e.Ctx, e.Client, namespace) + Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("Could not retrieve test namespace %s", namespace)) + Expect(checkNS.Name == namespace).To(Equal(true), fmt.Sprintf("Retrieved namespace for %s has name %s instead", namespace, checkNS.Name)) + + //Check deployment: should be included + _, err = GetDeployment(e.Client.ClientGo, namespace, e.NSBaseName) + Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) + + //Check secrets: secrets should not be included + _, err = GetSecret(e.Client.ClientGo, namespace, e.NSBaseName) + Expect(err).Should(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) + Expect(apierrors.IsNotFound(err)).To(Equal(true)) + + //Check configmap: should be included + _, err = GetConfigmap(e.Client.ClientGo, namespace, e.NSBaseName) + Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list configmap in namespace: %q", namespace)) + }) return nil } diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index fe0ad7e9d9..dc8597512a 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -83,8 +83,8 @@ func TestFunc(test VeleroBackupRestoreTest) func() { } }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { + if !VeleroCfg.Debug { + if VeleroCfg.InstallVelero { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) } } @@ -117,9 +117,11 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if countIt == len(tests) && !VeleroCfg.Debug { - Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) + if !VeleroCfg.Debug { + if VeleroCfg.InstallVelero { + if countIt == len(tests) && !VeleroCfg.Debug { + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To((Succeed())) + } } } }) @@ -174,7 +176,15 @@ func (t *TestCase) Verify() error { } func (t *TestCase) Clean() error { - return CleanupNamespacesWithPoll(t.Ctx, t.Client, t.NSBaseName) + if !VeleroCfg.Debug { + By(fmt.Sprintf("Clean namespace with prefix %s after test", t.NSBaseName), func() { + CleanupNamespaces(t.Ctx, t.Client, t.NSBaseName) + }) + By("Clean backups after test", func() { + DeleteBackups(t.Ctx, t.Client) + }) + } + return nil } func (t *TestCase) GetTestMsg() *TestMSG { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 488e25961b..c862730381 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -70,11 +70,14 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC } }) AfterEach(func() { - if VeleroCfg.InstallVelero { - if !VeleroCfg.Debug { - By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { - DeleteNamespace(context.Background(), *VeleroCfg.ClientToInstallVelero, upgradeNamespace, true) - }) + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.ClientToInstallVelero) + }) + By(fmt.Sprintf("Delete sample workload namespace %s", upgradeNamespace), func() { + DeleteNamespace(context.Background(), *VeleroCfg.ClientToInstallVelero, upgradeNamespace, true) + }) + if VeleroCfg.InstallVelero { By("Uninstall Velero", func() { Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed()) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index aa54a0cfff..e7bc0404e4 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -37,6 +37,8 @@ import ( "github.com/pkg/errors" "k8s.io/apimachinery/pkg/util/wait" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" cliinstall "github.com/vmware-tanzu/velero/pkg/cmd/cli/install" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" @@ -433,6 +435,9 @@ func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { cmd.Stderr = os.Stderr fmt.Printf("velero cmd =%v\n", cmd) err := cmd.Run() + if strings.Contains(fmt.Sprint(cmd.Stdout), "Failed") { + return errors.New(fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) + } if err != nil { return err } @@ -1008,3 +1013,16 @@ func GetVersionList(veleroCli, veleroVersion string) []VeleroCLI2Version { } return veleroCLI2VersionList } +func DeleteBackups(ctx context.Context, client TestClient) error { + backupList := new(velerov1api.BackupList) + if err := client.Kubebuilder.List(ctx, backupList, &kbclient.ListOptions{Namespace: VeleroCfg.VeleroNamespace}); err != nil { + return fmt.Errorf("failed to list backup object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) + } + for _, backup := range backupList.Items { + fmt.Printf("Backup %s is going to be deleted...", backup.Name) + if err := VeleroBackupDelete(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup.Name); err != nil { + return err + } + } + return nil +} From 262de19f52c8230ebde92a7133b5424c6a786750 Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 8 Aug 2022 07:10:04 +0000 Subject: [PATCH 255/366] Uploader Implementation: Kopia backup and restore Signed-off-by: Ming --- changelogs/unreleased/5221-qiuming-best | 1 + go.sum | 1 + .../v1/pod_volume_operation_progress.go | 55 +++ pkg/apis/velero/v1/progress.go | 26 ++ .../pod_volume_backup_controller.go | 12 - .../pod_volume_restore_controller.go | 12 - pkg/repository/mocks/repository_writer.go | 349 ++++++++++++++++++ pkg/uploader/kopia/progress.go | 13 +- pkg/uploader/kopia/snapshot.go | 286 ++++++++++++++ pkg/uploader/kopia/snapshot_test.go | 199 ++++++++++ pkg/uploader/mocks/policy.go | 92 +++++ pkg/uploader/mocks/shim.go | 42 +++ pkg/uploader/mocks/snapshot.go | 76 ++++ pkg/uploader/mocks/uploader.go | 63 ++++ pkg/uploader/provider/kopia.go | 204 ++++++++++ pkg/uploader/provider/kopia_test.go | 118 ++++++ pkg/uploader/provider/provider.go | 32 +- pkg/uploader/provider/restic.go | 34 ++ pkg/uploader/types.go | 11 +- 19 files changed, 1582 insertions(+), 44 deletions(-) create mode 100644 changelogs/unreleased/5221-qiuming-best create mode 100644 pkg/apis/velero/v1/progress.go create mode 100644 pkg/repository/mocks/repository_writer.go create mode 100644 pkg/uploader/kopia/snapshot.go create mode 100644 pkg/uploader/kopia/snapshot_test.go create mode 100644 pkg/uploader/mocks/policy.go create mode 100644 pkg/uploader/mocks/shim.go create mode 100644 pkg/uploader/mocks/snapshot.go create mode 100644 pkg/uploader/mocks/uploader.go create mode 100644 pkg/uploader/provider/kopia.go create mode 100644 pkg/uploader/provider/kopia_test.go create mode 100644 pkg/uploader/provider/restic.go diff --git a/changelogs/unreleased/5221-qiuming-best b/changelogs/unreleased/5221-qiuming-best new file mode 100644 index 0000000000..2de71e91ab --- /dev/null +++ b/changelogs/unreleased/5221-qiuming-best @@ -0,0 +1 @@ +Uploader Implementation: Kopia backup and restore diff --git a/go.sum b/go.sum index 58f63b5f24..9ec412ef4f 100644 --- a/go.sum +++ b/go.sum @@ -547,6 +547,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= diff --git a/pkg/apis/velero/v1/pod_volume_operation_progress.go b/pkg/apis/velero/v1/pod_volume_operation_progress.go index ceb67a87ea..4461a7e7ab 100644 --- a/pkg/apis/velero/v1/pod_volume_operation_progress.go +++ b/pkg/apis/velero/v1/pod_volume_operation_progress.go @@ -16,6 +16,13 @@ limitations under the License. package v1 +import ( + "context" + + "github.com/sirupsen/logrus" + "sigs.k8s.io/controller-runtime/pkg/client" +) + // PodVolumeOperationProgress represents the progress of a // PodVolumeBackup/Restore (restic) operation type PodVolumeOperationProgress struct { @@ -25,3 +32,51 @@ type PodVolumeOperationProgress struct { // +optional BytesDone int64 `json:"bytesDone,omitempty"` } + +type BackupProgressUpdater struct { + pvb *PodVolumeBackup + log logrus.FieldLogger + ctx context.Context + cli client.Client +} + +type RestoreProgressUpdater struct { + pvr *PodVolumeRestore + log logrus.FieldLogger + ctx context.Context + cli client.Client +} + +func NewBackupProgressUpdater(pvb *PodVolumeBackup, log logrus.FieldLogger, ctx context.Context, cli client.Client) *BackupProgressUpdater { + return &BackupProgressUpdater{pvb, log, ctx, cli} +} + +//UpdateProgress which implement ProgressUpdater to update pvb progress status +func (b *BackupProgressUpdater) UpdateProgress(p *UploaderProgress) { + original := b.pvb.DeepCopy() + b.pvb.Status.Progress = PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} + if b.cli == nil { + b.log.Errorf("failed to update backup pod %s volume %s progress with uninitailize client", b.pvb.Spec.Pod.Name, b.pvb.Spec.Volume) + return + } + if err := b.cli.Patch(b.ctx, b.pvb, client.MergeFrom(original)); err != nil { + b.log.Errorf("update backup pod %s volume %s progress with %v", b.pvb.Spec.Pod.Name, b.pvb.Spec.Volume, err) + } +} + +func NewRestoreProgressUpdater(pvr *PodVolumeRestore, log logrus.FieldLogger, ctx context.Context, cli client.Client) *RestoreProgressUpdater { + return &RestoreProgressUpdater{pvr, log, ctx, cli} +} + +//UpdateProgress which implement ProgressUpdater to update update pvb progress status +func (r *RestoreProgressUpdater) UpdateProgress(p *UploaderProgress) { + original := r.pvr.DeepCopy() + r.pvr.Status.Progress = PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} + if r.cli == nil { + r.log.Errorf("failed to update restore pod %s volume %s progress with uninitailize client", r.pvr.Spec.Pod.Name, r.pvr.Spec.Volume) + return + } + if err := r.cli.Patch(r.ctx, r.pvr, client.MergeFrom(original)); err != nil { + r.log.Errorf("update restore pod %s volume %s progress with %v", r.pvr.Spec.Pod.Name, r.pvr.Spec.Volume, err) + } +} diff --git a/pkg/apis/velero/v1/progress.go b/pkg/apis/velero/v1/progress.go new file mode 100644 index 0000000000..cf5d42b1d7 --- /dev/null +++ b/pkg/apis/velero/v1/progress.go @@ -0,0 +1,26 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +type UploaderProgress struct { + TotalBytes int64 `json:"totalBytes,omitempty"` + BytesDone int64 `json:"doneBytes,omitempty"` +} + +type ProgressUpdater interface { + UpdateProgress(p *UploaderProgress) +} diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index abec6d601f..3b21189265 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -264,18 +264,6 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l return mostRecentPVB.Status.SnapshotID } -// updateBackupProgressFunc returns a func that takes progress info and patches -// the PVB with the new progress. -func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { - return func(progress velerov1api.PodVolumeOperationProgress) { - original := pvb.DeepCopy() - pvb.Status.Progress = progress - if err := r.Client.Patch(context.Background(), pvb, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("error update progress") - } - } -} - func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 2b81d363ad..70cf1a53d3 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -317,15 +317,3 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return nil } - -// updateRestoreProgressFunc returns a func that takes progress info and patches -// the PVR with the new progress -func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { - return func(progress velerov1api.PodVolumeOperationProgress) { - original := req.DeepCopy() - req.Status.Progress = progress - if err := c.Patch(context.Background(), req, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("Unable to update PodVolumeRestore progress") - } - } -} diff --git a/pkg/repository/mocks/repository_writer.go b/pkg/repository/mocks/repository_writer.go new file mode 100644 index 0000000000..c3e9964bef --- /dev/null +++ b/pkg/repository/mocks/repository_writer.go @@ -0,0 +1,349 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + context "context" + + index "github.com/kopia/kopia/repo/content/index" + manifest "github.com/kopia/kopia/repo/manifest" + + mock "github.com/stretchr/testify/mock" + + object "github.com/kopia/kopia/repo/object" + + repo "github.com/kopia/kopia/repo" + + time "time" +) + +// RepositoryWriter is an autogenerated mock type for the RepositoryWriter type +type RepositoryWriter struct { + mock.Mock +} + +// ClientOptions provides a mock function with given fields: +func (_m *RepositoryWriter) ClientOptions() repo.ClientOptions { + ret := _m.Called() + + var r0 repo.ClientOptions + if rf, ok := ret.Get(0).(func() repo.ClientOptions); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(repo.ClientOptions) + } + + return r0 +} + +// Close provides a mock function with given fields: ctx +func (_m *RepositoryWriter) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ContentInfo provides a mock function with given fields: ctx, contentID +func (_m *RepositoryWriter) ContentInfo(ctx context.Context, contentID index.ID) (index.Info, error) { + ret := _m.Called(ctx, contentID) + + var r0 index.Info + if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { + r0 = rf(ctx, contentID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(index.Info) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { + r1 = rf(ctx, contentID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DeleteManifest provides a mock function with given fields: ctx, id +func (_m *RepositoryWriter) DeleteManifest(ctx context.Context, id manifest.ID) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// FindManifests provides a mock function with given fields: ctx, labels +func (_m *RepositoryWriter) FindManifests(ctx context.Context, labels map[string]string) ([]*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, labels) + + var r0 []*manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { + r0 = rf(ctx, labels) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { + r1 = rf(ctx, labels) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Flush provides a mock function with given fields: ctx +func (_m *RepositoryWriter) Flush(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetManifest provides a mock function with given fields: ctx, id, data +func (_m *RepositoryWriter) GetManifest(ctx context.Context, id manifest.ID, data interface{}) (*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, id, data) + + var r0 *manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { + r0 = rf(ctx, id, data) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { + r1 = rf(ctx, id, data) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewObjectWriter provides a mock function with given fields: ctx, opt +func (_m *RepositoryWriter) NewObjectWriter(ctx context.Context, opt object.WriterOptions) object.Writer { + ret := _m.Called(ctx, opt) + + var r0 object.Writer + if rf, ok := ret.Get(0).(func(context.Context, object.WriterOptions) object.Writer); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(object.Writer) + } + } + + return r0 +} + +// NewWriter provides a mock function with given fields: ctx, opt +func (_m *RepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error) { + ret := _m.Called(ctx, opt) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + var r1 repo.RepositoryWriter + if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { + r1 = rf(ctx, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(repo.RepositoryWriter) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { + r2 = rf(ctx, opt) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// OpenObject provides a mock function with given fields: ctx, id +func (_m *RepositoryWriter) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { + ret := _m.Called(ctx, id) + + var r0 object.Reader + if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(object.Reader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PrefetchContents provides a mock function with given fields: ctx, contentIDs, hint +func (_m *RepositoryWriter) PrefetchContents(ctx context.Context, contentIDs []index.ID, hint string) []index.ID { + ret := _m.Called(ctx, contentIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []index.ID, string) []index.ID); ok { + r0 = rf(ctx, contentIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + return r0 +} + +// PrefetchObjects provides a mock function with given fields: ctx, objectIDs, hint +func (_m *RepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs []object.ID, hint string) ([]index.ID, error) { + ret := _m.Called(ctx, objectIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { + r0 = rf(ctx, objectIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { + r1 = rf(ctx, objectIDs, hint) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PutManifest provides a mock function with given fields: ctx, labels, payload +func (_m *RepositoryWriter) PutManifest(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + ret := _m.Called(ctx, labels, payload) + + var r0 manifest.ID + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { + r0 = rf(ctx, labels, payload) + } else { + r0 = ret.Get(0).(manifest.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { + r1 = rf(ctx, labels, payload) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Refresh provides a mock function with given fields: ctx +func (_m *RepositoryWriter) Refresh(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Time provides a mock function with given fields: +func (_m *RepositoryWriter) Time() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// UpdateDescription provides a mock function with given fields: d +func (_m *RepositoryWriter) UpdateDescription(d string) { + _m.Called(d) +} + +// VerifyObject provides a mock function with given fields: ctx, id +func (_m *RepositoryWriter) VerifyObject(ctx context.Context, id object.ID) ([]index.ID, error) { + ret := _m.Called(ctx, id) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/uploader/kopia/progress.go b/pkg/uploader/kopia/progress.go index 050d190dc3..7da518eb56 100644 --- a/pkg/uploader/kopia/progress.go +++ b/pkg/uploader/kopia/progress.go @@ -20,7 +20,7 @@ import ( "sync/atomic" "time" - "github.com/vmware-tanzu/velero/pkg/uploader" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) //Throttle throttles controlle the interval of output result @@ -60,9 +60,9 @@ type KopiaProgress struct { estimatedFileCount int32 // +checklocksignore the total count of files to be processed estimatedTotalBytes int64 // +checklocksignore the total size of files to be processed // +checkatomic - processedBytes int64 // which statistic all bytes has been processed currently - outputThrottle Throttle // which control the frequency of update progress - UpFunc func(uploader.UploaderProgress) //which called by UpdateProgress func, it is used to update pvb or pvr status + processedBytes int64 // which statistic all bytes has been processed currently + outputThrottle Throttle // which control the frequency of update progress + Updater velerov1api.ProgressUpdater //which the kopia progress will call the UpdateProgress, the third party will implement the interface to update progress } //UploadedBytes the total bytes has uploaded currently @@ -93,10 +93,7 @@ func (p *KopiaProgress) EstimatedDataSize(fileCount int, totalBytes int64) { //UpdateProgress which called by UpdateProgress func, it is used to update pvb or pvr status func (p *KopiaProgress) UpdateProgress() { if p.outputThrottle.ShouldOutput() { - p.UpFunc(uploader.UploaderProgress{ - TotalBytes: atomic.LoadInt64(&p.estimatedTotalBytes), - BytesDone: atomic.LoadInt64(&p.processedBytes), - }) + p.Updater.UpdateProgress(&velerov1api.UploaderProgress{TotalBytes: p.estimatedTotalBytes, BytesDone: p.processedBytes}) } } diff --git a/pkg/uploader/kopia/snapshot.go b/pkg/uploader/kopia/snapshot.go new file mode 100644 index 0000000000..5b6e338b41 --- /dev/null +++ b/pkg/uploader/kopia/snapshot.go @@ -0,0 +1,286 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopia + +import ( + "context" + "fmt" + "math" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/service" + "github.com/vmware-tanzu/velero/pkg/uploader" + + "github.com/kopia/kopia/fs" + "github.com/kopia/kopia/fs/localfs" + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/manifest" + "github.com/kopia/kopia/snapshot" + "github.com/kopia/kopia/snapshot/policy" + "github.com/kopia/kopia/snapshot/restore" + "github.com/kopia/kopia/snapshot/snapshotfs" + "github.com/pkg/errors" +) + +//All function mainly used to make testing more convenient +var treeForSourceFunc = policy.TreeForSource +var applyRetentionPolicyFunc = policy.ApplyRetentionPolicy +var setPolicyFunc = policy.SetPolicy +var saveSnapshotFunc = snapshot.SaveSnapshot +var loadSnapshotFunc = snapshot.LoadSnapshot + +//SnapshotUploader which mainly used for UT test that could overwrite Upload interface +type SnapshotUploader interface { + Upload( + ctx context.Context, + source fs.Entry, + policyTree *policy.Tree, + sourceInfo snapshot.SourceInfo, + previousManifests ...*snapshot.Manifest, + ) (*snapshot.Manifest, error) +} + +func newOptionalInt(b policy.OptionalInt) *policy.OptionalInt { + return &b +} + +//setupDefaultPolicy set default policy for kopia +func setupDefaultPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceInfo snapshot.SourceInfo) error { + return setPolicyFunc(ctx, rep, sourceInfo, &policy.Policy{ + RetentionPolicy: policy.RetentionPolicy{ + KeepLatest: newOptionalInt(math.MaxInt32), + }, + CompressionPolicy: policy.CompressionPolicy{ + CompressorName: "none", + }, + UploadPolicy: policy.UploadPolicy{ + MaxParallelFileReads: newOptionalInt(policy.OptionalInt(runtime.NumCPU())), + }, + SchedulingPolicy: policy.SchedulingPolicy{ + Manual: true, + }, + }) +} + +//Backup backup specific sourcePath and update progress +func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath string, + parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { + if fsUploader == nil { + return nil, fmt.Errorf("get empty kopia uploader") + } + dir, err := filepath.Abs(sourcePath) + if err != nil { + return nil, errors.Wrapf(err, "Invalid source path '%s'", sourcePath) + } + + sourceInfo := snapshot.SourceInfo{ + Path: filepath.Clean(dir), + } + sourceInfo.UserName, sourceInfo.Host = service.GetRepoUser() + rootDir, err := getLocalFSEntry(sourceInfo.Path) + if err != nil { + return nil, errors.Wrap(err, "Unable to get local filesystem entry") + } + snapID, snapshotSize, err := SnapshotSource(ctx, repoWriter, fsUploader, sourceInfo, rootDir, parentSnapshot, log, "Kopia Uploader") + if err != nil { + return nil, err + } + + snapshotInfo := &uploader.SnapshotInfo{ + ID: snapID, + Size: snapshotSize, + } + + return snapshotInfo, nil +} + +func getLocalFSEntry(path0 string) (fs.Entry, error) { + path, err := resolveSymlink(path0) + if err != nil { + return nil, errors.Wrap(err, "resolveSymlink") + } + + e, err := localfs.NewEntry(path) + if err != nil { + return nil, errors.Wrap(err, "can't get local fs entry") + } + + return e, nil +} + +//resolveSymlink returns the path name after the evaluation of any symbolic links +func resolveSymlink(path string) (string, error) { + st, err := os.Lstat(path) + if err != nil { + return "", errors.Wrap(err, "stat") + } + + if (st.Mode() & os.ModeSymlink) == 0 { + return path, nil + } + + return filepath.EvalSymlinks(path) +} + +//SnapshotSource which setup policy for snapshot, upload snapshot, update progress +func SnapshotSource( + ctx context.Context, + rep repo.RepositoryWriter, + u SnapshotUploader, + sourceInfo snapshot.SourceInfo, + rootDir fs.Entry, + parentSnapshot string, + log logrus.FieldLogger, + description string, +) (string, int64, error) { + log.Info("Start to snapshot...") + snapshotStartTime := time.Now() + + var previous []*snapshot.Manifest + if parentSnapshot != "" { + mani, err := loadSnapshotFunc(ctx, rep, manifest.ID(parentSnapshot)) + if err != nil { + return "", 0, errors.Wrapf(err, "Failed to load previous snapshot %v from kopia", parentSnapshot) + } + + previous = append(previous, mani) + } else { + pre, err := findPreviousSnapshotManifest(ctx, rep, sourceInfo, nil) + if err != nil { + return "", 0, errors.Wrapf(err, "Failed to find previous kopia snapshot manifests for si %v", sourceInfo) + } + + previous = pre + } + var manifest *snapshot.Manifest + if err := setupDefaultPolicy(ctx, rep, sourceInfo); err != nil { + return "", 0, errors.Wrapf(err, "unable to set policy for si %v", sourceInfo) + } + + policyTree, err := treeForSourceFunc(ctx, rep, sourceInfo) + if err != nil { + return "", 0, errors.Wrapf(err, "unable to create policy getter for si %v", sourceInfo) + } + + manifest, err = u.Upload(ctx, rootDir, policyTree, sourceInfo, previous...) + if err != nil { + return "", 0, errors.Wrapf(err, "Failed to upload the kopia snapshot for si %v", sourceInfo) + } + + manifest.Description = description + + if _, err = saveSnapshotFunc(ctx, rep, manifest); err != nil { + return "", 0, errors.Wrapf(err, "Failed to save kopia manifest %v", manifest.ID) + } + _, err = applyRetentionPolicyFunc(ctx, rep, sourceInfo, true) + if err != nil { + return "", 0, errors.Wrapf(err, "Failed to apply kopia retention policy for si %v", sourceInfo) + } + if err = rep.Flush(ctx); err != nil { + return "", 0, errors.Wrapf(err, "Failed to flush kopia repository") + } + log.Infof("Created snapshot with root %v and ID %v in %v", manifest.RootObjectID(), manifest.ID, time.Since(snapshotStartTime).Truncate(time.Second)) + return reportSnapshotStatus(manifest) +} + +func reportSnapshotStatus(manifest *snapshot.Manifest) (string, int64, error) { + manifestID := manifest.ID + snapSize := manifest.Stats.TotalFileSize + + var errs []string + if ds := manifest.RootEntry.DirSummary; ds != nil { + for _, ent := range ds.FailedEntries { + errs = append(errs, ent.Error) + } + } + if len(errs) != 0 { + return "", 0, errors.New(strings.Join(errs, "\n")) + } + + return string(manifestID), snapSize, nil +} + +// findPreviousSnapshotManifest returns the list of previous snapshots for a given source, including +// last complete snapshot following it. +func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, noLaterThan *time.Time) ([]*snapshot.Manifest, error) { + man, err := snapshot.ListSnapshots(ctx, rep, sourceInfo) + if err != nil { + return nil, err + } + + var previousComplete *snapshot.Manifest + var result []*snapshot.Manifest + + for _, p := range man { + if noLaterThan != nil && p.StartTime.After(*noLaterThan) { + continue + } + + if p.IncompleteReason == "" && (previousComplete == nil || p.StartTime.After(previousComplete.StartTime)) { + previousComplete = p + } + } + + if previousComplete != nil { + result = append(result, previousComplete) + } + + return result, nil +} + +//Restore restore specific sourcePath with given snapshotID and update progress +func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *KopiaProgress, snapshotID, dest string, log logrus.FieldLogger, cancleCh chan struct{}) (int64, int32, error) { + log.Info("Start to restore...") + + rootEntry, err := snapshotfs.FilesystemEntryFromIDWithPath(ctx, rep, snapshotID, false) + if err != nil { + return 0, 0, errors.Wrapf(err, "Unable to get filesystem entry for snapshot %v", snapshotID) + } + + path, err := filepath.Abs(dest) + if err != nil { + return 0, 0, errors.Wrapf(err, "Unable to resolve path %v", dest) + } + + output := &restore.FilesystemOutput{ + TargetPath: path, + OverwriteDirectories: true, + OverwriteFiles: true, + OverwriteSymlinks: true, + IgnorePermissionErrors: true, + } + + stat, err := restore.Entry(ctx, rep, output, rootEntry, restore.Options{ + Parallel: runtime.NumCPU(), + RestoreDirEntryAtDepth: math.MaxInt32, + Cancel: cancleCh, + ProgressCallback: func(ctx context.Context, stats restore.Stats) { + progress.ProgressBytes(stats.RestoredTotalFileSize, stats.EnqueuedTotalFileSize) + }, + }) + + if err != nil { + return 0, 0, errors.Wrapf(err, "Failed to copy snapshot data to the target") + } + return stat.RestoredTotalFileSize, stat.RestoredFileCount, nil +} diff --git a/pkg/uploader/kopia/snapshot_test.go b/pkg/uploader/kopia/snapshot_test.go new file mode 100644 index 0000000000..3a60658c91 --- /dev/null +++ b/pkg/uploader/kopia/snapshot_test.go @@ -0,0 +1,199 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopia + +import ( + "context" + "testing" + + "github.com/kopia/kopia/snapshot" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + repomocks "github.com/vmware-tanzu/velero/pkg/repository/mocks" + "github.com/vmware-tanzu/velero/pkg/uploader" + uploadermocks "github.com/vmware-tanzu/velero/pkg/uploader/mocks" +) + +type snapshotMockes struct { + policyMock *uploadermocks.Policy + snapshotMock *uploadermocks.Snapshot + uploderMock *uploadermocks.Uploader + repoWriterMock *repomocks.RepositoryWriter +} + +type mockArgs struct { + methodName string + returns []interface{} +} + +func InjectSnapshotFuncs() *snapshotMockes { + s := &snapshotMockes{ + policyMock: &uploadermocks.Policy{}, + snapshotMock: &uploadermocks.Snapshot{}, + uploderMock: &uploadermocks.Uploader{}, + repoWriterMock: &repomocks.RepositoryWriter{}, + } + + setPolicyFunc = s.policyMock.SetPolicy + treeForSourceFunc = s.policyMock.TreeForSource + applyRetentionPolicyFunc = s.policyMock.ApplyRetentionPolicy + loadSnapshotFunc = s.snapshotMock.LoadSnapshot + saveSnapshotFunc = s.snapshotMock.SaveSnapshot + return s +} + +func MockFuncs(s *snapshotMockes, args []mockArgs) { + s.snapshotMock.On("LoadSnapshot", mock.Anything, mock.Anything, mock.Anything).Return(args[0].returns...) + s.snapshotMock.On("SaveSnapshot", mock.Anything, mock.Anything, mock.Anything).Return(args[1].returns...) + s.policyMock.On("TreeForSource", mock.Anything, mock.Anything, mock.Anything).Return(args[2].returns...) + s.policyMock.On("ApplyRetentionPolicy", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(args[3].returns...) + s.policyMock.On("SetPolicy", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(args[4].returns...) + s.uploderMock.On("Upload", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(args[5].returns...) + s.repoWriterMock.On("Flush", mock.Anything).Return(args[6].returns...) +} + +func TestSnapshotSource(t *testing.T) { + + ctx := context.TODO() + sourceInfo := snapshot.SourceInfo{ + UserName: "testUserName", + Host: "testHost", + Path: "/var", + } + rootDir, err := getLocalFSEntry(sourceInfo.Path) + assert.NoError(t, err) + log := logrus.New() + manifest := &snapshot.Manifest{ + ID: "test", + RootEntry: &snapshot.DirEntry{}, + } + + testCases := []struct { + name string + args []mockArgs + notError bool + }{ + { + name: "regular test", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: true, + }, + { + name: "failed to load snapshot", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, errors.New("failed to load snapshot")}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: false, + }, + { + name: "failed to save snapshot", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, errors.New("failed to save snapshot")}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: false, + }, + { + name: "failed to apply policy", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, errors.New("failed to save snapshot")}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: false, + }, + { + name: "failed to set policy", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{errors.New("failed to set policy")}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: false, + }, + { + name: "failed to upload snapshot", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, errors.New("failed to upload snapshot")}}, + {methodName: "Flush", returns: []interface{}{nil}}, + }, + notError: false, + }, + { + name: "failed to flush repo", + args: []mockArgs{ + {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, + {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, errors.New("failed to save snapshot")}}, + {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, + {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, + {methodName: "SetPolicy", returns: []interface{}{nil}}, + {methodName: "Upload", returns: []interface{}{manifest, nil}}, + {methodName: "Flush", returns: []interface{}{errors.New("failed to flush repo")}}, + }, + notError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + s := InjectSnapshotFuncs() + MockFuncs(s, tc.args) + _, _, err = SnapshotSource(ctx, s.repoWriterMock, s.uploderMock, sourceInfo, rootDir, "/", log, "TestSnapshotSource", func(up uploader.UploaderProgress) {}) + if tc.notError { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + +} diff --git a/pkg/uploader/mocks/policy.go b/pkg/uploader/mocks/policy.go new file mode 100644 index 0000000000..3c1dcdd787 --- /dev/null +++ b/pkg/uploader/mocks/policy.go @@ -0,0 +1,92 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + "context" + + "github.com/kopia/kopia/snapshot/policy" + "github.com/stretchr/testify/mock" + + "github.com/kopia/kopia/repo" + + "github.com/kopia/kopia/snapshot" +) + +// policy is an autogenerated mock type for the TreeForSource type +type Policy struct { + mock.Mock +} + +// Execute provides a mock function with given fields: ctx, rep, si +func (_m *Policy) TreeForSource(ctx context.Context, rep repo.Repository, si snapshot.SourceInfo) (*policy.Tree, error) { + ret := _m.Called(ctx, rep, si) + + var r0 *policy.Tree + if rf, ok := ret.Get(0).(func(context.Context, repo.Repository, snapshot.SourceInfo) *policy.Tree); ok { + r0 = rf(ctx, rep, si) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*policy.Tree) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, repo.Repository, snapshot.SourceInfo) error); ok { + r1 = rf(ctx, rep, si) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ApplyRetentionPolicy provides a mock function with given fields: ctx, rep, sourceInfo, reallyDelete +func (_m *Policy) ApplyRetentionPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceInfo snapshot.SourceInfo, reallyDelete bool) ([]*snapshot.Manifest, error) { + ret := _m.Called(ctx, rep, sourceInfo, reallyDelete) + + var r0 []*snapshot.Manifest + if rf, ok := ret.Get(0).(func(context.Context, repo.RepositoryWriter, snapshot.SourceInfo, bool) []*snapshot.Manifest); ok { + r0 = rf(ctx, rep, sourceInfo, reallyDelete) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*snapshot.Manifest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, repo.RepositoryWriter, snapshot.SourceInfo, bool) error); ok { + r1 = rf(ctx, rep, sourceInfo, reallyDelete) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +func (_m *Policy) SetPolicy(ctx context.Context, rep repo.RepositoryWriter, si snapshot.SourceInfo, pol *policy.Policy) error { + ret := _m.Called(ctx, rep, si, pol) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, repo.RepositoryWriter, snapshot.SourceInfo, *policy.Policy) error); ok { + r0 = rf(ctx, rep, si, pol) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/uploader/mocks/shim.go b/pkg/uploader/mocks/shim.go new file mode 100644 index 0000000000..1ec3acc580 --- /dev/null +++ b/pkg/uploader/mocks/shim.go @@ -0,0 +1,42 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// shimRepository is an autogenerated mock type for the shimRepository type +type ShimRepository struct { + mock.Mock +} + +// Flush provides a mock function with given fields: ctx +func (_m *ShimRepository) Flush(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/uploader/mocks/snapshot.go b/pkg/uploader/mocks/snapshot.go new file mode 100644 index 0000000000..c651242eb4 --- /dev/null +++ b/pkg/uploader/mocks/snapshot.go @@ -0,0 +1,76 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + "context" + + "github.com/kopia/kopia/repo/manifest" + "github.com/kopia/kopia/snapshot" + "github.com/stretchr/testify/mock" + + "github.com/kopia/kopia/repo" +) + +// snapshot is an autogenerated mock type for the snapshot type +type Snapshot struct { + mock.Mock +} + +// LoadSnapshot provides a mock function with given fields: ctx, rep, manifestID +func (_m *Snapshot) LoadSnapshot(ctx context.Context, rep repo.Repository, manifestID manifest.ID) (*snapshot.Manifest, error) { + ret := _m.Called(ctx, rep, manifestID) + + var r0 *snapshot.Manifest + if rf, ok := ret.Get(0).(func(context.Context, repo.Repository, manifest.ID) *snapshot.Manifest); ok { + r0 = rf(ctx, rep, manifestID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*snapshot.Manifest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, repo.Repository, manifest.ID) error); ok { + r1 = rf(ctx, rep, manifestID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SaveSnapshot provides a mock function with given fields: ctx, rep, man +func (_m *Snapshot) SaveSnapshot(ctx context.Context, rep repo.RepositoryWriter, man *snapshot.Manifest) (manifest.ID, error) { + ret := _m.Called(ctx, rep, man) + + var r0 manifest.ID + if rf, ok := ret.Get(0).(func(context.Context, repo.RepositoryWriter, *snapshot.Manifest) manifest.ID); ok { + r0 = rf(ctx, rep, man) + } else { + r0 = ret.Get(0).(manifest.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, repo.RepositoryWriter, *snapshot.Manifest) error); ok { + r1 = rf(ctx, rep, man) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/uploader/mocks/uploader.go b/pkg/uploader/mocks/uploader.go new file mode 100644 index 0000000000..d8b5fa2fda --- /dev/null +++ b/pkg/uploader/mocks/uploader.go @@ -0,0 +1,63 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mocks + +import ( + "context" + + "github.com/kopia/kopia/fs" + "github.com/stretchr/testify/mock" + + "github.com/kopia/kopia/snapshot/policy" + + "github.com/kopia/kopia/snapshot" +) + +// Upload is an autogenerated mock type for the Upload type +type Uploader struct { + mock.Mock +} + +// Execute provides a mock function with given fields: ctx, source, policyTree, sourceInfo, previousManifests +func (_m *Uploader) Upload(ctx context.Context, source fs.Entry, policyTree *policy.Tree, sourceInfo snapshot.SourceInfo, previousManifests ...*snapshot.Manifest) (*snapshot.Manifest, error) { + _va := make([]interface{}, len(previousManifests)) + for _i := range previousManifests { + _va[_i] = previousManifests[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, source, policyTree, sourceInfo) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *snapshot.Manifest + if rf, ok := ret.Get(0).(func(context.Context, fs.Entry, *policy.Tree, snapshot.SourceInfo, ...*snapshot.Manifest) *snapshot.Manifest); ok { + r0 = rf(ctx, source, policyTree, sourceInfo, previousManifests...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*snapshot.Manifest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, fs.Entry, *policy.Tree, snapshot.SourceInfo, ...*snapshot.Manifest) error); ok { + r1 = rf(ctx, source, policyTree, sourceInfo, previousManifests...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/uploader/provider/kopia.go b/pkg/uploader/provider/kopia.go new file mode 100644 index 0000000000..6bdd1a4462 --- /dev/null +++ b/pkg/uploader/provider/kopia.go @@ -0,0 +1,204 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/kopia/kopia/snapshot/snapshotfs" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/pkg/uploader/kopia" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokeys "github.com/vmware-tanzu/velero/pkg/repository/keys" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/service" +) + +//BackupFunc mainly used to make testing more convenient +var BackupFunc = kopia.Backup +var RestoreFunc = kopia.Restore + +//kopiaProvider recorded info related with kopiaProvider +//action which means provider handle backup or restore +type kopiaProvider struct { + bkRepo udmrepo.BackupRepo + credGetter *credentials.CredentialGetter + uploader *snapshotfs.Uploader + restoreCancel chan struct{} + log logrus.FieldLogger +} + +//NewKopiaUploaderProvider initialized with open or create a repository +func NewKopiaUploaderProvider( + ctx context.Context, + credGetter *credentials.CredentialGetter, + bsl *velerov1api.BackupStorageLocation, + log logrus.FieldLogger, +) (Provider, error) { + kp := &kopiaProvider{ + log: log, + credGetter: credGetter, + } + //repoUID which is used to generate kopia repository config with unique directory path + repoUID := string(bsl.GetUID()) + repoOpt, err := udmrepo.NewRepoOptions( + udmrepo.WithPassword(kp, ""), + udmrepo.WithConfigFile("", repoUID), + udmrepo.WithDescription("Initial kopia uploader provider"), + ) + if err != nil { + return nil, errors.Wrapf(err, "error to get repo options") + } + + repoSvc := service.Create(log) + log.WithField("repoUID", repoUID).Info("Opening backup repo") + + kp.bkRepo, err = repoSvc.Open(ctx, *repoOpt) + if err != nil { + return nil, errors.Wrapf(err, "Failed to find kopia repository") + } + return kp, nil +} + +//CheckContext check context status periodically +//check if context is timeout or cancel +func (kp *kopiaProvider) CheckContext(ctx context.Context) { + for { + select { + case <-ctx.Done(): + if kp.uploader != nil { + kp.uploader.Cancel() + kp.log.Infof("Backup is been canceled") + } + if kp.restoreCancel != nil { + close(kp.restoreCancel) + kp.log.Infof("Restore is been canceled") + } + return + default: + time.Sleep(time.Second * 10) + } + } +} + +func (kp *kopiaProvider) Close(ctx context.Context) { + kp.bkRepo.Close(ctx) +} + +//RunBackup which will backup specific path and update backup progress in pvb status +func (kp *kopiaProvider) RunBackup( + ctx context.Context, + path string, + tags map[string]string, + parentSnapshot string, + updater velerov1api.ProgressUpdater) (string, error) { + if updater == nil { + return "", errors.New("Need to inital backup progress updater first") + } + + log := kp.log.WithFields(logrus.Fields{ + "path": path, + "parentSnapshot": parentSnapshot, + }) + repoWriter := kopia.NewShimRepo(kp.bkRepo) + kp.uploader = snapshotfs.NewUploader(repoWriter) + prorgess := new(kopia.KopiaProgress) + prorgess.InitThrottle(backupProgressCheckInterval) + prorgess.Updater = updater + kp.uploader.Progress = prorgess + + log.Info("Starting backup") + go kp.CheckContext(ctx) + + snapshotInfo, err := BackupFunc(ctx, kp.uploader, repoWriter, path, parentSnapshot, log) + + if err != nil { + return "", errors.Wrapf(err, "Failed to run kopia backup") + } else if snapshotInfo == nil { + return "", fmt.Errorf("failed to get kopia backup snapshot info for path %v", path) + } + + updater.UpdateProgress( + &velerov1api.UploaderProgress{ + TotalBytes: snapshotInfo.Size, + BytesDone: snapshotInfo.Size, + }, + ) + + log.Debugf("Kopia backup finished, snapshot ID %s, backup size %d", snapshotInfo.ID, snapshotInfo.Size) + return snapshotInfo.ID, nil +} + +func (kp *kopiaProvider) GetPassword(param interface{}) (string, error) { + if kp.credGetter.FromSecret == nil { + return "", errors.New("invalid credentials interface") + } + rawPass, err := kp.credGetter.FromSecret.Get(repokeys.RepoKeySelector()) + if err != nil { + return "", errors.Wrap(err, "error to get password") + } + + return strings.TrimSpace(rawPass), nil +} + +//RunRestore which will restore specific path and update restore progress in pvr status +func (kp *kopiaProvider) RunRestore( + ctx context.Context, + snapshotID string, + volumePath string, + updater velerov1api.ProgressUpdater) error { + log := kp.log.WithFields(logrus.Fields{ + "snapshotID": snapshotID, + "volumePath": volumePath, + }) + repoWriter := kopia.NewShimRepo(kp.bkRepo) + prorgess := new(kopia.KopiaProgress) + prorgess.InitThrottle(restoreProgressCheckInterval) + prorgess.Updater = updater + kp.restoreCancel = make(chan struct{}) + defer func() { + if kp.restoreCancel != nil { + close(kp.restoreCancel) + } + }() + + log.Info("Starting restore") + go kp.CheckContext(ctx) + size, fileCount, err := RestoreFunc(ctx, repoWriter, prorgess, snapshotID, volumePath, log, kp.restoreCancel) + + if err != nil { + return errors.Wrapf(err, "Failed to run kopia restore") + } + + updater.UpdateProgress(&velerov1api.UploaderProgress{ + TotalBytes: size, + BytesDone: size, + }) + + output := fmt.Sprintf("Kopia restore finished, restore size %d, file count %d", size, fileCount) + + log.Info(output) + + return nil +} diff --git a/pkg/uploader/provider/kopia_test.go b/pkg/uploader/provider/kopia_test.go new file mode 100644 index 0000000000..3656053d07 --- /dev/null +++ b/pkg/uploader/provider/kopia_test.go @@ -0,0 +1,118 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "testing" + + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/snapshot/snapshotfs" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/uploader/kopia" +) + +func TestRunBackup(t *testing.T) { + var kp kopiaProvider + kp.log = logrus.New() + fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme) + updater := velerov1api.NewBackupProgressUpdater(&velerov1api.PodVolumeBackup{}, kp.log, context.Background(), fakeClient) + testCases := []struct { + name string + hookBackupFunc func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) + notError bool + }{ + { + name: "success to backup", + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { + return &uploader.SnapshotInfo{}, nil + }, + notError: true, + }, + { + name: "get error to backup", + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { + return &uploader.SnapshotInfo{}, errors.New("failed to backup") + }, + notError: false, + }, + { + name: "got empty snapshot", + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { + return nil, nil + }, + notError: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + BackupFunc = tc.hookBackupFunc + _, err := kp.RunBackup(context.Background(), "var", nil, "", updater) + if tc.notError { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +func TestRunRestore(t *testing.T) { + var kp kopiaProvider + kp.log = logrus.New() + updater := velerov1api.NewRestoreProgressUpdater(&velerov1api.PodVolumeRestore{}, kp.log, context.Background(), fake.NewFakeClientWithScheme(scheme.Scheme)) + + testCases := []struct { + name string + hookRestoreFunc func(ctx context.Context, rep repo.RepositoryWriter, progress *kopia.KopiaProgress, snapshotID, dest string, log logrus.FieldLogger, cancleCh chan struct{}) (int64, int32, error) + notError bool + }{ + { + name: "normal restore", + hookRestoreFunc: func(ctx context.Context, rep repo.RepositoryWriter, progress *kopia.KopiaProgress, snapshotID, dest string, log logrus.FieldLogger, cancleCh chan struct{}) (int64, int32, error) { + return 0, 0, nil + }, + notError: true, + }, + { + name: "failed to restore", + hookRestoreFunc: func(ctx context.Context, rep repo.RepositoryWriter, progress *kopia.KopiaProgress, snapshotID, dest string, log logrus.FieldLogger, cancleCh chan struct{}) (int64, int32, error) { + return 0, 0, errors.New("failed to restore") + }, + notError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + RestoreFunc = tc.hookRestoreFunc + err := kp.RunRestore(context.Background(), "", "/var", updater) + if tc.notError { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index 5a90a806f1..c6fe0a177e 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -18,27 +18,53 @@ package provider import ( "context" + "time" + "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + + "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/uploader" ) +const restoreProgressCheckInterval = 10 * time.Second +const backupProgressCheckInterval = 10 * time.Second + // Provider which is designed for one pod volumn to do the backup or restore type Provider interface { // RunBackup which will do backup for one specific volumn and return snapshotID error - // updateFunc which is used for update backup progress into related pvb status + // updater which is used for update backup progress into related pvb status RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updateFunc func(velerov1api.PodVolumeOperationProgress)) (string, error) + updater velerov1api.ProgressUpdater) (string, error) // RunRestore which will do restore for one specific volumn with given snapshot id and return error // updateFunc which is used for update restore progress into related pvr status RunRestore( ctx context.Context, snapshotID string, volumePath string, - updateFunc func(velerov1api.PodVolumeOperationProgress)) error + updater velerov1api.ProgressUpdater) error // Close which will close related repository Close(ctx context.Context) } + +//NewUploaderProvider initialize provider with specific uploader_type +func NewUploaderProvider( + ctx context.Context, + uploader_type string, + repoIdentifier string, + bsl *velerov1api.BackupStorageLocation, + credGetter *credentials.CredentialGetter, + repoKeySelector *v1.SecretKeySelector, + log logrus.FieldLogger, +) (Provider, error) { + if uploader_type == uploader.KopiaType { + return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) + } else { + return NewKopiaUploaderProvider(ctx, credGetter, bsl, log) + } +} diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go new file mode 100644 index 0000000000..f908077a3c --- /dev/null +++ b/pkg/uploader/provider/restic.go @@ -0,0 +1,34 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package provider + +import ( + "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func NewResticUploaderProvider( + repoIdentifier string, + bsl *velerov1api.BackupStorageLocation, + credGetter *credentials.CredentialGetter, + repoKeySelector *v1.SecretKeySelector, + log logrus.FieldLogger, +) (Provider, error) { + return nil, nil //TODO +} diff --git a/pkg/uploader/types.go b/pkg/uploader/types.go index 134e36cceb..4f5274a97c 100644 --- a/pkg/uploader/types.go +++ b/pkg/uploader/types.go @@ -22,10 +22,8 @@ import ( ) const ( - ResticType = "restic" - KopiaType = "kopia" - VeleroBackup = "backup" - VeleroRestore = "restore" + ResticType = "restic" + KopiaType = "kopia" ) // ValidateUploaderType validates if the input param is a valid uploader type. @@ -42,8 +40,3 @@ type SnapshotInfo struct { ID string `json:"id"` Size int64 `json:"Size"` } - -type UploaderProgress struct { - TotalBytes int64 `json:"totalBytes,omitempty"` - BytesDone int64 `json:"doneBytes,omitempty"` -} From 3e435eeb4480918e8db137823745475c13f5dfa2 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Fri, 19 Aug 2022 15:28:45 +0800 Subject: [PATCH 256/366] Remove reference to non-existent doc fixes #4923 Signed-off-by: Daniel Jiang --- changelogs/unreleased/5234-reasonerjt | 1 + site/content/docs/main/backup-hooks.md | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 changelogs/unreleased/5234-reasonerjt diff --git a/changelogs/unreleased/5234-reasonerjt b/changelogs/unreleased/5234-reasonerjt new file mode 100644 index 0000000000..34fd5768ad --- /dev/null +++ b/changelogs/unreleased/5234-reasonerjt @@ -0,0 +1 @@ +Remove reference to non-existent doc \ No newline at end of file diff --git a/site/content/docs/main/backup-hooks.md b/site/content/docs/main/backup-hooks.md index ef38dc7fed..87310fbb3c 100644 --- a/site/content/docs/main/backup-hooks.md +++ b/site/content/docs/main/backup-hooks.md @@ -52,9 +52,6 @@ spec. This examples walks you through using both pre and post hooks for freezing a file system. Freezing the file system is useful to ensure that all pending disk I/O operations have completed prior to taking a snapshot. -This example uses [examples/nginx-app/with-pv.yaml][2]. Follow the [steps for your provider][3] to -setup this example. - ### Annotations The Velero [example/nginx-app/with-pv.yaml][2] serves as an example of adding the pre and post hook annotations directly @@ -108,4 +105,3 @@ Note that the container must support the shell command you use. [1]: api-types/backup.md [2]: https://github.com/vmware-tanzu/velero/blob/main/examples/nginx-app/with-pv.yaml -[3]: cloud-common.md From 0378020c8d78fe327611c418b389e0a1607564ef Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Wed, 10 Aug 2022 19:28:35 +0800 Subject: [PATCH 257/366] refactor gc controller with kubebuilder Signed-off-by: allenxu404 --- changelogs/unreleased/5215-allenxu404 | 1 + pkg/cmd/server/server.go | 31 ++-- pkg/controller/gc_controller.go | 179 ++++++++++------------ pkg/controller/gc_controller_test.go | 207 ++++---------------------- 4 files changed, 115 insertions(+), 303 deletions(-) create mode 100644 changelogs/unreleased/5215-allenxu404 diff --git a/changelogs/unreleased/5215-allenxu404 b/changelogs/unreleased/5215-allenxu404 new file mode 100644 index 0000000000..48275bb251 --- /dev/null +++ b/changelogs/unreleased/5215-allenxu404 @@ -0,0 +1 @@ +Refactor GCController with kubebuilder \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f45a302207..d846bdc12e 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -672,22 +672,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } - gcControllerRunInfo := func() controllerRunInfo { - gcController := controller.NewGCController( - s.logger, - s.sharedInformerFactory.Velero().V1().Backups(), - s.sharedInformerFactory.Velero().V1().DeleteBackupRequests().Lister(), - s.veleroClient.VeleroV1(), - s.mgr.GetClient(), - s.config.garbageCollectionFrequency, - ) - - return controllerRunInfo{ - controller: gcController, - numWorkers: defaultControllerWorkers, - } - } - restoreControllerRunInfo := func() controllerRunInfo { restorer, err := restore.NewKubernetesRestorer( s.veleroClient.VeleroV1(), @@ -728,15 +712,15 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } enabledControllers := map[string]func() controllerRunInfo{ - controller.BackupSync: backupSyncControllerRunInfo, - controller.Backup: backupControllerRunInfo, - controller.GarbageCollection: gcControllerRunInfo, - controller.Restore: restoreControllerRunInfo, + controller.BackupSync: backupSyncControllerRunInfo, + controller.Backup: backupControllerRunInfo, + controller.Restore: restoreControllerRunInfo, } // Note: all runtime type controllers that can be disabled are grouped separately, below: enabledRuntimeControllers := make(map[string]struct{}) enabledRuntimeControllers[controller.ServerStatusRequest] = struct{}{} enabledRuntimeControllers[controller.DownloadRequest] = struct{}{} + enabledRuntimeControllers[controller.GarbageCollection] = struct{}{} if s.config.restoreOnly { s.logger.Info("Restore only mode - not starting the backup, schedule, delete-backup, or GC controllers") @@ -848,6 +832,13 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } + if _, ok := enabledRuntimeControllers[controller.GarbageCollection]; ok { + r := controller.NewGCReconciler(s.logger, s.mgr.GetClient()) + if err := r.SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.GarbageCollection) + } + } + // TODO(2.0): presuming all controllers and resources are converted to runtime-controller // by v2.0, the block from this line and including the `s.mgr.Start() will be // deprecated, since the manager auto-starts all the caches. Until then, we need to start the diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index b8297aa2a9..c12ba45d9c 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -23,19 +23,16 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/client-go/tools/cache" - + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/util/kube" ) const ( @@ -46,100 +43,77 @@ const ( gcFailureBSLReadOnly = "BSLReadOnly" ) -// gcController creates DeleteBackupRequests for expired backups. -type gcController struct { - *genericController - - backupLister velerov1listers.BackupLister - deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister - deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - kbClient client.Client - frequency time.Duration - - clock clock.Clock +// gcReconciler creates DeleteBackupRequests for expired backups. +type gcReconciler struct { + client.Client + logger logrus.FieldLogger + clock clock.Clock } -// NewGCController constructs a new gcController. -func NewGCController( +// NewGCReconciler constructs a new gcReconciler. +func NewGCReconciler( logger logrus.FieldLogger, - backupInformer velerov1informers.BackupInformer, - deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, - deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - kbClient client.Client, - frequency time.Duration, -) Interface { - c := &gcController{ - genericController: newGenericController(GarbageCollection, logger), - clock: clock.RealClock{}, - backupLister: backupInformer.Lister(), - deleteBackupRequestLister: deleteBackupRequestLister, - deleteBackupRequestClient: deleteBackupRequestClient, - kbClient: kbClient, + client client.Client, +) *gcReconciler { + return &gcReconciler{ + Client: client, + logger: logger, + clock: clock.RealClock{}, } - - c.syncHandler = c.processQueueItem - c.resyncPeriod = frequency - if c.resyncPeriod <= 0 { - c.resyncPeriod = defaultGCFrequency - } - logger.Infof("Garbage collection frequency: %s", c.resyncPeriod.String()) - c.resyncFunc = c.enqueueAllBackups - - backupInformer.Informer().AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: c.enqueue, - UpdateFunc: func(_, obj interface{}) { c.enqueue(obj) }, - }, - ) - - return c } -// enqueueAllBackups lists all backups from cache and enqueues all of them so we can check each one -// for expiration. -func (c *gcController) enqueueAllBackups() { - c.logger.Debug("gcController.enqueueAllBackups") - - backups, err := c.backupLister.List(labels.Everything()) - if err != nil { - c.logger.WithError(errors.WithStack(err)).Error("error listing backups") - return - } - - for _, backup := range backups { - c.enqueue(backup) - } +// GCController only watches on CreateEvent for ensuring every new backup will be taken care of. +// Other Events will be filtered to decrease the number of reconcile call. Especially UpdateEvent must be filtered since we removed +// the backup status as the sub-resource of backup in v1.9, every change on it will be treated as UpdateEvent and trigger reconcile call. +func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error { + s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, defaultGCFrequency) + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1api.Backup{}). + WithEventFilter(predicate.Funcs{ + UpdateFunc: func(ue event.UpdateEvent) bool { + return false + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + GenericFunc: func(ge event.GenericEvent) bool { + return false + }, + }). + Watches(s, nil). + Complete(c) } -func (c *gcController) processQueueItem(key string) error { - log := c.logger.WithField("backup", key) - - ns, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return errors.Wrap(err, "error splitting queue key") - } - - backup, err := c.backupLister.Backups(ns).Get(name) - if apierrors.IsNotFound(err) { - log.Debug("Unable to find backup") - return nil - } - if err != nil { - return errors.Wrap(err, "error getting backup") +// +kubebuilder:rbac:groups=velero.io,resources=backups,verbs=get;list;watch;update +// +kubebuilder:rbac:groups=velero.io,resources=backups/status,verbs=get +// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests,verbs=get;list;watch;create; +// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests/status,verbs=get +// +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations,verbs=get +func (c *gcReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := c.logger.WithField("gc backup", req.String()) + log.Debug("gcController getting backup") + + backup := &velerov1api.Backup{} + if err := c.Get(ctx, req.NamespacedName, backup); err != nil { + if apierrors.IsNotFound(err) { + log.WithError(err).Error("backup not found") + return ctrl.Result{}, nil + } + return ctrl.Result{}, errors.Wrapf(err, "error getting backup %s", req.String()) } + log.Debugf("backup: %v", backup) log = c.logger.WithFields( logrus.Fields{ - "backup": key, + "backup": req.String(), "expiration": backup.Status.Expiration, }, ) now := c.clock.Now() - if backup.Status.Expiration == nil || backup.Status.Expiration.After(now) { log.Debug("Backup has not expired yet, skipping") - return nil + return ctrl.Result{}, nil } log.Info("Backup has expired") @@ -149,8 +123,8 @@ func (c *gcController) processQueueItem(key string) error { } loc := &velerov1api.BackupStorageLocation{} - if err := c.kbClient.Get(context.Background(), client.ObjectKey{ - Namespace: ns, + if err := c.Get(ctx, client.ObjectKey{ + Namespace: req.Namespace, Name: backup.Spec.StorageLocation, }, loc); err != nil { if apierrors.IsNotFound(err) { @@ -159,53 +133,56 @@ func (c *gcController) processQueueItem(key string) error { } else { backup.Labels[garbageCollectionFailure] = gcFailureBSLCannotGet } - if err := c.kbClient.Update(context.Background(), backup); err != nil { + if err := c.Update(ctx, backup); err != nil { log.WithError(err).Error("error updating backup labels") } - return errors.Wrap(err, "error getting backup storage location") + return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } if loc.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { log.Infof("Backup cannot be garbage-collected because backup storage location %s is currently in read-only mode", loc.Name) backup.Labels[garbageCollectionFailure] = gcFailureBSLReadOnly - if err := c.kbClient.Update(context.Background(), backup); err != nil { + if err := c.Update(ctx, backup); err != nil { log.WithError(err).Error("error updating backup labels") } - return nil + return ctrl.Result{}, nil } // remove gc fail error label after this point delete(backup.Labels, garbageCollectionFailure) - if err := c.kbClient.Update(context.Background(), backup); err != nil { + if err := c.Update(ctx, backup); err != nil { log.WithError(err).Error("error updating backup labels") } - selector := labels.SelectorFromSet(labels.Set(map[string]string{ + selector := client.MatchingLabels{ velerov1api.BackupNameLabel: label.GetValidName(backup.Name), velerov1api.BackupUIDLabel: string(backup.UID), - })) + } - dbrs, err := c.deleteBackupRequestLister.DeleteBackupRequests(ns).List(selector) - if err != nil { - return errors.Wrap(err, "error listing existing DeleteBackupRequests for backup") + dbrs := &velerov1api.DeleteBackupRequestList{} + if err := c.List(ctx, dbrs, selector); err != nil { + log.WithError(err).Error("error listing DeleteBackupRequests") + return ctrl.Result{}, errors.Wrap(err, "error listing existing DeleteBackupRequests for backup") } + log.Debugf("length of dbrs:%d", len(dbrs.Items)) // if there's an existing unprocessed deletion request for this backup, don't create // another one - for _, dbr := range dbrs { + for _, dbr := range dbrs.Items { switch dbr.Status.Phase { case "", velerov1api.DeleteBackupRequestPhaseNew, velerov1api.DeleteBackupRequestPhaseInProgress: log.Info("Backup already has a pending deletion request") - return nil + return ctrl.Result{}, nil } } log.Info("Creating a new deletion request") - req := pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) - - if _, err = c.deleteBackupRequestClient.DeleteBackupRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{}); err != nil { - return errors.Wrap(err, "error creating DeleteBackupRequest") + ndbr := pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) + ndbr.SetNamespace(backup.Namespace) + if err := c.Create(ctx, ndbr); err != nil { + log.WithError(err).Error("error creating DeleteBackupRequests") + return ctrl.Result{}, errors.Wrap(err, "error creating DeleteBackupRequest") } - return nil + return ctrl.Result{}, nil } diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index c373064e48..8985f3be18 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -18,152 +18,41 @@ package controller import ( "context" - "fmt" - "sort" "testing" "time" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/apimachinery/pkg/watch" - core "k8s.io/client-go/testing" - + ctrl "sigs.k8s.io/controller-runtime" kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" velerotest "github.com/vmware-tanzu/velero/pkg/test" - "github.com/vmware-tanzu/velero/pkg/util/kube" ) -func TestGCControllerEnqueueAllBackups(t *testing.T) { - var ( - client = fake.NewSimpleClientset() - sharedInformers = informers.NewSharedInformerFactory(client, 0) - - controller = NewGCController( - velerotest.NewLogger(), - sharedInformers.Velero().V1().Backups(), - sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), - client.VeleroV1(), - nil, - defaultGCFrequency, - ).(*gcController) - ) - - keys := make(chan string) - - controller.syncHandler = func(key string) error { - keys <- key - return nil - } - - var expected []string - - for i := 0; i < 3; i++ { - backup := builder.ForBackup(velerov1api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result() - sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup) - expected = append(expected, kube.NamespaceAndName(backup)) - } - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - go controller.Run(ctx, 1) - - var received []string - -Loop: - for { - select { - case <-ctx.Done(): - t.Fatal("test timed out") - case key := <-keys: - received = append(received, key) - if len(received) == len(expected) { - break Loop - } - } - } - - sort.Strings(expected) - sort.Strings(received) - assert.Equal(t, expected, received) -} - -func TestGCControllerHasUpdateFunc(t *testing.T) { - backup := defaultBackup().Result() - expected := kube.NamespaceAndName(backup) - - client := fake.NewSimpleClientset(backup) - - fakeWatch := watch.NewFake() - defer fakeWatch.Stop() - client.PrependWatchReactor("backups", core.DefaultWatchReactor(fakeWatch, nil)) - - sharedInformers := informers.NewSharedInformerFactory(client, 0) - - controller := NewGCController( +func mockGCReconciler(fakeClient kbclient.Client, fakeClock *clock.FakeClock) *gcReconciler { + gcr := NewGCReconciler( velerotest.NewLogger(), - sharedInformers.Velero().V1().Backups(), - sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), - client.VeleroV1(), - nil, - defaultGCFrequency, - ).(*gcController) - - keys := make(chan string) - - controller.syncHandler = func(key string) error { - keys <- key - return nil - } - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - go sharedInformers.Start(ctx.Done()) - go controller.Run(ctx, 1) - - // wait for the AddFunc - select { - case <-ctx.Done(): - t.Fatal("test timed out waiting for AddFunc") - case key := <-keys: - assert.Equal(t, expected, key) - } - - backup.Status.Version = 1234 - fakeWatch.Add(backup) - - // wait for the UpdateFunc - select { - case <-ctx.Done(): - t.Fatal("test timed out waiting for UpdateFunc") - case key := <-keys: - assert.Equal(t, expected, key) - } + fakeClient, + ) + gcr.clock = fakeClock + return gcr } -func TestGCControllerProcessQueueItem(t *testing.T) { - +func TestGCReconcile(t *testing.T) { fakeClock := clock.NewFakeClock(time.Now()) - defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result() + defaultBackupLocation := builder.ForBackupStorageLocation(velerov1api.DefaultNamespace, "default").Result() tests := []struct { - name string - backup *velerov1api.Backup - deleteBackupRequests []*velerov1api.DeleteBackupRequest - backupLocation *velerov1api.BackupStorageLocation - expectDeletion bool - createDeleteBackupRequestError bool - expectError bool + name string + backup *velerov1api.Backup + deleteBackupRequests []*velerov1api.DeleteBackupRequest + backupLocation *velerov1api.BackupStorageLocation + expectError bool }{ { name: "can't find backup - no error", @@ -172,25 +61,21 @@ func TestGCControllerProcessQueueItem(t *testing.T) { name: "unexpired backup is not deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(time.Minute)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, - expectDeletion: false, }, { name: "expired backup in read-only storage location is not deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(), - expectDeletion: false, }, { name: "expired backup in read-write storage location is deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).Result(), - expectDeletion: true, }, { name: "expired backup with no pending deletion requests is deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, - expectDeletion: true, }, { name: "expired backup with a pending deletion request is not deleted", @@ -211,7 +96,6 @@ func TestGCControllerProcessQueueItem(t *testing.T) { }, }, }, - expectDeletion: false, }, { name: "expired backup with only processed deletion requests is deleted", @@ -232,72 +116,31 @@ func TestGCControllerProcessQueueItem(t *testing.T) { }, }, }, - expectDeletion: true, - }, - { - name: "create DeleteBackupRequest error returns an error", - backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), - backupLocation: defaultBackupLocation, - expectDeletion: true, - createDeleteBackupRequestError: true, - expectError: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var ( - client = fake.NewSimpleClientset() - sharedInformers = informers.NewSharedInformerFactory(client, 0) - ) - - var fakeClient kbclient.Client - if test.backupLocation != nil { - fakeClient = velerotest.NewFakeControllerRuntimeClient(t, test.backupLocation) - } else { - fakeClient = velerotest.NewFakeControllerRuntimeClient(t) + if test.backup == nil { + return } - controller := NewGCController( - velerotest.NewLogger(), - sharedInformers.Velero().V1().Backups(), - sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), - client.VeleroV1(), - fakeClient, - defaultGCFrequency, - ).(*gcController) - controller.clock = fakeClock + initObjs := []runtime.Object{} + initObjs = append(initObjs, test.backup) - var key string - if test.backup != nil { - key = kube.NamespaceAndName(test.backup) - sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup) + if test.backupLocation != nil { + initObjs = append(initObjs, test.backupLocation) } for _, dbr := range test.deleteBackupRequests { - sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(dbr) + initObjs = append(initObjs, dbr) } - if test.createDeleteBackupRequestError { - client.PrependReactor("create", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("foo") - }) - } - - err := controller.processQueueItem(key) + fakeClient := velerotest.NewFakeControllerRuntimeClient(t, initObjs...) + reconciler := mockGCReconciler(fakeClient, fakeClock) + _, err := reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}}) gotErr := err != nil assert.Equal(t, test.expectError, gotErr) - - if test.expectDeletion { - require.Len(t, client.Actions(), 1) - - createAction, ok := client.Actions()[0].(core.CreateAction) - require.True(t, ok) - - assert.Equal(t, "deletebackuprequests", createAction.GetResource().Resource) - } else { - assert.Len(t, client.Actions(), 0) - } }) } } From 5cddaeae6ce903126bb625a306fd540ada8ac651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=BE=99=E5=B3=B0?= Date: Tue, 16 Aug 2022 09:28:57 +0800 Subject: [PATCH 258/366] check vsc null pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 李龙峰 --- changelogs/unreleased/5217-lilongfeng0902 | 1 + pkg/controller/backup_controller.go | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelogs/unreleased/5217-lilongfeng0902 diff --git a/changelogs/unreleased/5217-lilongfeng0902 b/changelogs/unreleased/5217-lilongfeng0902 new file mode 100644 index 0000000000..819069ac3e --- /dev/null +++ b/changelogs/unreleased/5217-lilongfeng0902 @@ -0,0 +1 @@ +check vsc null pointer \ No newline at end of file diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 59080abf37..33cd1ca3ed 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -951,6 +951,10 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api if vs.Status.BoundVolumeSnapshotContentName != nil && len(*vs.Status.BoundVolumeSnapshotContentName) > 0 { vsc = vscMap[*vs.Status.BoundVolumeSnapshotContentName] + if nil == vsc { + logger.Errorf("Not find %s from the vscMap", vs.Status.BoundVolumeSnapshotContentName) + return + } if vsc.Spec.DeletionPolicy == snapshotv1api.VolumeSnapshotContentDelete { modifyVSCFlag = true } From 2bf054ad0b97e04f9bf72936cf9b107cece35837 Mon Sep 17 00:00:00 2001 From: Ming Date: Fri, 19 Aug 2022 06:45:28 +0000 Subject: [PATCH 259/366] update upstream codes Signed-off-by: Ming --- .../v1/pod_volume_operation_progress.go | 55 ----------- pkg/apis/velero/v1/progress.go | 26 ----- pkg/cmd/util/output/backup_describer.go | 2 +- .../pod_volume_backup_controller.go | 37 ++++++++ .../pod_volume_restore_controller.go | 37 ++++++++ pkg/uploader/kopia/progress.go | 12 +-- pkg/uploader/kopia/snapshot.go | 11 ++- pkg/uploader/kopia/snapshot_test.go | 3 +- pkg/uploader/provider/kopia.go | 94 ++++++++++--------- pkg/uploader/provider/kopia_test.go | 10 +- pkg/uploader/provider/provider.go | 17 ++-- pkg/uploader/types.go | 11 +++ 12 files changed, 162 insertions(+), 153 deletions(-) delete mode 100644 pkg/apis/velero/v1/progress.go diff --git a/pkg/apis/velero/v1/pod_volume_operation_progress.go b/pkg/apis/velero/v1/pod_volume_operation_progress.go index 4461a7e7ab..ceb67a87ea 100644 --- a/pkg/apis/velero/v1/pod_volume_operation_progress.go +++ b/pkg/apis/velero/v1/pod_volume_operation_progress.go @@ -16,13 +16,6 @@ limitations under the License. package v1 -import ( - "context" - - "github.com/sirupsen/logrus" - "sigs.k8s.io/controller-runtime/pkg/client" -) - // PodVolumeOperationProgress represents the progress of a // PodVolumeBackup/Restore (restic) operation type PodVolumeOperationProgress struct { @@ -32,51 +25,3 @@ type PodVolumeOperationProgress struct { // +optional BytesDone int64 `json:"bytesDone,omitempty"` } - -type BackupProgressUpdater struct { - pvb *PodVolumeBackup - log logrus.FieldLogger - ctx context.Context - cli client.Client -} - -type RestoreProgressUpdater struct { - pvr *PodVolumeRestore - log logrus.FieldLogger - ctx context.Context - cli client.Client -} - -func NewBackupProgressUpdater(pvb *PodVolumeBackup, log logrus.FieldLogger, ctx context.Context, cli client.Client) *BackupProgressUpdater { - return &BackupProgressUpdater{pvb, log, ctx, cli} -} - -//UpdateProgress which implement ProgressUpdater to update pvb progress status -func (b *BackupProgressUpdater) UpdateProgress(p *UploaderProgress) { - original := b.pvb.DeepCopy() - b.pvb.Status.Progress = PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} - if b.cli == nil { - b.log.Errorf("failed to update backup pod %s volume %s progress with uninitailize client", b.pvb.Spec.Pod.Name, b.pvb.Spec.Volume) - return - } - if err := b.cli.Patch(b.ctx, b.pvb, client.MergeFrom(original)); err != nil { - b.log.Errorf("update backup pod %s volume %s progress with %v", b.pvb.Spec.Pod.Name, b.pvb.Spec.Volume, err) - } -} - -func NewRestoreProgressUpdater(pvr *PodVolumeRestore, log logrus.FieldLogger, ctx context.Context, cli client.Client) *RestoreProgressUpdater { - return &RestoreProgressUpdater{pvr, log, ctx, cli} -} - -//UpdateProgress which implement ProgressUpdater to update update pvb progress status -func (r *RestoreProgressUpdater) UpdateProgress(p *UploaderProgress) { - original := r.pvr.DeepCopy() - r.pvr.Status.Progress = PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} - if r.cli == nil { - r.log.Errorf("failed to update restore pod %s volume %s progress with uninitailize client", r.pvr.Spec.Pod.Name, r.pvr.Spec.Volume) - return - } - if err := r.cli.Patch(r.ctx, r.pvr, client.MergeFrom(original)); err != nil { - r.log.Errorf("update restore pod %s volume %s progress with %v", r.pvr.Spec.Pod.Name, r.pvr.Spec.Volume, err) - } -} diff --git a/pkg/apis/velero/v1/progress.go b/pkg/apis/velero/v1/progress.go deleted file mode 100644 index cf5d42b1d7..0000000000 --- a/pkg/apis/velero/v1/progress.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright The Velero Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1 - -type UploaderProgress struct { - TotalBytes int64 `json:"totalBytes,omitempty"` - BytesDone int64 `json:"doneBytes,omitempty"` -} - -type ProgressUpdater interface { - UpdateProgress(p *UploaderProgress) -} diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 1ec0928316..2bc742c9f5 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -486,7 +486,7 @@ func (v *volumesByPod) Add(namespace, name, volume, phase string, progress veler key := fmt.Sprintf("%s/%s", namespace, name) // append backup progress percentage if backup is in progress - if phase == "In Progress" && progress != (velerov1api.PodVolumeOperationProgress{}) { + if phase == "In Progress" && progress.TotalBytes != 0 { volume = fmt.Sprintf("%s (%.2f%%)", volume, float64(progress.BytesDone)/float64(progress.TotalBytes)*100) } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 3b21189265..635ef00a76 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -38,6 +38,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -61,6 +62,13 @@ type PodVolumeBackupReconciler struct { Log logrus.FieldLogger } +type BackupProgressUpdater struct { + PodVolumeBackup *velerov1api.PodVolumeBackup + Log logrus.FieldLogger + Ctx context.Context + Cli client.Client +} + // +kubebuilder:rbac:groups=velero.io,resources=podvolumebackups,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=podvolumebackups/status,verbs=get;update;patch func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -264,6 +272,18 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l return mostRecentPVB.Status.SnapshotID } +// updateBackupProgressFunc returns a func that takes progress info and patches +// the PVB with the new progress. +func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { + return func(progress velerov1api.PodVolumeOperationProgress) { + original := pvb.DeepCopy() + pvb.Status.Progress = progress + if err := r.Client.Patch(context.Background(), pvb, client.MergeFrom(original)); err != nil { + log.WithError(err).Error("error update progress") + } + } +} + func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed @@ -352,3 +372,20 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log return cmd, nil } + +func (r *PodVolumeBackupReconciler) NewBackupProgressUpdater(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger, ctx context.Context) *BackupProgressUpdater { + return &BackupProgressUpdater{pvb, log, ctx, r.Client} +} + +//UpdateProgress which implement ProgressUpdater interface to update pvb progress status +func (b *BackupProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) { + original := b.PodVolumeBackup.DeepCopy() + b.PodVolumeBackup.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} + if b.Cli == nil { + b.Log.Errorf("failed to update backup pod %s volume %s progress with uninitailize client", b.PodVolumeBackup.Spec.Pod.Name, b.PodVolumeBackup.Spec.Volume) + return + } + if err := b.Cli.Patch(b.Ctx, b.PodVolumeBackup, client.MergeFrom(original)); err != nil { + b.Log.Errorf("update backup pod %s volume %s progress with %v", b.PodVolumeBackup.Spec.Pod.Name, b.PodVolumeBackup.Spec.Volume, err) + } +} diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 70cf1a53d3..00853710fc 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -41,6 +41,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -64,6 +65,13 @@ type PodVolumeRestoreReconciler struct { clock clock.Clock } +type RestoreProgressUpdater struct { + PodVolumeRestore *velerov1api.PodVolumeRestore + Log logrus.FieldLogger + Ctx context.Context + Cli client.Client +} + // +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores/status,verbs=get;update;patch // +kubebuilder:rbac:groups="",resources=pods,verbs=get @@ -317,3 +325,32 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return nil } + +// updateRestoreProgressFunc returns a func that takes progress info and patches +// the PVR with the new progress +func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { + return func(progress velerov1api.PodVolumeOperationProgress) { + original := req.DeepCopy() + req.Status.Progress = progress + if err := c.Patch(context.Background(), req, client.MergeFrom(original)); err != nil { + log.WithError(err).Error("Unable to update PodVolumeRestore progress") + } + } +} + +func (r *PodVolumeRestoreReconciler) NewRestoreProgressUpdater(pvr *velerov1api.PodVolumeRestore, log logrus.FieldLogger, ctx context.Context) *RestoreProgressUpdater { + return &RestoreProgressUpdater{pvr, log, ctx, r.Client} +} + +//UpdateProgress which implement ProgressUpdater interface to update pvr progress status +func (r *RestoreProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) { + original := r.PodVolumeRestore.DeepCopy() + r.PodVolumeRestore.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone} + if r.Cli == nil { + r.Log.Errorf("failed to update restore pod %s volume %s progress with uninitailize client", r.PodVolumeRestore.Spec.Pod.Name, r.PodVolumeRestore.Spec.Volume) + return + } + if err := r.Cli.Patch(r.Ctx, r.PodVolumeRestore, client.MergeFrom(original)); err != nil { + r.Log.Errorf("update restore pod %s volume %s progress with %v", r.PodVolumeRestore.Spec.Pod.Name, r.PodVolumeRestore.Spec.Volume, err) + } +} diff --git a/pkg/uploader/kopia/progress.go b/pkg/uploader/kopia/progress.go index 7da518eb56..d768181269 100644 --- a/pkg/uploader/kopia/progress.go +++ b/pkg/uploader/kopia/progress.go @@ -20,7 +20,7 @@ import ( "sync/atomic" "time" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/uploader" ) //Throttle throttles controlle the interval of output result @@ -60,9 +60,9 @@ type KopiaProgress struct { estimatedFileCount int32 // +checklocksignore the total count of files to be processed estimatedTotalBytes int64 // +checklocksignore the total size of files to be processed // +checkatomic - processedBytes int64 // which statistic all bytes has been processed currently - outputThrottle Throttle // which control the frequency of update progress - Updater velerov1api.ProgressUpdater //which the kopia progress will call the UpdateProgress, the third party will implement the interface to update progress + processedBytes int64 // which statistic all bytes has been processed currently + outputThrottle Throttle // which control the frequency of update progress + Updater uploader.ProgressUpdater //which kopia progress will call the UpdateProgress interface, the third party will implement the interface to do the progress update } //UploadedBytes the total bytes has uploaded currently @@ -90,10 +90,10 @@ func (p *KopiaProgress) EstimatedDataSize(fileCount int, totalBytes int64) { p.UpdateProgress() } -//UpdateProgress which called by UpdateProgress func, it is used to update pvb or pvr status +//UpdateProgress which calls Updater UpdateProgress interface, update progress by third-party implementation func (p *KopiaProgress) UpdateProgress() { if p.outputThrottle.ShouldOutput() { - p.Updater.UpdateProgress(&velerov1api.UploaderProgress{TotalBytes: p.estimatedTotalBytes, BytesDone: p.processedBytes}) + p.Updater.UpdateProgress(&uploader.UploaderProgress{TotalBytes: p.estimatedTotalBytes, BytesDone: p.processedBytes}) } } diff --git a/pkg/uploader/kopia/snapshot.go b/pkg/uploader/kopia/snapshot.go index 5b6e338b41..c3ab2d10e0 100644 --- a/pkg/uploader/kopia/snapshot.go +++ b/pkg/uploader/kopia/snapshot.go @@ -18,7 +18,6 @@ package kopia import ( "context" - "fmt" "math" "os" "path/filepath" @@ -28,7 +27,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/service" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/kopia/kopia/fs" @@ -86,7 +85,7 @@ func setupDefaultPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceIn func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath string, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { if fsUploader == nil { - return nil, fmt.Errorf("get empty kopia uploader") + return nil, errors.New("get empty kopia uploader") } dir, err := filepath.Abs(sourcePath) if err != nil { @@ -94,9 +93,11 @@ func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter rep } sourceInfo := snapshot.SourceInfo{ - Path: filepath.Clean(dir), + UserName: udmrepo.GetRepoUser(), + Host: udmrepo.GetRepoDomain(), + Path: filepath.Clean(dir), } - sourceInfo.UserName, sourceInfo.Host = service.GetRepoUser() + rootDir, err := getLocalFSEntry(sourceInfo.Path) if err != nil { return nil, errors.Wrap(err, "Unable to get local filesystem entry") diff --git a/pkg/uploader/kopia/snapshot_test.go b/pkg/uploader/kopia/snapshot_test.go index 3a60658c91..a5b6d81fbe 100644 --- a/pkg/uploader/kopia/snapshot_test.go +++ b/pkg/uploader/kopia/snapshot_test.go @@ -27,7 +27,6 @@ import ( "github.com/stretchr/testify/mock" repomocks "github.com/vmware-tanzu/velero/pkg/repository/mocks" - "github.com/vmware-tanzu/velero/pkg/uploader" uploadermocks "github.com/vmware-tanzu/velero/pkg/uploader/mocks" ) @@ -187,7 +186,7 @@ func TestSnapshotSource(t *testing.T) { t.Run(tc.name, func(t *testing.T) { s := InjectSnapshotFuncs() MockFuncs(s, tc.args) - _, _, err = SnapshotSource(ctx, s.repoWriterMock, s.uploderMock, sourceInfo, rootDir, "/", log, "TestSnapshotSource", func(up uploader.UploaderProgress) {}) + _, _, err = SnapshotSource(ctx, s.repoWriterMock, s.uploderMock, sourceInfo, rootDir, "/", log, "TestSnapshotSource") if tc.notError { assert.NoError(t, err) } else { diff --git a/pkg/uploader/provider/kopia.go b/pkg/uploader/provider/kopia.go index 6bdd1a4462..6384142eff 100644 --- a/pkg/uploader/provider/kopia.go +++ b/pkg/uploader/provider/kopia.go @@ -20,12 +20,12 @@ import ( "context" "fmt" "strings" - "time" "github.com/kopia/kopia/snapshot/snapshotfs" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/kopia" "github.com/vmware-tanzu/velero/internal/credentials" @@ -40,20 +40,17 @@ var BackupFunc = kopia.Backup var RestoreFunc = kopia.Restore //kopiaProvider recorded info related with kopiaProvider -//action which means provider handle backup or restore type kopiaProvider struct { - bkRepo udmrepo.BackupRepo - credGetter *credentials.CredentialGetter - uploader *snapshotfs.Uploader - restoreCancel chan struct{} - log logrus.FieldLogger + bkRepo udmrepo.BackupRepo + credGetter *credentials.CredentialGetter + log logrus.FieldLogger } //NewKopiaUploaderProvider initialized with open or create a repository func NewKopiaUploaderProvider( ctx context.Context, credGetter *credentials.CredentialGetter, - bsl *velerov1api.BackupStorageLocation, + backupRepo *velerov1api.BackupRepository, log logrus.FieldLogger, ) (Provider, error) { kp := &kopiaProvider{ @@ -61,7 +58,7 @@ func NewKopiaUploaderProvider( credGetter: credGetter, } //repoUID which is used to generate kopia repository config with unique directory path - repoUID := string(bsl.GetUID()) + repoUID := string(backupRepo.GetUID()) repoOpt, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(kp, ""), udmrepo.WithConfigFile("", repoUID), @@ -81,24 +78,22 @@ func NewKopiaUploaderProvider( return kp, nil } -//CheckContext check context status periodically -//check if context is timeout or cancel -func (kp *kopiaProvider) CheckContext(ctx context.Context) { - for { - select { - case <-ctx.Done(): - if kp.uploader != nil { - kp.uploader.Cancel() - kp.log.Infof("Backup is been canceled") - } - if kp.restoreCancel != nil { - close(kp.restoreCancel) - kp.log.Infof("Restore is been canceled") - } - return - default: - time.Sleep(time.Second * 10) +//CheckContext check context status check if context is timeout or cancel and backup restore once finished it will quit and return +func (kp *kopiaProvider) CheckContext(ctx context.Context, finishChan chan struct{}, restoreChan chan struct{}, uploader *snapshotfs.Uploader) { + select { + case <-finishChan: + kp.log.Infof("Action finished") + return + case <-ctx.Done(): + if uploader != nil { + uploader.Cancel() + kp.log.Infof("Backup is been canceled") } + if restoreChan != nil { + close(restoreChan) + kp.log.Infof("Restore is been canceled") + } + return } } @@ -106,15 +101,15 @@ func (kp *kopiaProvider) Close(ctx context.Context) { kp.bkRepo.Close(ctx) } -//RunBackup which will backup specific path and update backup progress in pvb status +//RunBackup which will backup specific path and update backup progress func (kp *kopiaProvider) RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updater velerov1api.ProgressUpdater) (string, error) { + updater uploader.ProgressUpdater) (string, error) { if updater == nil { - return "", errors.New("Need to inital backup progress updater first") + return "", errors.New("Need to initial backup progress updater first") } log := kp.log.WithFields(logrus.Fields{ @@ -122,25 +117,29 @@ func (kp *kopiaProvider) RunBackup( "parentSnapshot": parentSnapshot, }) repoWriter := kopia.NewShimRepo(kp.bkRepo) - kp.uploader = snapshotfs.NewUploader(repoWriter) + kpUploader := snapshotfs.NewUploader(repoWriter) prorgess := new(kopia.KopiaProgress) prorgess.InitThrottle(backupProgressCheckInterval) prorgess.Updater = updater - kp.uploader.Progress = prorgess - + kpUploader.Progress = prorgess + quit := make(chan struct{}) log.Info("Starting backup") - go kp.CheckContext(ctx) + go kp.CheckContext(ctx, quit, nil, kpUploader) + + defer func() { + close(quit) + }() - snapshotInfo, err := BackupFunc(ctx, kp.uploader, repoWriter, path, parentSnapshot, log) + snapshotInfo, err := BackupFunc(ctx, kpUploader, repoWriter, path, parentSnapshot, log) if err != nil { return "", errors.Wrapf(err, "Failed to run kopia backup") } else if snapshotInfo == nil { return "", fmt.Errorf("failed to get kopia backup snapshot info for path %v", path) } - + // which ensure that the statistic data of TotalBytes equal to BytesDone when finished updater.UpdateProgress( - &velerov1api.UploaderProgress{ + &uploader.UploaderProgress{ TotalBytes: snapshotInfo.Size, BytesDone: snapshotInfo.Size, }, @@ -162,12 +161,12 @@ func (kp *kopiaProvider) GetPassword(param interface{}) (string, error) { return strings.TrimSpace(rawPass), nil } -//RunRestore which will restore specific path and update restore progress in pvr status +//RunRestore which will restore specific path and update restore progress func (kp *kopiaProvider) RunRestore( ctx context.Context, snapshotID string, volumePath string, - updater velerov1api.ProgressUpdater) error { + updater uploader.ProgressUpdater) error { log := kp.log.WithFields(logrus.Fields{ "snapshotID": snapshotID, "volumePath": volumePath, @@ -176,22 +175,27 @@ func (kp *kopiaProvider) RunRestore( prorgess := new(kopia.KopiaProgress) prorgess.InitThrottle(restoreProgressCheckInterval) prorgess.Updater = updater - kp.restoreCancel = make(chan struct{}) + restoreCancel := make(chan struct{}) + quit := make(chan struct{}) + + log.Info("Starting restore") + go kp.CheckContext(ctx, quit, restoreCancel, nil) + defer func() { - if kp.restoreCancel != nil { - close(kp.restoreCancel) + if restoreCancel != nil { + close(restoreCancel) } + close(quit) }() - log.Info("Starting restore") - go kp.CheckContext(ctx) - size, fileCount, err := RestoreFunc(ctx, repoWriter, prorgess, snapshotID, volumePath, log, kp.restoreCancel) + size, fileCount, err := RestoreFunc(ctx, repoWriter, prorgess, snapshotID, volumePath, log, restoreCancel) if err != nil { return errors.Wrapf(err, "Failed to run kopia restore") } - updater.UpdateProgress(&velerov1api.UploaderProgress{ + // which ensure that the statistic data of TotalBytes equal to BytesDone when finished + updater.UpdateProgress(&uploader.UploaderProgress{ TotalBytes: size, BytesDone: size, }) diff --git a/pkg/uploader/provider/kopia_test.go b/pkg/uploader/provider/kopia_test.go index 3656053d07..747acbfb4c 100644 --- a/pkg/uploader/provider/kopia_test.go +++ b/pkg/uploader/provider/kopia_test.go @@ -28,6 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/controller" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/kopia" @@ -36,8 +37,7 @@ import ( func TestRunBackup(t *testing.T) { var kp kopiaProvider kp.log = logrus.New() - fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme) - updater := velerov1api.NewBackupProgressUpdater(&velerov1api.PodVolumeBackup{}, kp.log, context.Background(), fakeClient) + updater := controller.BackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { name string hookBackupFunc func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) @@ -68,7 +68,7 @@ func TestRunBackup(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { BackupFunc = tc.hookBackupFunc - _, err := kp.RunBackup(context.Background(), "var", nil, "", updater) + _, err := kp.RunBackup(context.Background(), "var", nil, "", &updater) if tc.notError { assert.NoError(t, err) } else { @@ -81,7 +81,7 @@ func TestRunBackup(t *testing.T) { func TestRunRestore(t *testing.T) { var kp kopiaProvider kp.log = logrus.New() - updater := velerov1api.NewRestoreProgressUpdater(&velerov1api.PodVolumeRestore{}, kp.log, context.Background(), fake.NewFakeClientWithScheme(scheme.Scheme)) + updater := controller.RestoreProgressUpdater{PodVolumeRestore: &velerov1api.PodVolumeRestore{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { name string @@ -107,7 +107,7 @@ func TestRunRestore(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { RestoreFunc = tc.hookRestoreFunc - err := kp.RunRestore(context.Background(), "", "/var", updater) + err := kp.RunRestore(context.Background(), "", "/var", &updater) if tc.notError { assert.NoError(t, err) } else { diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index c6fe0a177e..b435708f63 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -34,37 +34,38 @@ const backupProgressCheckInterval = 10 * time.Second // Provider which is designed for one pod volumn to do the backup or restore type Provider interface { // RunBackup which will do backup for one specific volumn and return snapshotID error - // updater which is used for update backup progress into related pvb status + // updater is used for updating backup progress which implement by third-party RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updater velerov1api.ProgressUpdater) (string, error) + updater uploader.ProgressUpdater) (string, error) // RunRestore which will do restore for one specific volumn with given snapshot id and return error - // updateFunc which is used for update restore progress into related pvr status + // updater is used for updating backup progress which implement by third-party RunRestore( ctx context.Context, snapshotID string, volumePath string, - updater velerov1api.ProgressUpdater) error + updater uploader.ProgressUpdater) error // Close which will close related repository Close(ctx context.Context) } -//NewUploaderProvider initialize provider with specific uploader_type +// NewUploaderProvider initialize provider with specific uploaderType func NewUploaderProvider( ctx context.Context, - uploader_type string, + uploaderType string, repoIdentifier string, bsl *velerov1api.BackupStorageLocation, + backupReo *velerov1api.BackupRepository, credGetter *credentials.CredentialGetter, repoKeySelector *v1.SecretKeySelector, log logrus.FieldLogger, ) (Provider, error) { - if uploader_type == uploader.KopiaType { + if uploaderType == uploader.KopiaType { return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) } else { - return NewKopiaUploaderProvider(ctx, credGetter, bsl, log) + return NewKopiaUploaderProvider(ctx, credGetter, backupReo, log) } } diff --git a/pkg/uploader/types.go b/pkg/uploader/types.go index 4f5274a97c..015a2c156e 100644 --- a/pkg/uploader/types.go +++ b/pkg/uploader/types.go @@ -40,3 +40,14 @@ type SnapshotInfo struct { ID string `json:"id"` Size int64 `json:"Size"` } + +//UploaderProgress which defined two variables to record progress +type UploaderProgress struct { + TotalBytes int64 `json:"totalBytes,omitempty"` + BytesDone int64 `json:"doneBytes,omitempty"` +} + +//UploaderProgress which defined generic interface to update progress +type ProgressUpdater interface { + UpdateProgress(p *UploaderProgress) +} From 5118c8ac01ed75bc6145f63d27b28ccb3598de94 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Mon, 22 Aug 2022 11:30:58 -0400 Subject: [PATCH 260/366] add OADP to adopters list Signed-off-by: Shubham Pampattiwar fix typo Signed-off-by: Shubham Pampattiwar --- ADOPTERS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 44f25513c1..af71b859d4 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -40,7 +40,9 @@ We have integrated our [solution with Velero][11] to provide our customers with Kyma [integrates with Velero][41] to effortlessly back up and restore Kyma clusters with all its resources. Velero capabilities allow Kyma users to define and run manual and scheduled backups in order to successfully handle a disaster-recovery scenario. **[Red Hat][50]** -Red Hat has developed the [Cluster Application Migration Tool][51] which uses [Velero and Restic][52] to drive the migration of applications between OpenShift clusters. +Red Hat has developed 2 operators for the OpenShift platform: +- [Migration Toolkit for Containers][51] (Crane): This operator uses [Velero and Restic][52] to drive the migration of applications between OpenShift clusters. +- [OADP (OpenShift API for Data Protection) Operator][53]: This operator sets up and installs Velero on the OpenShift platform, allowing users to backup and restore applications. **[Dell EMC][70]** For Kubernetes environments, [PowerProtect Data Manager][71] leverages the Container Storage Interface (CSI) framework to take snapshots to back up the persistent data or the data that the application creates e.g. databases. [Dell EMC leverages Velero][72] to backup the namespace configuration files (also known as Namespace meta data) for enterprise grade data protection. @@ -89,6 +91,7 @@ If you would like to add your logo to a future `Adopters of Velero` section on [ [50]: https://redhat.com [51]: https://github.com/fusor/mig-operator [52]: https://github.com/fusor/mig-operator/blob/master/docs/usage/2.md +[53]: https://github.com/openshift/oadp-operator [60]: https://banzaicloud.com [61]: https://banzaicloud.com/products/pipeline/ From 71e5027bfbe33eab0f4ad576f6ac580d4d551456 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 22 Aug 2022 20:25:15 +0800 Subject: [PATCH 261/366] Controller refactor code modifications. 1. Add some refactored controllers initiation code into enabledRuntimeControllers. 2. Add reconciler struct initiation function for DownloadRequest and ServerStatusRequest controllers. Signed-off-by: Xun Jiang --- changelogs/unreleased/5241-jxun | 1 + pkg/cmd/server/server.go | 77 ++++++++++--------- pkg/cmd/server/server_test.go | 16 ++-- pkg/controller/download_request_controller.go | 61 +++++++++------ .../download_request_controller_test.go | 20 ++--- .../server_status_request_controller.go | 52 ++++++++----- .../server_status_request_controller_test.go | 18 ++--- 7 files changed, 141 insertions(+), 104 deletions(-) create mode 100644 changelogs/unreleased/5241-jxun diff --git a/changelogs/unreleased/5241-jxun b/changelogs/unreleased/5241-jxun new file mode 100644 index 0000000000..23e5c0897d --- /dev/null +++ b/changelogs/unreleased/5241-jxun @@ -0,0 +1 @@ +Controller refactor code modifications. \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 96dc264e8f..ab15a08c9f 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -737,9 +737,13 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string controller.Restore: restoreControllerRunInfo, } // Note: all runtime type controllers that can be disabled are grouped separately, below: - enabledRuntimeControllers := make(map[string]struct{}) - enabledRuntimeControllers[controller.ServerStatusRequest] = struct{}{} - enabledRuntimeControllers[controller.DownloadRequest] = struct{}{} + enabledRuntimeControllers := map[string]struct{}{ + controller.ServerStatusRequest: {}, + controller.DownloadRequest: {}, + controller.Schedule: {}, + controller.ResticRepo: {}, + controller.BackupDeletion: {}, + } if s.config.restoreOnly { s.logger.Info("Restore only mode - not starting the backup, schedule, delete-backup, or GC controllers") @@ -802,50 +806,53 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupStorageLocation) } - if err := controller.NewScheduleReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.metrics).SetupWithManager(s.mgr); err != nil { - s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) + if _, ok := enabledRuntimeControllers[controller.Schedule]; ok { + if err := controller.NewScheduleReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.metrics).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) + } } - if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { - s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) + if _, ok := enabledRuntimeControllers[controller.ResticRepo]; ok { + if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) + } } - if err := controller.NewBackupDeletionReconciler( - s.logger, - s.mgr.GetClient(), - backupTracker, - s.repoManager, - s.metrics, - s.discoveryHelper, - newPluginManager, - backupStoreGetter, - ).SetupWithManager(s.mgr); err != nil { - s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupDeletion) + if _, ok := enabledRuntimeControllers[controller.BackupDeletion]; ok { + if err := controller.NewBackupDeletionReconciler( + s.logger, + s.mgr.GetClient(), + backupTracker, + s.repoManager, + s.metrics, + s.discoveryHelper, + newPluginManager, + backupStoreGetter, + ).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupDeletion) + } } if _, ok := enabledRuntimeControllers[controller.ServerStatusRequest]; ok { - r := controller.ServerStatusRequestReconciler{ - Scheme: s.mgr.GetScheme(), - Client: s.mgr.GetClient(), - Ctx: s.ctx, - PluginRegistry: s.pluginRegistry, - Clock: clock.RealClock{}, - Log: s.logger, - } - if err := r.SetupWithManager(s.mgr); err != nil { + if err := controller.NewServerStatusRequestReconciler( + s.mgr.GetClient(), + s.ctx, + s.pluginRegistry, + clock.RealClock{}, + s.logger, + ).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.ServerStatusRequest) } } if _, ok := enabledRuntimeControllers[controller.DownloadRequest]; ok { - r := controller.DownloadRequestReconciler{ - Scheme: s.mgr.GetScheme(), - Client: s.mgr.GetClient(), - Clock: clock.RealClock{}, - NewPluginManager: newPluginManager, - BackupStoreGetter: backupStoreGetter, - Log: s.logger, - } + r := controller.NewDownloadRequestReconciler( + s.mgr.GetClient(), + clock.RealClock{}, + newPluginManager, + backupStoreGetter, + s.logger, + ) if err := r.SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.DownloadRequest) } diff --git a/pkg/cmd/server/server_test.go b/pkg/cmd/server/server_test.go index df726e6bf9..e8110793c7 100644 --- a/pkg/cmd/server/server_test.go +++ b/pkg/cmd/server/server_test.go @@ -80,14 +80,14 @@ func TestRemoveControllers(t *testing.T) { errorExpected bool }{ { - name: "Remove one disabable controller", + name: "Remove one disable controller", disabledControllers: []string{ controller.Backup, }, errorExpected: false, }, { - name: "Remove all disabable controllers", + name: "Remove all disable controllers", disabledControllers: []string{ controller.Backup, controller.BackupDeletion, @@ -102,7 +102,7 @@ func TestRemoveControllers(t *testing.T) { errorExpected: false, }, { - name: "Remove with a non-disabable controller included", + name: "Remove with a non-disable controller included", disabledControllers: []string{ controller.Backup, controller.BackupStorageLocation, @@ -110,7 +110,7 @@ func TestRemoveControllers(t *testing.T) { errorExpected: true, }, { - name: "Remove with a misspelled/inexisting controller name", + name: "Remove with a misspelled/non-existing controller name", disabledControllers: []string{ "go", }, @@ -122,16 +122,16 @@ func TestRemoveControllers(t *testing.T) { enabledControllers := map[string]func() controllerRunInfo{ controller.BackupSync: func() controllerRunInfo { return controllerRunInfo{} }, controller.Backup: func() controllerRunInfo { return controllerRunInfo{} }, - controller.Schedule: func() controllerRunInfo { return controllerRunInfo{} }, controller.GarbageCollection: func() controllerRunInfo { return controllerRunInfo{} }, - controller.BackupDeletion: func() controllerRunInfo { return controllerRunInfo{} }, controller.Restore: func() controllerRunInfo { return controllerRunInfo{} }, - controller.ResticRepo: func() controllerRunInfo { return controllerRunInfo{} }, - controller.DownloadRequest: func() controllerRunInfo { return controllerRunInfo{} }, } enabledRuntimeControllers := map[string]struct{}{ controller.ServerStatusRequest: {}, + controller.Schedule: {}, + controller.BackupDeletion: {}, + controller.ResticRepo: {}, + controller.DownloadRequest: {}, } totalNumOriginalControllers := len(enabledControllers) + len(enabledRuntimeControllers) diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index a633daf0d2..c40d7aed3a 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -23,7 +23,6 @@ import ( "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -33,23 +32,39 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" ) -// DownloadRequestReconciler reconciles a DownloadRequest object -type DownloadRequestReconciler struct { - Scheme *runtime.Scheme - Client kbclient.Client - Clock clock.Clock +// downloadRequestReconciler reconciles a DownloadRequest object +type downloadRequestReconciler struct { + client kbclient.Client + clock clock.Clock // use variables to refer to these functions so they can be // replaced with fakes for testing. - NewPluginManager func(logrus.FieldLogger) clientmgmt.Manager - BackupStoreGetter persistence.ObjectBackupStoreGetter + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager + backupStoreGetter persistence.ObjectBackupStoreGetter - Log logrus.FieldLogger + log logrus.FieldLogger +} + +// NewDownloadRequestReconciler initializes and returns downloadRequestReconciler struct. +func NewDownloadRequestReconciler( + client kbclient.Client, + clock clock.Clock, + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, + backupStoreGetter persistence.ObjectBackupStoreGetter, + log logrus.FieldLogger, +) *downloadRequestReconciler { + return &downloadRequestReconciler{ + client: client, + clock: clock, + newPluginManager: newPluginManager, + backupStoreGetter: backupStoreGetter, + log: log, + } } // +kubebuilder:rbac:groups=velero.io,resources=downloadrequests,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=downloadrequests/status,verbs=get;update;patch -func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithFields(logrus.Fields{ +func (r *downloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.log.WithFields(logrus.Fields{ "controller": "download-request", "downloadRequest": req.NamespacedName, }) @@ -57,7 +72,7 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ // Fetch the DownloadRequest instance. log.Debug("Getting DownloadRequest") downloadRequest := &velerov1api.DownloadRequest{} - if err := r.Client.Get(ctx, req.NamespacedName, downloadRequest); err != nil { + if err := r.client.Get(ctx, req.NamespacedName, downloadRequest); err != nil { if apierrors.IsNotFound(err) { log.Debug("Unable to find DownloadRequest") return ctrl.Result{}, nil @@ -70,19 +85,19 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ original := downloadRequest.DeepCopy() defer func() { // Always attempt to Patch the downloadRequest object and status after each reconciliation. - if err := r.Client.Patch(ctx, downloadRequest, kbclient.MergeFrom(original)); err != nil { + if err := r.client.Patch(ctx, downloadRequest, kbclient.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating download request") return } }() if downloadRequest.Status != (velerov1api.DownloadRequestStatus{}) && downloadRequest.Status.Expiration != nil { - if downloadRequest.Status.Expiration.Time.Before(r.Clock.Now()) { + if downloadRequest.Status.Expiration.Time.Before(r.clock.Now()) { // Delete any request that is expired, regardless of the phase: it is not // worth proceeding and trying/retrying to find it. log.Debug("DownloadRequest has expired - deleting") - if err := r.Client.Delete(ctx, downloadRequest); err != nil { + if err := r.client.Delete(ctx, downloadRequest); err != nil { log.WithError(err).Error("Error deleting an expired download request") return ctrl.Result{}, errors.WithStack(err) } @@ -103,12 +118,12 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ if downloadRequest.Status.Phase == "" || downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseNew { // Update the expiration. - downloadRequest.Status.Expiration = &metav1.Time{Time: r.Clock.Now().Add(persistence.DownloadURLTTL)} + downloadRequest.Status.Expiration = &metav1.Time{Time: r.clock.Now().Add(persistence.DownloadURLTTL)} if downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreLog || downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreResults { restore := &velerov1api.Restore{} - if err := r.Client.Get(ctx, kbclient.ObjectKey{ + if err := r.client.Get(ctx, kbclient.ObjectKey{ Namespace: downloadRequest.Namespace, Name: downloadRequest.Spec.Target.Name, }, restore); err != nil { @@ -118,7 +133,7 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ } backup := &velerov1api.Backup{} - if err := r.Client.Get(ctx, kbclient.ObjectKey{ + if err := r.client.Get(ctx, kbclient.ObjectKey{ Namespace: downloadRequest.Namespace, Name: backupName, }, backup); err != nil { @@ -126,17 +141,17 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ } location := &velerov1api.BackupStorageLocation{} - if err := r.Client.Get(ctx, kbclient.ObjectKey{ + if err := r.client.Get(ctx, kbclient.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { return ctrl.Result{}, errors.WithStack(err) } - pluginManager := r.NewPluginManager(log) + pluginManager := r.newPluginManager(log) defer pluginManager.CleanupClients() - backupStore, err := r.BackupStoreGetter.Get(location, pluginManager, log) + backupStore, err := r.backupStoreGetter.Get(location, pluginManager, log) if err != nil { log.WithError(err).Error("Error getting a backup store") return ctrl.Result{}, errors.WithStack(err) @@ -149,7 +164,7 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ downloadRequest.Status.Phase = velerov1api.DownloadRequestPhaseProcessed // Update the expiration again to extend the time we wait (the TTL) to start after successfully processing the URL. - downloadRequest.Status.Expiration = &metav1.Time{Time: r.Clock.Now().Add(persistence.DownloadURLTTL)} + downloadRequest.Status.Expiration = &metav1.Time{Time: r.clock.Now().Add(persistence.DownloadURLTTL)} } // Requeue is mostly to handle deleting any expired requests that were not @@ -157,7 +172,7 @@ func (r *DownloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{Requeue: true}, nil } -func (r *DownloadRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *downloadRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.DownloadRequest{}). Complete(r) diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index f84bdaed0c..340a6e5ce2 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -87,7 +87,7 @@ var _ = Describe("Download Request Reconciler", func() { test.downloadRequest.Status.Expiration = &metav1.Time{Time: rClock.Now().Add(-1 * time.Minute)} } - fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme) + fakeClient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() err = fakeClient.Create(context.TODO(), test.downloadRequest) Expect(err).To(BeNil()) @@ -109,13 +109,13 @@ var _ = Describe("Download Request Reconciler", func() { // Setup reconciler Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - r := DownloadRequestReconciler{ - Client: fakeClient, - Clock: rClock, - NewPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - BackupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), - Log: velerotest.NewLogger(), - } + r := NewDownloadRequestReconciler( + fakeClient, + rClock, + func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + NewFakeObjectBackupStoreGetter(backupStores), + velerotest.NewLogger(), + ) if test.backupLocation != nil && test.expectGetsURL { backupStores[test.backupLocation.Name].On("GetDownloadURL", test.downloadRequest.Spec.Target).Return("a-url", nil) @@ -136,7 +136,7 @@ var _ = Describe("Download Request Reconciler", func() { } instance := &velerov1api.DownloadRequest{} - err = r.Client.Get(ctx, kbclient.ObjectKey{Name: test.downloadRequest.Name, Namespace: test.downloadRequest.Namespace}, instance) + err = r.client.Get(ctx, kbclient.ObjectKey{Name: test.downloadRequest.Name, Namespace: test.downloadRequest.Namespace}, instance) if test.expired { Expect(instance).ToNot(Equal(test.downloadRequest)) @@ -153,7 +153,7 @@ var _ = Describe("Download Request Reconciler", func() { if test.expectGetsURL { Expect(string(instance.Status.Phase)).To(Equal(string(velerov1api.DownloadRequestPhaseProcessed))) Expect(instance.Status.DownloadURL).To(Equal("a-url")) - Expect(velerotest.TimesAreEqual(instance.Status.Expiration.Time, r.Clock.Now().Add(signedURLTTL))).To(BeTrue()) + Expect(velerotest.TimesAreEqual(instance.Status.Expiration.Time, r.clock.Now().Add(signedURLTTL))).To(BeTrue()) } }, diff --git a/pkg/controller/server_status_request_controller.go b/pkg/controller/server_status_request_controller.go index 73b693968e..6d76576834 100644 --- a/pkg/controller/server_status_request_controller.go +++ b/pkg/controller/server_status_request_controller.go @@ -24,7 +24,6 @@ import ( "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -46,21 +45,36 @@ type PluginLister interface { List(kind framework.PluginKind) []framework.PluginIdentifier } -// ServerStatusRequestReconciler reconciles a ServerStatusRequest object -type ServerStatusRequestReconciler struct { - Scheme *runtime.Scheme - Client client.Client - Ctx context.Context - PluginRegistry PluginLister - Clock clock.Clock +// serverStatusRequestReconciler reconciles a ServerStatusRequest object +type serverStatusRequestReconciler struct { + client client.Client + ctx context.Context + pluginRegistry PluginLister + clock clock.Clock - Log logrus.FieldLogger + log logrus.FieldLogger +} + +// NewServerStatusRequestReconciler initializes and returns serverStatusRequestReconciler struct. +func NewServerStatusRequestReconciler( + client client.Client, + ctx context.Context, + pluginRegistry PluginLister, + clock clock.Clock, + log logrus.FieldLogger) *serverStatusRequestReconciler { + return &serverStatusRequestReconciler{ + client: client, + ctx: ctx, + pluginRegistry: pluginRegistry, + clock: clock, + log: log, + } } // +kubebuilder:rbac:groups=velero.io,resources=serverstatusrequests,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=serverstatusrequests/status,verbs=get;update;patch -func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithFields(logrus.Fields{ +func (r *serverStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.log.WithFields(logrus.Fields{ "controller": ServerStatusRequest, "serverStatusRequest": req.NamespacedName, }) @@ -68,7 +82,7 @@ func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl. // Fetch the ServerStatusRequest instance. log.Debug("Getting ServerStatusRequest") statusRequest := &velerov1api.ServerStatusRequest{} - if err := r.Client.Get(r.Ctx, req.NamespacedName, statusRequest); err != nil { + if err := r.client.Get(r.ctx, req.NamespacedName, statusRequest); err != nil { if apierrors.IsNotFound(err) { log.Debug("Unable to find ServerStatusRequest") return ctrl.Result{}, nil @@ -78,7 +92,7 @@ func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - log = r.Log.WithFields(logrus.Fields{ + log = r.log.WithFields(logrus.Fields{ "controller": ServerStatusRequest, "serverStatusRequest": req.NamespacedName, "phase": statusRequest.Status.Phase, @@ -90,23 +104,23 @@ func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl. original := statusRequest.DeepCopy() statusRequest.Status.ServerVersion = buildinfo.Version statusRequest.Status.Phase = velerov1api.ServerStatusRequestPhaseProcessed - statusRequest.Status.ProcessedTimestamp = &metav1.Time{Time: r.Clock.Now()} - statusRequest.Status.Plugins = velero.GetInstalledPluginInfo(r.PluginRegistry) + statusRequest.Status.ProcessedTimestamp = &metav1.Time{Time: r.clock.Now()} + statusRequest.Status.Plugins = velero.GetInstalledPluginInfo(r.pluginRegistry) - if err := r.Client.Patch(r.Ctx, statusRequest, client.MergeFrom(original)); err != nil { + if err := r.client.Patch(r.ctx, statusRequest, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating ServerStatusRequest status") return ctrl.Result{RequeueAfter: statusRequestResyncPeriod}, err } case velerov1api.ServerStatusRequestPhaseProcessed: log.Debug("Checking whether ServerStatusRequest has expired") expiration := statusRequest.Status.ProcessedTimestamp.Add(ttl) - if expiration.After(r.Clock.Now()) { + if expiration.After(r.clock.Now()) { log.Debug("ServerStatusRequest has not expired") return ctrl.Result{RequeueAfter: statusRequestResyncPeriod}, nil } log.Debug("ServerStatusRequest has expired, deleting it") - if err := r.Client.Delete(r.Ctx, statusRequest); err != nil { + if err := r.client.Delete(r.ctx, statusRequest); err != nil { log.WithError(err).Error("Unable to delete the request") return ctrl.Result{}, nil } @@ -119,7 +133,7 @@ func (r *ServerStatusRequestReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{RequeueAfter: statusRequestResyncPeriod}, nil } -func (r *ServerStatusRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *serverStatusRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.ServerStatusRequest{}). WithOptions(controller.Options{ diff --git a/pkg/controller/server_status_request_controller_test.go b/pkg/controller/server_status_request_controller_test.go index d91f6d5d1c..c968a60180 100644 --- a/pkg/controller/server_status_request_controller_test.go +++ b/pkg/controller/server_status_request_controller_test.go @@ -62,15 +62,15 @@ var _ = Describe("Server Status Request Reconciler", func() { func(test request) { // Setup reconciler Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - r := ServerStatusRequestReconciler{ - Client: fake.NewFakeClientWithScheme(scheme.Scheme, test.req), - Ctx: context.Background(), - PluginRegistry: test.reqPluginLister, - Clock: clock.NewFakeClock(now), - Log: velerotest.NewLogger(), - } + r := NewServerStatusRequestReconciler( + fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(test.req).Build(), + context.Background(), + test.reqPluginLister, + clock.NewFakeClock(now), + velerotest.NewLogger(), + ) - actualResult, err := r.Reconcile(r.Ctx, ctrl.Request{ + actualResult, err := r.Reconcile(r.ctx, ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: velerov1api.DefaultNamespace, Name: test.req.Name, @@ -86,7 +86,7 @@ var _ = Describe("Server Status Request Reconciler", func() { } instance := &velerov1api.ServerStatusRequest{} - err = r.Client.Get(ctx, kbclient.ObjectKey{Name: test.req.Name, Namespace: test.req.Namespace}, instance) + err = r.client.Get(ctx, kbclient.ObjectKey{Name: test.req.Name, Namespace: test.req.Namespace}, instance) // Assertions if test.expected == nil { From 55bf2de15dbdf384ebeb14b5ad7dba2f5c5aed83 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Fri, 19 Aug 2022 14:50:16 -0400 Subject: [PATCH 262/366] Check for empty ns list before checking nslist[0] In determining whether a backup includes all namespaces, item_collector checks for an empty string in the first element of the ns list. If processing includes+excludes results in an empty list, treat this as another case of a not-all-namespaces backup rather than crashing velero. Signed-off-by: Scott Seago --- changelogs/unreleased/5236-sseago | 1 + pkg/backup/item_collector.go | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5236-sseago diff --git a/changelogs/unreleased/5236-sseago b/changelogs/unreleased/5236-sseago new file mode 100644 index 0000000000..4d295cce85 --- /dev/null +++ b/changelogs/unreleased/5236-sseago @@ -0,0 +1 @@ +Check for empty ns list before checking nslist[0] diff --git a/pkg/backup/item_collector.go b/pkg/backup/item_collector.go index 5cbc178361..474fbabee3 100644 --- a/pkg/backup/item_collector.go +++ b/pkg/backup/item_collector.go @@ -225,8 +225,11 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group namespacesToList := getNamespacesToList(r.backupRequest.NamespaceIncludesExcludes) - // Check if we're backing up namespaces, and only certain ones - if gr == kuberesource.Namespaces && namespacesToList[0] != "" { + // Check if we're backing up namespaces for a less-than-full backup. + // We enter this block if resource is Namespaces and the namespae list is either empty or contains + // an explicit namespace list. (We skip this block if the list contains "" since that indicates + // a full-cluster backup + if gr == kuberesource.Namespaces && (len(namespacesToList) == 0 || namespacesToList[0] != "") { resourceClient, err := r.dynamicFactory.ClientForGroupVersionResource(gv, resource, "") if err != nil { log.WithError(err).Error("Error getting dynamic client") From ad4e733ef2bbd400a765a482f93edf90af2efe7b Mon Sep 17 00:00:00 2001 From: divolgin Date: Thu, 25 Aug 2022 01:06:59 +0000 Subject: [PATCH 263/366] Don't panic when storageClassName is not set in stateful sets Signed-off-by: divolgin --- changelogs/unreleased/5247-divolgin | 1 + pkg/restore/change_storageclass_action.go | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/5247-divolgin diff --git a/changelogs/unreleased/5247-divolgin b/changelogs/unreleased/5247-divolgin new file mode 100644 index 0000000000..7a6e24cfec --- /dev/null +++ b/changelogs/unreleased/5247-divolgin @@ -0,0 +1 @@ +Fix nil pointer panic when restoring StatefulSets \ No newline at end of file diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index 5714a1a7f3..577ab28ec2 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -99,7 +99,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut if len(sts.Spec.VolumeClaimTemplates) > 0 { for index, pvc := range sts.Spec.VolumeClaimTemplates { - exists, newStorageClass, err := a.isStorageClassExist(log, *pvc.Spec.StorageClassName, config) + exists, newStorageClass, err := a.isStorageClassExist(log, pvc.Spec.StorageClassName, config) if err != nil { return nil, err } else if !exists { @@ -124,7 +124,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut return nil, errors.Wrap(err, "error getting item's spec.storageClassName") } - exists, newStorageClass, err := a.isStorageClassExist(log, storageClass, config) + exists, newStorageClass, err := a.isStorageClassExist(log, &storageClass, config) if err != nil { return nil, err } else if !exists { @@ -140,15 +140,15 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut return velero.NewRestoreItemActionExecuteOutput(obj), nil } -func (a *ChangeStorageClassAction) isStorageClassExist(log *logrus.Entry, storageClass string, cm *corev1.ConfigMap) (exists bool, newStorageClass string, err error) { - if storageClass == "" { +func (a *ChangeStorageClassAction) isStorageClassExist(log *logrus.Entry, storageClass *string, cm *corev1.ConfigMap) (exists bool, newStorageClass string, err error) { + if storageClass == nil || *storageClass == "" { log.Debug("Item has no storage class specified") return false, "", nil } - newStorageClass, ok := cm.Data[storageClass] + newStorageClass, ok := cm.Data[*storageClass] if !ok { - log.Debugf("No mapping found for storage class %s", storageClass) + log.Debugf("No mapping found for storage class %s", *storageClass) return false, "", nil } From b54424bdc636a14d0032ed222b3676be85ab6555 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Fri, 27 May 2022 17:19:36 -0400 Subject: [PATCH 264/366] Refactor BackupItemAction to backupitemaction/v1 Signed-off-by: Hoang, Phuong --- hack/update-proto.sh | 3 + pkg/backup/backup.go | 5 +- pkg/backup/backup_test.go | 35 +- pkg/controller/backup_controller_test.go | 6 +- pkg/plugin/clientmgmt/manager.go | 24 +- .../restartable_backup_item_action.go | 7 +- pkg/plugin/framework/action_resolver.go | 10 +- pkg/plugin/framework/backup_item_action.go | 4 +- .../framework/backup_item_action_client.go | 10 +- .../framework/backup_item_action_server.go | 17 +- .../framework/backup_item_action_test.go | 3 +- .../framework/item_snapshotter_client.go | 3 +- pkg/plugin/generated/BackupItemAction.pb.go | 323 ------------------ pkg/plugin/generated/DeleteItemAction.pb.go | 81 ++++- pkg/plugin/generated/ItemSnapshotter.pb.go | 28 +- pkg/plugin/generated/ObjectStore.pb.go | 30 +- pkg/plugin/generated/PluginLister.pb.go | 8 +- pkg/plugin/generated/RestoreItemAction.pb.go | 12 +- pkg/plugin/generated/Shared.pb.go | 59 ++-- pkg/plugin/generated/VolumeSnapshotter.pb.go | 28 +- pkg/plugin/mocks/manager.go | 18 +- pkg/plugin/proto/Shared.proto | 1 + .../v1}/BackupItemAction.proto | 7 +- .../v1}/backup_item_action.go | 15 +- .../item_snapshotter/v1/item_snapshotter.go | 3 +- .../v1/mocks/item_snapshotter.go | 2 +- pkg/plugin/velero/shared.go | 9 + 27 files changed, 259 insertions(+), 492 deletions(-) delete mode 100644 pkg/plugin/generated/BackupItemAction.pb.go rename pkg/plugin/proto/{ => backupitemaction/v1}/BackupItemAction.proto (68%) rename pkg/plugin/velero/{ => backupitemaction/v1}/backup_item_action.go (81%) diff --git a/hack/update-proto.sh b/hack/update-proto.sh index 28ba964d17..bc8d3a14e6 100755 --- a/hack/update-proto.sh +++ b/hack/update-proto.sh @@ -18,6 +18,9 @@ HACK_DIR=$(dirname "${BASH_SOURCE}") echo "Updating plugin proto" +protoc pkg/plugin/proto/backupitemaction/v1/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ -I pkg/plugin/proto/ protoc pkg/plugin/proto/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ -I pkg/plugin/proto/ +cp -rf pkg/plugin/generated/github.com/vmware-tanzu/velero/pkg/plugin/generated/* pkg/plugin/generated +rm -rf pkg/plugin/generated/github.com echo "Updating plugin proto - done!" diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index d026b09cff..0ee855fe3f 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -45,6 +45,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -62,7 +63,7 @@ const BackupFormatVersion = "1.1.0" type Backupper interface { // Backup takes a backup using the specification in the velerov1api.Backup and writes backup and log data // to the given writers. - Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error + Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer, actions []biav1.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error BackupWithResolvers(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer, backupItemActionResolver framework.BackupItemActionResolver, itemSnapshotterResolver framework.ItemSnapshotterResolver, volumeSnapshotterGetter VolumeSnapshotterGetter) error @@ -170,7 +171,7 @@ type VolumeSnapshotterGetter interface { // back up individual resources that don't prevent the backup from continuing to be processed) are logged // to the backup log. func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer, - actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error { + actions []biav1.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error { backupItemActions := framework.NewBackupItemActionResolver(actions) itemSnapshotters := framework.NewItemSnapshotterResolver(nil) return kb.BackupWithResolvers(log, backupRequest, backupFile, backupItemActions, itemSnapshotters, diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index cf6a4269f8..42257290a7 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -47,6 +47,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" @@ -1360,7 +1361,7 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { h.addItems(t, resource) } - actions := []velero.BackupItemAction{} + actions := []biav1.BackupItemAction{} for action := range tc.actions { actions = append(actions, action) } @@ -1386,7 +1387,7 @@ func TestBackupWithInvalidActions(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []biav1.BackupItemAction }{ { name: "action with invalid label selector results in an error", @@ -1402,7 +1403,7 @@ func TestBackupWithInvalidActions(t *testing.T) { builder.ForPersistentVolume("baz").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ new(recordResourcesAction).ForLabelSelector("=invalid-selector"), }, }, @@ -1420,7 +1421,7 @@ func TestBackupWithInvalidActions(t *testing.T) { builder.ForPersistentVolume("baz").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &appliesToErrorAction{}, }, }, @@ -1482,7 +1483,7 @@ func TestBackupActionModifications(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []biav1.BackupItemAction want map[string]unstructuredObject }{ { @@ -1493,7 +1494,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(map[string]string{"updated": "true"}) }), @@ -1510,7 +1511,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(nil) }), @@ -1527,7 +1528,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.Object["spec"].(map[string]interface{})["nodeName"] = "foo" }), @@ -1545,7 +1546,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetName(item.GetName() + "-updated") item.SetNamespace(item.GetNamespace() + "-updated") @@ -1586,7 +1587,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []biav1.BackupItemAction want []string }{ { @@ -1599,7 +1600,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { @@ -1631,7 +1632,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1661,7 +1662,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1694,7 +1695,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1724,7 +1725,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1755,7 +1756,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1785,7 +1786,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []biav1.BackupItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 29e7ecc9c5..14dcc2349d 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -49,7 +49,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/framework" pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -59,7 +59,7 @@ type fakeBackupper struct { mock.Mock } -func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error { +func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []biav1.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error { args := b.Called(logger, backup, backupFile, actions, volumeSnapshotterGetter) return args.Error(0) } @@ -843,7 +843,7 @@ func TestProcessBackupCompletions(t *testing.T) { pluginManager.On("GetBackupItemActions").Return(nil, nil) pluginManager.On("CleanupClients").Return(nil) pluginManager.On("GetItemSnapshotters").Return(nil, nil) - backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []velero.BackupItemAction(nil), pluginManager).Return(nil) + backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []biav1.BackupItemAction(nil), pluginManager).Return(nil) backupper.On("BackupWithResolvers", mock.Anything, mock.Anything, mock.Anything, framework.BackupItemActionResolver{}, framework.ItemSnapshotterResolver{}, pluginManager).Return(nil) backupStore.On("BackupExists", test.backupLocation.Spec.StorageType.ObjectStorage.Bucket, test.backup.Name).Return(test.backupExists, test.existenceCheckError) diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index 9dc2ff3274..4766081380 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -20,12 +20,12 @@ import ( "strings" "sync" - v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" - "github.com/sirupsen/logrus" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" + isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) // Manager manages the lifecycles of plugins. @@ -37,10 +37,10 @@ type Manager interface { GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) // GetBackupItemActions returns all backup item action plugins. - GetBackupItemActions() ([]velero.BackupItemAction, error) + GetBackupItemActions() ([]biav1.BackupItemAction, error) // GetBackupItemAction returns the backup item action plugin for name. - GetBackupItemAction(name string) (velero.BackupItemAction, error) + GetBackupItemAction(name string) (biav1.BackupItemAction, error) // GetRestoreItemActions returns all restore item action plugins. GetRestoreItemActions() ([]velero.RestoreItemAction, error) @@ -55,10 +55,10 @@ type Manager interface { GetDeleteItemAction(name string) (velero.DeleteItemAction, error) // GetItemSnapshotter returns the item snapshotter plugin for name - GetItemSnapshotter(name string) (v1.ItemSnapshotter, error) + GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) // GetItemSnapshotters returns all item snapshotter plugins - GetItemSnapshotters() ([]v1.ItemSnapshotter, error) + GetItemSnapshotters() ([]isv1.ItemSnapshotter, error) // CleanupClients terminates all of the Manager's running plugin processes. CleanupClients() @@ -166,10 +166,10 @@ func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, e } // GetBackupItemActions returns all backup item actions as restartableBackupItemActions. -func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { +func (m *manager) GetBackupItemActions() ([]biav1.BackupItemAction, error) { list := m.registry.List(framework.PluginKindBackupItemAction) - actions := make([]velero.BackupItemAction, 0, len(list)) + actions := make([]biav1.BackupItemAction, 0, len(list)) for i := range list { id := list[i] @@ -186,7 +186,7 @@ func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { } // GetBackupItemAction returns a restartableBackupItemAction for name. -func (m *manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) { +func (m *manager) GetBackupItemAction(name string) (biav1.BackupItemAction, error) { name = sanitizeName(name) restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name) @@ -264,7 +264,7 @@ func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, err return r, nil } -func (m *manager) GetItemSnapshotter(name string) (v1.ItemSnapshotter, error) { +func (m *manager) GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) { name = sanitizeName(name) restartableProcess, err := m.getRestartableProcess(framework.PluginKindItemSnapshotter, name) @@ -276,10 +276,10 @@ func (m *manager) GetItemSnapshotter(name string) (v1.ItemSnapshotter, error) { return r, nil } -func (m *manager) GetItemSnapshotters() ([]v1.ItemSnapshotter, error) { +func (m *manager) GetItemSnapshotters() ([]isv1.ItemSnapshotter, error) { list := m.registry.List(framework.PluginKindItemSnapshotter) - actions := make([]v1.ItemSnapshotter, 0, len(list)) + actions := make([]isv1.ItemSnapshotter, 0, len(list)) for i := range list { id := list[i] diff --git a/pkg/plugin/clientmgmt/restartable_backup_item_action.go b/pkg/plugin/clientmgmt/restartable_backup_item_action.go index c8e96ab805..f9ff2faa35 100644 --- a/pkg/plugin/clientmgmt/restartable_backup_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_backup_item_action.go @@ -23,6 +23,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" ) // restartableBackupItemAction is a backup item action for a given implementation (such as "pod"). It is associated with @@ -45,13 +46,13 @@ func newRestartableBackupItemAction(name string, sharedPluginProcess Restartable // getBackupItemAction returns the backup item action for this restartableBackupItemAction. It does *not* restart the // plugin process. -func (r *restartableBackupItemAction) getBackupItemAction() (velero.BackupItemAction, error) { +func (r *restartableBackupItemAction) getBackupItemAction() (biav1.BackupItemAction, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - backupItemAction, ok := plugin.(velero.BackupItemAction) + backupItemAction, ok := plugin.(biav1.BackupItemAction) if !ok { return nil, errors.Errorf("%T is not a BackupItemAction!", plugin) } @@ -60,7 +61,7 @@ func (r *restartableBackupItemAction) getBackupItemAction() (velero.BackupItemAc } // getDelegate restarts the plugin process (if needed) and returns the backup item action for this restartableBackupItemAction. -func (r *restartableBackupItemAction) getDelegate() (velero.BackupItemAction, error) { +func (r *restartableBackupItemAction) getDelegate() (biav1.BackupItemAction, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/framework/action_resolver.go b/pkg/plugin/framework/action_resolver.go index 9797ba5269..345061aacb 100644 --- a/pkg/plugin/framework/action_resolver.go +++ b/pkg/plugin/framework/action_resolver.go @@ -22,10 +22,10 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" - - "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -98,11 +98,11 @@ func resolveAction(helper discovery.Helper, action velero.Applicable) (resources } type BackupItemResolvedAction struct { - velero.BackupItemAction + biav1.BackupItemAction resolvedAction } -func NewBackupItemActionResolver(actions []velero.BackupItemAction) BackupItemActionResolver { +func NewBackupItemActionResolver(actions []biav1.BackupItemAction) BackupItemActionResolver { return BackupItemActionResolver{ actions: actions, } @@ -131,7 +131,7 @@ type ActionResolver interface { } type BackupItemActionResolver struct { - actions []velero.BackupItemAction + actions []biav1.BackupItemAction } func (recv BackupItemActionResolver) ResolveActions(helper discovery.Helper) ([]BackupItemResolvedAction, error) { diff --git a/pkg/plugin/framework/backup_item_action.go b/pkg/plugin/framework/backup_item_action.go index 1dc7bf3e10..94bd6c8c0b 100644 --- a/pkg/plugin/framework/backup_item_action.go +++ b/pkg/plugin/framework/backup_item_action.go @@ -21,7 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" - proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" ) // BackupItemActionPlugin is an implementation of go-plugin's Plugin @@ -39,6 +39,6 @@ func (p *BackupItemActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBro // GRPCServer registers a BackupItemAction gRPC server. func (p *BackupItemActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterBackupItemActionServer(server, &BackupItemActionGRPCServer{mux: p.serverMux}) + protobiav1.RegisterBackupItemActionServer(server, &BackupItemActionGRPCServer{mux: p.serverMux}) return nil } diff --git a/pkg/plugin/framework/backup_item_action_client.go b/pkg/plugin/framework/backup_item_action_client.go index b48de39289..a654962c0d 100644 --- a/pkg/plugin/framework/backup_item_action_client.go +++ b/pkg/plugin/framework/backup_item_action_client.go @@ -27,7 +27,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -42,18 +42,18 @@ func NewBackupItemActionPlugin(options ...PluginOption) *BackupItemActionPlugin // gRPC client to make calls to the plugin server. type BackupItemActionGRPCClient struct { *clientBase - grpcClient proto.BackupItemActionClient + grpcClient protobiav1.BackupItemActionClient } func newBackupItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { return &BackupItemActionGRPCClient{ clientBase: base, - grpcClient: proto.NewBackupItemActionClient(clientConn), + grpcClient: protobiav1.NewBackupItemActionClient(clientConn), } } func (c *BackupItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { - req := &proto.BackupItemActionAppliesToRequest{ + req := &protobiav1.BackupItemActionAppliesToRequest{ Plugin: c.plugin, } @@ -86,7 +86,7 @@ func (c *BackupItemActionGRPCClient) Execute(item runtime.Unstructured, backup * return nil, nil, errors.WithStack(err) } - req := &proto.ExecuteRequest{ + req := &protobiav1.ExecuteRequest{ Plugin: c.plugin, Item: itemJSON, Backup: backupJSON, diff --git a/pkg/plugin/framework/backup_item_action_server.go b/pkg/plugin/framework/backup_item_action_server.go index fb4b8a6af6..dfea095954 100644 --- a/pkg/plugin/framework/backup_item_action_server.go +++ b/pkg/plugin/framework/backup_item_action_server.go @@ -25,7 +25,9 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" ) // BackupItemActionGRPCServer implements the proto-generated BackupItemAction interface, and accepts @@ -34,13 +36,13 @@ type BackupItemActionGRPCServer struct { mux *serverMux } -func (s *BackupItemActionGRPCServer) getImpl(name string) (velero.BackupItemAction, error) { +func (s *BackupItemActionGRPCServer) getImpl(name string) (biav1.BackupItemAction, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.BackupItemAction) + itemAction, ok := impl.(biav1.BackupItemAction) if !ok { return nil, errors.Errorf("%T is not a backup item action", impl) } @@ -48,7 +50,9 @@ func (s *BackupItemActionGRPCServer) getImpl(name string) (velero.BackupItemActi return itemAction, nil } -func (s *BackupItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.BackupItemActionAppliesToRequest) (response *proto.BackupItemActionAppliesToResponse, err error) { +func (s *BackupItemActionGRPCServer) AppliesTo( + ctx context.Context, req *protobiav1.BackupItemActionAppliesToRequest) ( + response *protobiav1.BackupItemActionAppliesToResponse, err error) { defer func() { if recoveredErr := handlePanic(recover()); recoveredErr != nil { err = recoveredErr @@ -65,7 +69,7 @@ func (s *BackupItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.B return nil, newGRPCError(err) } - return &proto.BackupItemActionAppliesToResponse{ + return &protobiav1.BackupItemActionAppliesToResponse{ &proto.ResourceSelector{ IncludedNamespaces: resourceSelector.IncludedNamespaces, ExcludedNamespaces: resourceSelector.ExcludedNamespaces, @@ -76,7 +80,8 @@ func (s *BackupItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.B }, nil } -func (s *BackupItemActionGRPCServer) Execute(ctx context.Context, req *proto.ExecuteRequest) (response *proto.ExecuteResponse, err error) { +func (s *BackupItemActionGRPCServer) Execute( + ctx context.Context, req *protobiav1.ExecuteRequest) (response *protobiav1.ExecuteResponse, err error) { defer func() { if recoveredErr := handlePanic(recover()); recoveredErr != nil { err = recoveredErr @@ -115,7 +120,7 @@ func (s *BackupItemActionGRPCServer) Execute(ctx context.Context, req *proto.Exe } } - res := &proto.ExecuteResponse{ + res := &protobiav1.ExecuteResponse{ Item: updatedItemJSON, } diff --git a/pkg/plugin/framework/backup_item_action_test.go b/pkg/plugin/framework/backup_item_action_test.go index 7d41e0e781..ec4da43cc0 100644 --- a/pkg/plugin/framework/backup_item_action_test.go +++ b/pkg/plugin/framework/backup_item_action_test.go @@ -31,6 +31,7 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/backup/mocks" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -160,7 +161,7 @@ func TestBackupItemActionGRPCServerExecute(t *testing.T) { }, }} - req := &proto.ExecuteRequest{ + req := &protobiav1.ExecuteRequest{ Plugin: "xyz", Item: test.item, Backup: test.backup, diff --git a/pkg/plugin/framework/item_snapshotter_client.go b/pkg/plugin/framework/item_snapshotter_client.go index dd341b9662..23786046a3 100644 --- a/pkg/plugin/framework/item_snapshotter_client.go +++ b/pkg/plugin/framework/item_snapshotter_client.go @@ -21,14 +21,13 @@ import ( "encoding/json" "time" - isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" - "github.com/pkg/errors" "google.golang.org/grpc" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) // NewItemSnapshotterPlugin constructs a ItemSnapshotterPlugin. diff --git a/pkg/plugin/generated/BackupItemAction.pb.go b/pkg/plugin/generated/BackupItemAction.pb.go deleted file mode 100644 index 937da01d05..0000000000 --- a/pkg/plugin/generated/BackupItemAction.pb.go +++ /dev/null @@ -1,323 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: BackupItemAction.proto - -/* -Package generated is a generated protocol buffer package. - -It is generated from these files: - BackupItemAction.proto - DeleteItemAction.proto - ItemSnapshotter.proto - ObjectStore.proto - PluginLister.proto - RestoreItemAction.proto - Shared.proto - VolumeSnapshotter.proto - -It has these top-level messages: - ExecuteRequest - ExecuteResponse - BackupItemActionAppliesToRequest - BackupItemActionAppliesToResponse - DeleteItemActionExecuteRequest - DeleteItemActionAppliesToRequest - DeleteItemActionAppliesToResponse - ItemSnapshotterAppliesToRequest - ItemSnapshotterAppliesToResponse - AlsoHandlesRequest - AlsoHandlesResponse - SnapshotItemRequest - SnapshotItemResponse - ProgressRequest - ProgressResponse - DeleteItemSnapshotRequest - CreateItemFromSnapshotRequest - CreateItemFromSnapshotResponse - ItemSnapshotterInitRequest - PutObjectRequest - ObjectExistsRequest - ObjectExistsResponse - GetObjectRequest - Bytes - ListCommonPrefixesRequest - ListCommonPrefixesResponse - ListObjectsRequest - ListObjectsResponse - DeleteObjectRequest - CreateSignedURLRequest - CreateSignedURLResponse - ObjectStoreInitRequest - PluginIdentifier - ListPluginsResponse - RestoreItemActionExecuteRequest - RestoreItemActionExecuteResponse - RestoreItemActionAppliesToRequest - RestoreItemActionAppliesToResponse - Empty - Stack - StackFrame - ResourceIdentifier - ResourceSelector - CreateVolumeRequest - CreateVolumeResponse - GetVolumeInfoRequest - GetVolumeInfoResponse - CreateSnapshotRequest - CreateSnapshotResponse - DeleteSnapshotRequest - GetVolumeIDRequest - GetVolumeIDResponse - SetVolumeIDRequest - SetVolumeIDResponse - VolumeSnapshotterInitRequest -*/ -package generated - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type ExecuteRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` - Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` -} - -func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} } -func (m *ExecuteRequest) String() string { return proto.CompactTextString(m) } -func (*ExecuteRequest) ProtoMessage() {} -func (*ExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *ExecuteRequest) GetPlugin() string { - if m != nil { - return m.Plugin - } - return "" -} - -func (m *ExecuteRequest) GetItem() []byte { - if m != nil { - return m.Item - } - return nil -} - -func (m *ExecuteRequest) GetBackup() []byte { - if m != nil { - return m.Backup - } - return nil -} - -type ExecuteResponse struct { - Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems" json:"additionalItems,omitempty"` -} - -func (m *ExecuteResponse) Reset() { *m = ExecuteResponse{} } -func (m *ExecuteResponse) String() string { return proto.CompactTextString(m) } -func (*ExecuteResponse) ProtoMessage() {} -func (*ExecuteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *ExecuteResponse) GetItem() []byte { - if m != nil { - return m.Item - } - return nil -} - -func (m *ExecuteResponse) GetAdditionalItems() []*ResourceIdentifier { - if m != nil { - return m.AdditionalItems - } - return nil -} - -type BackupItemActionAppliesToRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` -} - -func (m *BackupItemActionAppliesToRequest) Reset() { *m = BackupItemActionAppliesToRequest{} } -func (m *BackupItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } -func (*BackupItemActionAppliesToRequest) ProtoMessage() {} -func (*BackupItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{2} -} - -func (m *BackupItemActionAppliesToRequest) GetPlugin() string { - if m != nil { - return m.Plugin - } - return "" -} - -type BackupItemActionAppliesToResponse struct { - ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` -} - -func (m *BackupItemActionAppliesToResponse) Reset() { *m = BackupItemActionAppliesToResponse{} } -func (m *BackupItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } -func (*BackupItemActionAppliesToResponse) ProtoMessage() {} -func (*BackupItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{3} -} - -func (m *BackupItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { - if m != nil { - return m.ResourceSelector - } - return nil -} - -func init() { - proto.RegisterType((*ExecuteRequest)(nil), "generated.ExecuteRequest") - proto.RegisterType((*ExecuteResponse)(nil), "generated.ExecuteResponse") - proto.RegisterType((*BackupItemActionAppliesToRequest)(nil), "generated.BackupItemActionAppliesToRequest") - proto.RegisterType((*BackupItemActionAppliesToResponse)(nil), "generated.BackupItemActionAppliesToResponse") -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for BackupItemAction service - -type BackupItemActionClient interface { - AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) - Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) -} - -type backupItemActionClient struct { - cc *grpc.ClientConn -} - -func NewBackupItemActionClient(cc *grpc.ClientConn) BackupItemActionClient { - return &backupItemActionClient{cc} -} - -func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) { - out := new(BackupItemActionAppliesToResponse) - err := grpc.Invoke(ctx, "/generated.BackupItemAction/AppliesTo", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *backupItemActionClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) { - out := new(ExecuteResponse) - err := grpc.Invoke(ctx, "/generated.BackupItemAction/Execute", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for BackupItemAction service - -type BackupItemActionServer interface { - AppliesTo(context.Context, *BackupItemActionAppliesToRequest) (*BackupItemActionAppliesToResponse, error) - Execute(context.Context, *ExecuteRequest) (*ExecuteResponse, error) -} - -func RegisterBackupItemActionServer(s *grpc.Server, srv BackupItemActionServer) { - s.RegisterService(&_BackupItemAction_serviceDesc, srv) -} - -func _BackupItemAction_AppliesTo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BackupItemActionAppliesToRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BackupItemActionServer).AppliesTo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/generated.BackupItemAction/AppliesTo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BackupItemActionServer).AppliesTo(ctx, req.(*BackupItemActionAppliesToRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _BackupItemAction_Execute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExecuteRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BackupItemActionServer).Execute(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/generated.BackupItemAction/Execute", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BackupItemActionServer).Execute(ctx, req.(*ExecuteRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _BackupItemAction_serviceDesc = grpc.ServiceDesc{ - ServiceName: "generated.BackupItemAction", - HandlerType: (*BackupItemActionServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "AppliesTo", - Handler: _BackupItemAction_AppliesTo_Handler, - }, - { - MethodName: "Execute", - Handler: _BackupItemAction_Execute_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "BackupItemAction.proto", -} - -func init() { proto.RegisterFile("BackupItemAction.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 293 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0xc3, 0x40, - 0x10, 0x86, 0x49, 0x2b, 0x95, 0x4e, 0x8b, 0x2d, 0x7b, 0x28, 0x31, 0x22, 0xc4, 0x9c, 0x02, 0x4a, - 0x0e, 0xf1, 0xe6, 0xc9, 0x0a, 0x52, 0x7a, 0xdd, 0xf6, 0x05, 0xd2, 0x64, 0x5a, 0x17, 0xd3, 0xdd, - 0x75, 0x77, 0x03, 0x3e, 0x9c, 0x0f, 0x27, 0xd9, 0x6e, 0x43, 0x8c, 0xc5, 0x7a, 0xcb, 0x64, 0xe6, - 0xff, 0xe7, 0xfb, 0xd9, 0x81, 0xd9, 0x4b, 0x96, 0xbf, 0x57, 0x72, 0x69, 0x70, 0x3f, 0xcf, 0x0d, - 0x13, 0x3c, 0x91, 0x4a, 0x18, 0x41, 0x86, 0x3b, 0xe4, 0xa8, 0x32, 0x83, 0x45, 0x30, 0x5e, 0xbd, - 0x65, 0x0a, 0x8b, 0x43, 0x23, 0x5a, 0xc3, 0xd5, 0xeb, 0x27, 0xe6, 0x95, 0x41, 0x8a, 0x1f, 0x15, - 0x6a, 0x43, 0x66, 0x30, 0x90, 0x65, 0xb5, 0x63, 0xdc, 0xf7, 0x42, 0x2f, 0x1e, 0x52, 0x57, 0x11, - 0x02, 0x17, 0xcc, 0xe0, 0xde, 0xef, 0x85, 0x5e, 0x3c, 0xa6, 0xf6, 0xbb, 0x9e, 0xdd, 0xd8, 0x85, - 0x7e, 0xdf, 0xfe, 0x75, 0x55, 0xc4, 0x61, 0xd2, 0xb8, 0x6a, 0x29, 0xb8, 0xc6, 0x46, 0xee, 0xb5, - 0xe4, 0x0b, 0x98, 0x64, 0x45, 0xc1, 0x6a, 0xce, 0xac, 0xac, 0x99, 0xb5, 0xdf, 0x0b, 0xfb, 0xf1, - 0x28, 0xbd, 0x4d, 0x1a, 0xde, 0x84, 0xa2, 0x16, 0x95, 0xca, 0x71, 0x59, 0x20, 0x37, 0x6c, 0xcb, - 0x50, 0xd1, 0xae, 0x2a, 0x7a, 0x82, 0xb0, 0x1b, 0x7c, 0x2e, 0x65, 0xc9, 0x50, 0xaf, 0xc5, 0x99, - 0x5c, 0x51, 0x09, 0x77, 0x7f, 0x68, 0x1d, 0xfd, 0x02, 0xa6, 0x47, 0x8e, 0x15, 0x96, 0x98, 0x1b, - 0xa1, 0xac, 0xcd, 0x28, 0xbd, 0x39, 0x81, 0x7a, 0x1c, 0xa1, 0xbf, 0x44, 0xe9, 0x97, 0x07, 0xd3, - 0xee, 0x3a, 0xb2, 0x85, 0x61, 0xb3, 0x92, 0xdc, 0xb7, 0x0c, 0xcf, 0x85, 0x0a, 0x1e, 0xfe, 0x37, - 0xec, 0x52, 0x3c, 0xc3, 0xa5, 0x7b, 0x16, 0x72, 0xdd, 0x12, 0xfe, 0x3c, 0x80, 0x20, 0x38, 0xd5, - 0x3a, 0x38, 0x6c, 0x06, 0xf6, 0x6a, 0x1e, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x45, 0xdb, 0x5d, - 0x9f, 0x68, 0x02, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/DeleteItemAction.pb.go b/pkg/plugin/generated/DeleteItemAction.pb.go index 1d0f64680f..5302d651ad 100644 --- a/pkg/plugin/generated/DeleteItemAction.pb.go +++ b/pkg/plugin/generated/DeleteItemAction.pb.go @@ -1,6 +1,71 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: DeleteItemAction.proto +/* +Package generated is a generated protocol buffer package. + +It is generated from these files: + DeleteItemAction.proto + ItemSnapshotter.proto + ObjectStore.proto + PluginLister.proto + RestoreItemAction.proto + Shared.proto + VolumeSnapshotter.proto + +It has these top-level messages: + DeleteItemActionExecuteRequest + DeleteItemActionAppliesToRequest + DeleteItemActionAppliesToResponse + ItemSnapshotterAppliesToRequest + ItemSnapshotterAppliesToResponse + AlsoHandlesRequest + AlsoHandlesResponse + SnapshotItemRequest + SnapshotItemResponse + ProgressRequest + ProgressResponse + DeleteItemSnapshotRequest + CreateItemFromSnapshotRequest + CreateItemFromSnapshotResponse + ItemSnapshotterInitRequest + PutObjectRequest + ObjectExistsRequest + ObjectExistsResponse + GetObjectRequest + Bytes + ListCommonPrefixesRequest + ListCommonPrefixesResponse + ListObjectsRequest + ListObjectsResponse + DeleteObjectRequest + CreateSignedURLRequest + CreateSignedURLResponse + ObjectStoreInitRequest + PluginIdentifier + ListPluginsResponse + RestoreItemActionExecuteRequest + RestoreItemActionExecuteResponse + RestoreItemActionAppliesToRequest + RestoreItemActionAppliesToResponse + Empty + Stack + StackFrame + ResourceIdentifier + ResourceSelector + CreateVolumeRequest + CreateVolumeResponse + GetVolumeInfoRequest + GetVolumeInfoResponse + CreateSnapshotRequest + CreateSnapshotResponse + DeleteSnapshotRequest + GetVolumeIDRequest + GetVolumeIDResponse + SetVolumeIDRequest + SetVolumeIDResponse + VolumeSnapshotterInitRequest +*/ package generated import proto "github.com/golang/protobuf/proto" @@ -17,6 +82,12 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + type DeleteItemActionExecuteRequest struct { Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` @@ -26,7 +97,7 @@ type DeleteItemActionExecuteRequest struct { func (m *DeleteItemActionExecuteRequest) Reset() { *m = DeleteItemActionExecuteRequest{} } func (m *DeleteItemActionExecuteRequest) String() string { return proto.CompactTextString(m) } func (*DeleteItemActionExecuteRequest) ProtoMessage() {} -func (*DeleteItemActionExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } +func (*DeleteItemActionExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (m *DeleteItemActionExecuteRequest) GetPlugin() string { if m != nil { @@ -57,7 +128,7 @@ func (m *DeleteItemActionAppliesToRequest) Reset() { *m = DeleteItemActi func (m *DeleteItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } func (*DeleteItemActionAppliesToRequest) ProtoMessage() {} func (*DeleteItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor1, []int{1} + return fileDescriptor0, []int{1} } func (m *DeleteItemActionAppliesToRequest) GetPlugin() string { @@ -75,7 +146,7 @@ func (m *DeleteItemActionAppliesToResponse) Reset() { *m = DeleteItemAct func (m *DeleteItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } func (*DeleteItemActionAppliesToResponse) ProtoMessage() {} func (*DeleteItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor1, []int{2} + return fileDescriptor0, []int{2} } func (m *DeleteItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { @@ -196,9 +267,9 @@ var _DeleteItemAction_serviceDesc = grpc.ServiceDesc{ Metadata: "DeleteItemAction.proto", } -func init() { proto.RegisterFile("DeleteItemAction.proto", fileDescriptor1) } +func init() { proto.RegisterFile("DeleteItemAction.proto", fileDescriptor0) } -var fileDescriptor1 = []byte{ +var fileDescriptor0 = []byte{ // 253 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x41, 0x4b, 0xc3, 0x40, 0x14, 0x84, 0x89, 0x4a, 0x25, 0xcf, 0x1e, 0xc2, 0x1e, 0x4a, 0x88, 0x20, 0x31, 0xa7, 0x8a, 0x92, diff --git a/pkg/plugin/generated/ItemSnapshotter.pb.go b/pkg/plugin/generated/ItemSnapshotter.pb.go index dfcf73c87b..79c99094ce 100644 --- a/pkg/plugin/generated/ItemSnapshotter.pb.go +++ b/pkg/plugin/generated/ItemSnapshotter.pb.go @@ -25,7 +25,7 @@ func (m *ItemSnapshotterAppliesToRequest) Reset() { *m = ItemSnapshotter func (m *ItemSnapshotterAppliesToRequest) String() string { return proto.CompactTextString(m) } func (*ItemSnapshotterAppliesToRequest) ProtoMessage() {} func (*ItemSnapshotterAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor2, []int{0} + return fileDescriptor1, []int{0} } func (m *ItemSnapshotterAppliesToRequest) GetPlugin() string { @@ -43,7 +43,7 @@ func (m *ItemSnapshotterAppliesToResponse) Reset() { *m = ItemSnapshotte func (m *ItemSnapshotterAppliesToResponse) String() string { return proto.CompactTextString(m) } func (*ItemSnapshotterAppliesToResponse) ProtoMessage() {} func (*ItemSnapshotterAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor2, []int{1} + return fileDescriptor1, []int{1} } func (m *ItemSnapshotterAppliesToResponse) GetResourceSelector() *ResourceSelector { @@ -62,7 +62,7 @@ type AlsoHandlesRequest struct { func (m *AlsoHandlesRequest) Reset() { *m = AlsoHandlesRequest{} } func (m *AlsoHandlesRequest) String() string { return proto.CompactTextString(m) } func (*AlsoHandlesRequest) ProtoMessage() {} -func (*AlsoHandlesRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{2} } +func (*AlsoHandlesRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } func (m *AlsoHandlesRequest) GetPlugin() string { if m != nil { @@ -92,7 +92,7 @@ type AlsoHandlesResponse struct { func (m *AlsoHandlesResponse) Reset() { *m = AlsoHandlesResponse{} } func (m *AlsoHandlesResponse) String() string { return proto.CompactTextString(m) } func (*AlsoHandlesResponse) ProtoMessage() {} -func (*AlsoHandlesResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} } +func (*AlsoHandlesResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} } func (m *AlsoHandlesResponse) GetHandledItems() []*ResourceIdentifier { if m != nil { @@ -111,7 +111,7 @@ type SnapshotItemRequest struct { func (m *SnapshotItemRequest) Reset() { *m = SnapshotItemRequest{} } func (m *SnapshotItemRequest) String() string { return proto.CompactTextString(m) } func (*SnapshotItemRequest) ProtoMessage() {} -func (*SnapshotItemRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} } +func (*SnapshotItemRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} } func (m *SnapshotItemRequest) GetPlugin() string { if m != nil { @@ -152,7 +152,7 @@ type SnapshotItemResponse struct { func (m *SnapshotItemResponse) Reset() { *m = SnapshotItemResponse{} } func (m *SnapshotItemResponse) String() string { return proto.CompactTextString(m) } func (*SnapshotItemResponse) ProtoMessage() {} -func (*SnapshotItemResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} } +func (*SnapshotItemResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } func (m *SnapshotItemResponse) GetItem() []byte { if m != nil { @@ -199,7 +199,7 @@ type ProgressRequest struct { func (m *ProgressRequest) Reset() { *m = ProgressRequest{} } func (m *ProgressRequest) String() string { return proto.CompactTextString(m) } func (*ProgressRequest) ProtoMessage() {} -func (*ProgressRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} } +func (*ProgressRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } func (m *ProgressRequest) GetPlugin() string { if m != nil { @@ -243,7 +243,7 @@ type ProgressResponse struct { func (m *ProgressResponse) Reset() { *m = ProgressResponse{} } func (m *ProgressResponse) String() string { return proto.CompactTextString(m) } func (*ProgressResponse) ProtoMessage() {} -func (*ProgressResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{7} } +func (*ProgressResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } func (m *ProgressResponse) GetPhase() string { if m != nil { @@ -312,7 +312,7 @@ type DeleteItemSnapshotRequest struct { func (m *DeleteItemSnapshotRequest) Reset() { *m = DeleteItemSnapshotRequest{} } func (m *DeleteItemSnapshotRequest) String() string { return proto.CompactTextString(m) } func (*DeleteItemSnapshotRequest) ProtoMessage() {} -func (*DeleteItemSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{8} } +func (*DeleteItemSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } func (m *DeleteItemSnapshotRequest) GetPlugin() string { if m != nil { @@ -362,7 +362,7 @@ type CreateItemFromSnapshotRequest struct { func (m *CreateItemFromSnapshotRequest) Reset() { *m = CreateItemFromSnapshotRequest{} } func (m *CreateItemFromSnapshotRequest) String() string { return proto.CompactTextString(m) } func (*CreateItemFromSnapshotRequest) ProtoMessage() {} -func (*CreateItemFromSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{9} } +func (*CreateItemFromSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } func (m *CreateItemFromSnapshotRequest) GetPlugin() string { if m != nil { @@ -423,7 +423,7 @@ func (m *CreateItemFromSnapshotResponse) Reset() { *m = CreateItemFromSn func (m *CreateItemFromSnapshotResponse) String() string { return proto.CompactTextString(m) } func (*CreateItemFromSnapshotResponse) ProtoMessage() {} func (*CreateItemFromSnapshotResponse) Descriptor() ([]byte, []int) { - return fileDescriptor2, []int{10} + return fileDescriptor1, []int{10} } func (m *CreateItemFromSnapshotResponse) GetItem() []byte { @@ -455,7 +455,7 @@ type ItemSnapshotterInitRequest struct { func (m *ItemSnapshotterInitRequest) Reset() { *m = ItemSnapshotterInitRequest{} } func (m *ItemSnapshotterInitRequest) String() string { return proto.CompactTextString(m) } func (*ItemSnapshotterInitRequest) ProtoMessage() {} -func (*ItemSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{11} } +func (*ItemSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{11} } func (m *ItemSnapshotterInitRequest) GetPlugin() string { if m != nil { @@ -756,9 +756,9 @@ var _ItemSnapshotter_serviceDesc = grpc.ServiceDesc{ Metadata: "ItemSnapshotter.proto", } -func init() { proto.RegisterFile("ItemSnapshotter.proto", fileDescriptor2) } +func init() { proto.RegisterFile("ItemSnapshotter.proto", fileDescriptor1) } -var fileDescriptor2 = []byte{ +var fileDescriptor1 = []byte{ // 887 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x5f, 0x8f, 0xdb, 0x44, 0x10, 0x97, 0xe3, 0x5c, 0xee, 0x6e, 0x12, 0x7a, 0xd1, 0xf6, 0x5a, 0x19, 0x57, 0xbd, 0x46, 0x16, diff --git a/pkg/plugin/generated/ObjectStore.pb.go b/pkg/plugin/generated/ObjectStore.pb.go index b22cf13799..121d70d456 100644 --- a/pkg/plugin/generated/ObjectStore.pb.go +++ b/pkg/plugin/generated/ObjectStore.pb.go @@ -27,7 +27,7 @@ type PutObjectRequest struct { func (m *PutObjectRequest) Reset() { *m = PutObjectRequest{} } func (m *PutObjectRequest) String() string { return proto.CompactTextString(m) } func (*PutObjectRequest) ProtoMessage() {} -func (*PutObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} } +func (*PutObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } func (m *PutObjectRequest) GetPlugin() string { if m != nil { @@ -66,7 +66,7 @@ type ObjectExistsRequest struct { func (m *ObjectExistsRequest) Reset() { *m = ObjectExistsRequest{} } func (m *ObjectExistsRequest) String() string { return proto.CompactTextString(m) } func (*ObjectExistsRequest) ProtoMessage() {} -func (*ObjectExistsRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{1} } +func (*ObjectExistsRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{1} } func (m *ObjectExistsRequest) GetPlugin() string { if m != nil { @@ -96,7 +96,7 @@ type ObjectExistsResponse struct { func (m *ObjectExistsResponse) Reset() { *m = ObjectExistsResponse{} } func (m *ObjectExistsResponse) String() string { return proto.CompactTextString(m) } func (*ObjectExistsResponse) ProtoMessage() {} -func (*ObjectExistsResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{2} } +func (*ObjectExistsResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{2} } func (m *ObjectExistsResponse) GetExists() bool { if m != nil { @@ -114,7 +114,7 @@ type GetObjectRequest struct { func (m *GetObjectRequest) Reset() { *m = GetObjectRequest{} } func (m *GetObjectRequest) String() string { return proto.CompactTextString(m) } func (*GetObjectRequest) ProtoMessage() {} -func (*GetObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{3} } +func (*GetObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} } func (m *GetObjectRequest) GetPlugin() string { if m != nil { @@ -144,7 +144,7 @@ type Bytes struct { func (m *Bytes) Reset() { *m = Bytes{} } func (m *Bytes) String() string { return proto.CompactTextString(m) } func (*Bytes) ProtoMessage() {} -func (*Bytes) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{4} } +func (*Bytes) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} } func (m *Bytes) GetData() []byte { if m != nil { @@ -163,7 +163,7 @@ type ListCommonPrefixesRequest struct { func (m *ListCommonPrefixesRequest) Reset() { *m = ListCommonPrefixesRequest{} } func (m *ListCommonPrefixesRequest) String() string { return proto.CompactTextString(m) } func (*ListCommonPrefixesRequest) ProtoMessage() {} -func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{5} } +func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} } func (m *ListCommonPrefixesRequest) GetPlugin() string { if m != nil { @@ -200,7 +200,7 @@ type ListCommonPrefixesResponse struct { func (m *ListCommonPrefixesResponse) Reset() { *m = ListCommonPrefixesResponse{} } func (m *ListCommonPrefixesResponse) String() string { return proto.CompactTextString(m) } func (*ListCommonPrefixesResponse) ProtoMessage() {} -func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{6} } +func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} } func (m *ListCommonPrefixesResponse) GetPrefixes() []string { if m != nil { @@ -218,7 +218,7 @@ type ListObjectsRequest struct { func (m *ListObjectsRequest) Reset() { *m = ListObjectsRequest{} } func (m *ListObjectsRequest) String() string { return proto.CompactTextString(m) } func (*ListObjectsRequest) ProtoMessage() {} -func (*ListObjectsRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{7} } +func (*ListObjectsRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{7} } func (m *ListObjectsRequest) GetPlugin() string { if m != nil { @@ -248,7 +248,7 @@ type ListObjectsResponse struct { func (m *ListObjectsResponse) Reset() { *m = ListObjectsResponse{} } func (m *ListObjectsResponse) String() string { return proto.CompactTextString(m) } func (*ListObjectsResponse) ProtoMessage() {} -func (*ListObjectsResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{8} } +func (*ListObjectsResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{8} } func (m *ListObjectsResponse) GetKeys() []string { if m != nil { @@ -266,7 +266,7 @@ type DeleteObjectRequest struct { func (m *DeleteObjectRequest) Reset() { *m = DeleteObjectRequest{} } func (m *DeleteObjectRequest) String() string { return proto.CompactTextString(m) } func (*DeleteObjectRequest) ProtoMessage() {} -func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{9} } +func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{9} } func (m *DeleteObjectRequest) GetPlugin() string { if m != nil { @@ -299,7 +299,7 @@ type CreateSignedURLRequest struct { func (m *CreateSignedURLRequest) Reset() { *m = CreateSignedURLRequest{} } func (m *CreateSignedURLRequest) String() string { return proto.CompactTextString(m) } func (*CreateSignedURLRequest) ProtoMessage() {} -func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{10} } +func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{10} } func (m *CreateSignedURLRequest) GetPlugin() string { if m != nil { @@ -336,7 +336,7 @@ type CreateSignedURLResponse struct { func (m *CreateSignedURLResponse) Reset() { *m = CreateSignedURLResponse{} } func (m *CreateSignedURLResponse) String() string { return proto.CompactTextString(m) } func (*CreateSignedURLResponse) ProtoMessage() {} -func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{11} } +func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{11} } func (m *CreateSignedURLResponse) GetUrl() string { if m != nil { @@ -353,7 +353,7 @@ type ObjectStoreInitRequest struct { func (m *ObjectStoreInitRequest) Reset() { *m = ObjectStoreInitRequest{} } func (m *ObjectStoreInitRequest) String() string { return proto.CompactTextString(m) } func (*ObjectStoreInitRequest) ProtoMessage() {} -func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{12} } +func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{12} } func (m *ObjectStoreInitRequest) GetPlugin() string { if m != nil { @@ -750,9 +750,9 @@ var _ObjectStore_serviceDesc = grpc.ServiceDesc{ Metadata: "ObjectStore.proto", } -func init() { proto.RegisterFile("ObjectStore.proto", fileDescriptor3) } +func init() { proto.RegisterFile("ObjectStore.proto", fileDescriptor2) } -var fileDescriptor3 = []byte{ +var fileDescriptor2 = []byte{ // 577 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x40, 0x10, 0xd6, 0xc6, 0x69, 0x54, 0x4f, 0x22, 0x61, 0xb6, 0x55, 0x30, 0x2e, 0x94, 0xb0, 0x02, 0x29, diff --git a/pkg/plugin/generated/PluginLister.pb.go b/pkg/plugin/generated/PluginLister.pb.go index a2d7957d9c..0f4fe7c878 100644 --- a/pkg/plugin/generated/PluginLister.pb.go +++ b/pkg/plugin/generated/PluginLister.pb.go @@ -26,7 +26,7 @@ type PluginIdentifier struct { func (m *PluginIdentifier) Reset() { *m = PluginIdentifier{} } func (m *PluginIdentifier) String() string { return proto.CompactTextString(m) } func (*PluginIdentifier) ProtoMessage() {} -func (*PluginIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{0} } +func (*PluginIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} } func (m *PluginIdentifier) GetCommand() string { if m != nil { @@ -56,7 +56,7 @@ type ListPluginsResponse struct { func (m *ListPluginsResponse) Reset() { *m = ListPluginsResponse{} } func (m *ListPluginsResponse) String() string { return proto.CompactTextString(m) } func (*ListPluginsResponse) ProtoMessage() {} -func (*ListPluginsResponse) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{1} } +func (*ListPluginsResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{1} } func (m *ListPluginsResponse) GetPlugins() []*PluginIdentifier { if m != nil { @@ -142,9 +142,9 @@ var _PluginLister_serviceDesc = grpc.ServiceDesc{ Metadata: "PluginLister.proto", } -func init() { proto.RegisterFile("PluginLister.proto", fileDescriptor4) } +func init() { proto.RegisterFile("PluginLister.proto", fileDescriptor3) } -var fileDescriptor4 = []byte{ +var fileDescriptor3 = []byte{ // 201 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x0a, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0xf3, 0xc9, 0x2c, 0x2e, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, diff --git a/pkg/plugin/generated/RestoreItemAction.pb.go b/pkg/plugin/generated/RestoreItemAction.pb.go index da06ffdac7..63a92b4c2e 100644 --- a/pkg/plugin/generated/RestoreItemAction.pb.go +++ b/pkg/plugin/generated/RestoreItemAction.pb.go @@ -28,7 +28,7 @@ func (m *RestoreItemActionExecuteRequest) Reset() { *m = RestoreItemActi func (m *RestoreItemActionExecuteRequest) String() string { return proto.CompactTextString(m) } func (*RestoreItemActionExecuteRequest) ProtoMessage() {} func (*RestoreItemActionExecuteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor5, []int{0} + return fileDescriptor4, []int{0} } func (m *RestoreItemActionExecuteRequest) GetPlugin() string { @@ -69,7 +69,7 @@ func (m *RestoreItemActionExecuteResponse) Reset() { *m = RestoreItemAct func (m *RestoreItemActionExecuteResponse) String() string { return proto.CompactTextString(m) } func (*RestoreItemActionExecuteResponse) ProtoMessage() {} func (*RestoreItemActionExecuteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor5, []int{1} + return fileDescriptor4, []int{1} } func (m *RestoreItemActionExecuteResponse) GetItem() []byte { @@ -101,7 +101,7 @@ func (m *RestoreItemActionAppliesToRequest) Reset() { *m = RestoreItemAc func (m *RestoreItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } func (*RestoreItemActionAppliesToRequest) ProtoMessage() {} func (*RestoreItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor5, []int{2} + return fileDescriptor4, []int{2} } func (m *RestoreItemActionAppliesToRequest) GetPlugin() string { @@ -119,7 +119,7 @@ func (m *RestoreItemActionAppliesToResponse) Reset() { *m = RestoreItemA func (m *RestoreItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } func (*RestoreItemActionAppliesToResponse) ProtoMessage() {} func (*RestoreItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor5, []int{3} + return fileDescriptor4, []int{3} } func (m *RestoreItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { @@ -241,9 +241,9 @@ var _RestoreItemAction_serviceDesc = grpc.ServiceDesc{ Metadata: "RestoreItemAction.proto", } -func init() { proto.RegisterFile("RestoreItemAction.proto", fileDescriptor5) } +func init() { proto.RegisterFile("RestoreItemAction.proto", fileDescriptor4) } -var fileDescriptor5 = []byte{ +var fileDescriptor4 = []byte{ // 332 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xdd, 0x4e, 0xc2, 0x30, 0x14, 0x4e, 0x81, 0x80, 0x1c, 0x88, 0x3f, 0xbd, 0xd0, 0x06, 0x63, 0x9c, 0xbb, 0x30, 0xc4, 0x1f, diff --git a/pkg/plugin/generated/Shared.pb.go b/pkg/plugin/generated/Shared.pb.go index 8f2716da51..8f7fc6c710 100644 --- a/pkg/plugin/generated/Shared.pb.go +++ b/pkg/plugin/generated/Shared.pb.go @@ -18,7 +18,7 @@ type Empty struct { func (m *Empty) Reset() { *m = Empty{} } func (m *Empty) String() string { return proto.CompactTextString(m) } func (*Empty) ProtoMessage() {} -func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{0} } +func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} } type Stack struct { Frames []*StackFrame `protobuf:"bytes,1,rep,name=frames" json:"frames,omitempty"` @@ -27,7 +27,7 @@ type Stack struct { func (m *Stack) Reset() { *m = Stack{} } func (m *Stack) String() string { return proto.CompactTextString(m) } func (*Stack) ProtoMessage() {} -func (*Stack) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{1} } +func (*Stack) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{1} } func (m *Stack) GetFrames() []*StackFrame { if m != nil { @@ -45,7 +45,7 @@ type StackFrame struct { func (m *StackFrame) Reset() { *m = StackFrame{} } func (m *StackFrame) String() string { return proto.CompactTextString(m) } func (*StackFrame) ProtoMessage() {} -func (*StackFrame) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{2} } +func (*StackFrame) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{2} } func (m *StackFrame) GetFile() string { if m != nil { @@ -78,7 +78,7 @@ type ResourceIdentifier struct { func (m *ResourceIdentifier) Reset() { *m = ResourceIdentifier{} } func (m *ResourceIdentifier) String() string { return proto.CompactTextString(m) } func (*ResourceIdentifier) ProtoMessage() {} -func (*ResourceIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{3} } +func (*ResourceIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{3} } func (m *ResourceIdentifier) GetGroup() string { if m != nil { @@ -119,7 +119,7 @@ type ResourceSelector struct { func (m *ResourceSelector) Reset() { *m = ResourceSelector{} } func (m *ResourceSelector) String() string { return proto.CompactTextString(m) } func (*ResourceSelector) ProtoMessage() {} -func (*ResourceSelector) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{4} } +func (*ResourceSelector) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{4} } func (m *ResourceSelector) GetIncludedNamespaces() []string { if m != nil { @@ -164,27 +164,30 @@ func init() { proto.RegisterType((*ResourceSelector)(nil), "generated.ResourceSelector") } -func init() { proto.RegisterFile("Shared.proto", fileDescriptor6) } - -var fileDescriptor6 = []byte{ - // 294 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0xc1, 0x4e, 0xb5, 0x30, - 0x10, 0x85, 0xc3, 0x05, 0xee, 0xff, 0x33, 0xba, 0xd0, 0x46, 0x93, 0xc6, 0xb8, 0x20, 0xac, 0x58, - 0x28, 0x0b, 0x4d, 0x7c, 0x03, 0x4d, 0xdc, 0x18, 0x53, 0x9e, 0x00, 0xcb, 0x70, 0x6d, 0xe4, 0xb6, - 0xa4, 0x94, 0xe4, 0xfa, 0xca, 0x3e, 0x85, 0x69, 0x4b, 0x61, 0x81, 0xbb, 0x39, 0x73, 0x3e, 0xce, - 0x0c, 0x93, 0xc2, 0x79, 0xfd, 0xd9, 0x68, 0x6c, 0xab, 0x41, 0x2b, 0xa3, 0x48, 0x76, 0x40, 0x89, - 0xba, 0x31, 0xd8, 0x16, 0xff, 0x20, 0x7d, 0x3e, 0x0e, 0xe6, 0xbb, 0x78, 0x82, 0xb4, 0x36, 0x0d, - 0xff, 0x22, 0xf7, 0xb0, 0xef, 0x74, 0x73, 0xc4, 0x91, 0x46, 0x79, 0x5c, 0x9e, 0x3d, 0x5c, 0x57, - 0x0b, 0x5d, 0x39, 0xe2, 0xc5, 0xba, 0x6c, 0x86, 0x8a, 0x77, 0x80, 0xb5, 0x4b, 0x08, 0x24, 0x9d, - 0xe8, 0x91, 0x46, 0x79, 0x54, 0x66, 0xcc, 0xd5, 0xb6, 0xd7, 0x0b, 0x89, 0x74, 0x97, 0x47, 0x65, - 0xca, 0x5c, 0x4d, 0x6e, 0xe0, 0x7f, 0x37, 0x49, 0x6e, 0x84, 0x92, 0x34, 0x76, 0xec, 0xa2, 0x8b, - 0x13, 0x10, 0x86, 0xa3, 0x9a, 0x34, 0xc7, 0xd7, 0x16, 0xa5, 0x11, 0x9d, 0x40, 0x4d, 0xae, 0x20, - 0x3d, 0x68, 0x35, 0x0d, 0x73, 0xb4, 0x17, 0x36, 0x47, 0xcf, 0xac, 0xcb, 0xcf, 0xd8, 0xa2, 0xc9, - 0x2d, 0x64, 0xd2, 0xae, 0x38, 0x34, 0x1c, 0xe7, 0x21, 0x6b, 0xc3, 0x6e, 0x65, 0x05, 0x4d, 0xfc, - 0xa6, 0xb6, 0x2e, 0x7e, 0x22, 0xb8, 0x08, 0xa3, 0x6b, 0xec, 0x91, 0x1b, 0xa5, 0x49, 0x05, 0x44, - 0x48, 0xde, 0x4f, 0x2d, 0xb6, 0x6f, 0xe1, 0x6b, 0x7f, 0x9b, 0x8c, 0xfd, 0xe1, 0x58, 0x1e, 0x4f, - 0x1b, 0x7e, 0xe7, 0xf9, 0xad, 0x43, 0xee, 0xe0, 0x32, 0xa4, 0x84, 0xd9, 0x23, 0x8d, 0x1d, 0xbe, - 0x35, 0x2c, 0x1d, 0x32, 0x56, 0x3a, 0xf1, 0xf4, 0xc6, 0xb0, 0xe7, 0x19, 0xe7, 0xff, 0xa0, 0xa9, - 0x3f, 0x4f, 0xd0, 0x1f, 0x7b, 0xf7, 0x16, 0x1e, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x83, - 0xa1, 0x97, 0x1b, 0x02, 0x00, 0x00, +func init() { proto.RegisterFile("Shared.proto", fileDescriptor5) } + +var fileDescriptor5 = []byte{ + // 337 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0xcd, 0x4e, 0xeb, 0x30, + 0x10, 0x85, 0x95, 0xb6, 0xe9, 0xbd, 0x19, 0x58, 0x80, 0x05, 0x52, 0x84, 0x58, 0x54, 0x59, 0x75, + 0x41, 0x13, 0x89, 0x0a, 0x1e, 0x00, 0x09, 0x24, 0x36, 0x08, 0xa5, 0x3b, 0x76, 0xae, 0x33, 0x49, + 0xad, 0x26, 0x76, 0xe4, 0xd8, 0xa5, 0xf0, 0xc8, 0x3c, 0x05, 0xb2, 0xf3, 0xd3, 0x45, 0xd8, 0xcd, + 0x99, 0xf3, 0xe5, 0xcc, 0x64, 0x64, 0x38, 0xdf, 0xec, 0xa8, 0xc2, 0x2c, 0xae, 0x95, 0xd4, 0x92, + 0x04, 0x05, 0x0a, 0x54, 0x54, 0x63, 0x16, 0xfd, 0x03, 0xff, 0xb9, 0xaa, 0xf5, 0x57, 0xf4, 0x08, + 0xfe, 0x46, 0x53, 0xb6, 0x27, 0x2b, 0x98, 0xe7, 0x8a, 0x56, 0xd8, 0x84, 0xde, 0x62, 0xba, 0x3c, + 0xbb, 0xbf, 0x8e, 0x07, 0x3a, 0x76, 0xc4, 0x8b, 0x75, 0xd3, 0x0e, 0x8a, 0xde, 0x01, 0x4e, 0x5d, + 0x42, 0x60, 0x96, 0xf3, 0x12, 0x43, 0x6f, 0xe1, 0x2d, 0x83, 0xd4, 0xd5, 0xb6, 0x57, 0x72, 0x81, + 0xe1, 0x64, 0xe1, 0x2d, 0xfd, 0xd4, 0xd5, 0xe4, 0x06, 0xfe, 0xe7, 0x46, 0x30, 0xcd, 0xa5, 0x08, + 0xa7, 0x8e, 0x1d, 0x74, 0x74, 0x04, 0x92, 0x62, 0x23, 0x8d, 0x62, 0xf8, 0x9a, 0xa1, 0xd0, 0x3c, + 0xe7, 0xa8, 0xc8, 0x15, 0xf8, 0x85, 0x92, 0xa6, 0xee, 0xa2, 0x5b, 0x61, 0x73, 0x54, 0xc7, 0xba, + 0xfc, 0x20, 0x1d, 0x34, 0xb9, 0x85, 0x40, 0xd8, 0x15, 0x6b, 0xca, 0xb0, 0x1b, 0x72, 0x6a, 0xd8, + 0xad, 0xac, 0x08, 0x67, 0xed, 0xa6, 0xb6, 0x8e, 0x7e, 0x3c, 0xb8, 0xe8, 0x47, 0x6f, 0xb0, 0x44, + 0xa6, 0xa5, 0x22, 0x31, 0x10, 0x2e, 0x58, 0x69, 0x32, 0xcc, 0xde, 0xfa, 0xaf, 0xdb, 0xdb, 0x04, + 0xe9, 0x1f, 0x8e, 0xe5, 0xf1, 0x38, 0xe2, 0x27, 0x2d, 0x3f, 0x76, 0xc8, 0x1d, 0x5c, 0xf6, 0x29, + 0xfd, 0xec, 0x26, 0x9c, 0x3a, 0x7c, 0x6c, 0x58, 0xba, 0xcf, 0x38, 0xd1, 0xb3, 0x96, 0x1e, 0x19, + 0xf6, 0x3c, 0x4d, 0xf7, 0x1f, 0xa1, 0xdf, 0x9e, 0xa7, 0xd7, 0x4f, 0x0f, 0x1f, 0xeb, 0x82, 0xeb, + 0x9d, 0xd9, 0xc6, 0x4c, 0x56, 0xc9, 0xa1, 0xfa, 0xa4, 0x0a, 0x57, 0x9a, 0x8a, 0x6f, 0x93, 0x1c, + 0xb0, 0x44, 0x25, 0x93, 0x7a, 0x5f, 0x24, 0x75, 0x69, 0x0a, 0x2e, 0x92, 0xe1, 0x09, 0x6c, 0xe7, + 0xee, 0x09, 0xad, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x58, 0x4c, 0x5c, 0x87, 0x52, 0x02, 0x00, + 0x00, } diff --git a/pkg/plugin/generated/VolumeSnapshotter.pb.go b/pkg/plugin/generated/VolumeSnapshotter.pb.go index ddb8a2d743..b814bae9a6 100644 --- a/pkg/plugin/generated/VolumeSnapshotter.pb.go +++ b/pkg/plugin/generated/VolumeSnapshotter.pb.go @@ -28,7 +28,7 @@ type CreateVolumeRequest struct { func (m *CreateVolumeRequest) Reset() { *m = CreateVolumeRequest{} } func (m *CreateVolumeRequest) String() string { return proto.CompactTextString(m) } func (*CreateVolumeRequest) ProtoMessage() {} -func (*CreateVolumeRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{0} } +func (*CreateVolumeRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{0} } func (m *CreateVolumeRequest) GetPlugin() string { if m != nil { @@ -72,7 +72,7 @@ type CreateVolumeResponse struct { func (m *CreateVolumeResponse) Reset() { *m = CreateVolumeResponse{} } func (m *CreateVolumeResponse) String() string { return proto.CompactTextString(m) } func (*CreateVolumeResponse) ProtoMessage() {} -func (*CreateVolumeResponse) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{1} } +func (*CreateVolumeResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{1} } func (m *CreateVolumeResponse) GetVolumeID() string { if m != nil { @@ -90,7 +90,7 @@ type GetVolumeInfoRequest struct { func (m *GetVolumeInfoRequest) Reset() { *m = GetVolumeInfoRequest{} } func (m *GetVolumeInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetVolumeInfoRequest) ProtoMessage() {} -func (*GetVolumeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{2} } +func (*GetVolumeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{2} } func (m *GetVolumeInfoRequest) GetPlugin() string { if m != nil { @@ -121,7 +121,7 @@ type GetVolumeInfoResponse struct { func (m *GetVolumeInfoResponse) Reset() { *m = GetVolumeInfoResponse{} } func (m *GetVolumeInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetVolumeInfoResponse) ProtoMessage() {} -func (*GetVolumeInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{3} } +func (*GetVolumeInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{3} } func (m *GetVolumeInfoResponse) GetVolumeType() string { if m != nil { @@ -147,7 +147,7 @@ type CreateSnapshotRequest struct { func (m *CreateSnapshotRequest) Reset() { *m = CreateSnapshotRequest{} } func (m *CreateSnapshotRequest) String() string { return proto.CompactTextString(m) } func (*CreateSnapshotRequest) ProtoMessage() {} -func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{4} } +func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{4} } func (m *CreateSnapshotRequest) GetPlugin() string { if m != nil { @@ -184,7 +184,7 @@ type CreateSnapshotResponse struct { func (m *CreateSnapshotResponse) Reset() { *m = CreateSnapshotResponse{} } func (m *CreateSnapshotResponse) String() string { return proto.CompactTextString(m) } func (*CreateSnapshotResponse) ProtoMessage() {} -func (*CreateSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{5} } +func (*CreateSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{5} } func (m *CreateSnapshotResponse) GetSnapshotID() string { if m != nil { @@ -201,7 +201,7 @@ type DeleteSnapshotRequest struct { func (m *DeleteSnapshotRequest) Reset() { *m = DeleteSnapshotRequest{} } func (m *DeleteSnapshotRequest) String() string { return proto.CompactTextString(m) } func (*DeleteSnapshotRequest) ProtoMessage() {} -func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{6} } +func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{6} } func (m *DeleteSnapshotRequest) GetPlugin() string { if m != nil { @@ -225,7 +225,7 @@ type GetVolumeIDRequest struct { func (m *GetVolumeIDRequest) Reset() { *m = GetVolumeIDRequest{} } func (m *GetVolumeIDRequest) String() string { return proto.CompactTextString(m) } func (*GetVolumeIDRequest) ProtoMessage() {} -func (*GetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{7} } +func (*GetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{7} } func (m *GetVolumeIDRequest) GetPlugin() string { if m != nil { @@ -248,7 +248,7 @@ type GetVolumeIDResponse struct { func (m *GetVolumeIDResponse) Reset() { *m = GetVolumeIDResponse{} } func (m *GetVolumeIDResponse) String() string { return proto.CompactTextString(m) } func (*GetVolumeIDResponse) ProtoMessage() {} -func (*GetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{8} } +func (*GetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{8} } func (m *GetVolumeIDResponse) GetVolumeID() string { if m != nil { @@ -266,7 +266,7 @@ type SetVolumeIDRequest struct { func (m *SetVolumeIDRequest) Reset() { *m = SetVolumeIDRequest{} } func (m *SetVolumeIDRequest) String() string { return proto.CompactTextString(m) } func (*SetVolumeIDRequest) ProtoMessage() {} -func (*SetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{9} } +func (*SetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{9} } func (m *SetVolumeIDRequest) GetPlugin() string { if m != nil { @@ -296,7 +296,7 @@ type SetVolumeIDResponse struct { func (m *SetVolumeIDResponse) Reset() { *m = SetVolumeIDResponse{} } func (m *SetVolumeIDResponse) String() string { return proto.CompactTextString(m) } func (*SetVolumeIDResponse) ProtoMessage() {} -func (*SetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{10} } +func (*SetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{10} } func (m *SetVolumeIDResponse) GetPersistentVolume() []byte { if m != nil { @@ -313,7 +313,7 @@ type VolumeSnapshotterInitRequest struct { func (m *VolumeSnapshotterInitRequest) Reset() { *m = VolumeSnapshotterInitRequest{} } func (m *VolumeSnapshotterInitRequest) String() string { return proto.CompactTextString(m) } func (*VolumeSnapshotterInitRequest) ProtoMessage() {} -func (*VolumeSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor7, []int{11} } +func (*VolumeSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{11} } func (m *VolumeSnapshotterInitRequest) GetPlugin() string { if m != nil { @@ -614,9 +614,9 @@ var _VolumeSnapshotter_serviceDesc = grpc.ServiceDesc{ Metadata: "VolumeSnapshotter.proto", } -func init() { proto.RegisterFile("VolumeSnapshotter.proto", fileDescriptor7) } +func init() { proto.RegisterFile("VolumeSnapshotter.proto", fileDescriptor6) } -var fileDescriptor7 = []byte{ +var fileDescriptor6 = []byte{ // 566 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xc1, 0x6e, 0xd3, 0x40, 0x10, 0xd5, 0xda, 0x6e, 0x44, 0x26, 0xa5, 0x0a, 0x9b, 0xa4, 0x58, 0x16, 0x04, 0xe3, 0x0b, 0x51, diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index 588f9b74d3..6783e1c51b 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -4,7 +4,9 @@ package mocks import ( mock "github.com/stretchr/testify/mock" + velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) @@ -19,15 +21,15 @@ func (_m *Manager) CleanupClients() { } // GetBackupItemAction provides a mock function with given fields: name -func (_m *Manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) { +func (_m *Manager) GetBackupItemAction(name string) (biav1.BackupItemAction, error) { ret := _m.Called(name) - var r0 velero.BackupItemAction - if rf, ok := ret.Get(0).(func(string) velero.BackupItemAction); ok { + var r0 biav1.BackupItemAction + if rf, ok := ret.Get(0).(func(string) biav1.BackupItemAction); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.BackupItemAction) + r0 = ret.Get(0).(biav1.BackupItemAction) } } @@ -42,15 +44,15 @@ func (_m *Manager) GetBackupItemAction(name string) (velero.BackupItemAction, er } // GetBackupItemActions provides a mock function with given fields: -func (_m *Manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { +func (_m *Manager) GetBackupItemActions() ([]biav1.BackupItemAction, error) { ret := _m.Called() - var r0 []velero.BackupItemAction - if rf, ok := ret.Get(0).(func() []velero.BackupItemAction); ok { + var r0 []biav1.BackupItemAction + if rf, ok := ret.Get(0).(func() []biav1.BackupItemAction); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]velero.BackupItemAction) + r0 = ret.Get(0).([]biav1.BackupItemAction) } } diff --git a/pkg/plugin/proto/Shared.proto b/pkg/plugin/proto/Shared.proto index a58f411249..c1c298e3a4 100644 --- a/pkg/plugin/proto/Shared.proto +++ b/pkg/plugin/proto/Shared.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; message Empty {} diff --git a/pkg/plugin/proto/BackupItemAction.proto b/pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto similarity index 68% rename from pkg/plugin/proto/BackupItemAction.proto rename to pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto index c2422c90bd..2fef696563 100644 --- a/pkg/plugin/proto/BackupItemAction.proto +++ b/pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto @@ -1,5 +1,6 @@ syntax = "proto3"; -package generated; +package v1; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1"; import "Shared.proto"; @@ -11,7 +12,7 @@ message ExecuteRequest { message ExecuteResponse { bytes item = 1; - repeated ResourceIdentifier additionalItems = 2; + repeated generated.ResourceIdentifier additionalItems = 2; } service BackupItemAction { @@ -24,5 +25,5 @@ message BackupItemActionAppliesToRequest { } message BackupItemActionAppliesToResponse { - ResourceSelector ResourceSelector = 1; + generated.ResourceSelector ResourceSelector = 1; } \ No newline at end of file diff --git a/pkg/plugin/velero/backup_item_action.go b/pkg/plugin/velero/backupitemaction/v1/backup_item_action.go similarity index 81% rename from pkg/plugin/velero/backup_item_action.go rename to pkg/plugin/velero/backupitemaction/v1/backup_item_action.go index 70d28555f7..35566d98a4 100644 --- a/pkg/plugin/velero/backup_item_action.go +++ b/pkg/plugin/velero/backupitemaction/v1/backup_item_action.go @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // BackupItemAction is an actor that performs an operation on an individual item being backed up. @@ -28,18 +28,11 @@ type BackupItemAction interface { // AppliesTo returns information about which resources this action should be invoked for. // A BackupItemAction's Execute function will only be invoked on items that match the returned // selector. A zero-valued ResourceSelector matches all resources. - AppliesTo() (ResourceSelector, error) + AppliesTo() (velero.ResourceSelector, error) // Execute allows the ItemAction to perform arbitrary logic with the item being backed up, // including mutating the item itself prior to backup. The item (unmodified or modified) // should be returned, along with an optional slice of ResourceIdentifiers specifying // additional related items that should be backed up. - Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []ResourceIdentifier, error) -} - -// ResourceIdentifier describes a single item by its group, resource, namespace, and name. -type ResourceIdentifier struct { - schema.GroupResource - Namespace string - Name string + Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) } diff --git a/pkg/plugin/velero/item_snapshotter/v1/item_snapshotter.go b/pkg/plugin/velero/item_snapshotter/v1/item_snapshotter.go index 7d9ddcd00c..e43760b8e8 100644 --- a/pkg/plugin/velero/item_snapshotter/v1/item_snapshotter.go +++ b/pkg/plugin/velero/item_snapshotter/v1/item_snapshotter.go @@ -21,11 +21,10 @@ import ( "fmt" "time" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "k8s.io/apimachinery/pkg/runtime" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) type AlsoHandlesInput struct { diff --git a/pkg/plugin/velero/item_snapshotter/v1/mocks/item_snapshotter.go b/pkg/plugin/velero/item_snapshotter/v1/mocks/item_snapshotter.go index 940b854261..5d2b76c1df 100644 --- a/pkg/plugin/velero/item_snapshotter/v1/mocks/item_snapshotter.go +++ b/pkg/plugin/velero/item_snapshotter/v1/mocks/item_snapshotter.go @@ -5,9 +5,9 @@ package mocks import ( context "context" mock "github.com/stretchr/testify/mock" - v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) // ItemSnapshotter is an autogenerated mock type for the ItemSnapshotter type diff --git a/pkg/plugin/velero/shared.go b/pkg/plugin/velero/shared.go index 76c7464ad0..a0d3d884c2 100644 --- a/pkg/plugin/velero/shared.go +++ b/pkg/plugin/velero/shared.go @@ -20,6 +20,8 @@ limitations under the License. // plugins of any type can be implemented. package velero +import "k8s.io/apimachinery/pkg/runtime/schema" + // ResourceSelector is a collection of included/excluded namespaces, // included/excluded resources, and a label-selector that can be used // to match a set of items from a cluster. @@ -54,3 +56,10 @@ type Applicable interface { // AppliesTo returns information about which resources this Responder should be invoked for. AppliesTo() (ResourceSelector, error) } + +// ResourceIdentifier describes a single item by its group, resource, namespace, and name. +type ResourceIdentifier struct { + schema.GroupResource + Namespace string + Name string +} From 4a043bdab9043b5586f01c7ff6b716e38b22806b Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Fri, 27 May 2022 17:24:01 -0400 Subject: [PATCH 265/366] Add changelog Signed-off-by: Hoang, Phuong --- changelogs/unreleased/4943-phuongatemc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/4943-phuongatemc diff --git a/changelogs/unreleased/4943-phuongatemc b/changelogs/unreleased/4943-phuongatemc new file mode 100644 index 0000000000..6443fc603f --- /dev/null +++ b/changelogs/unreleased/4943-phuongatemc @@ -0,0 +1 @@ +Refactor BackupItemAction proto and related code to backupitemaction/v1 package. This is part of implementation of the plugin version design https://github.com/vmware-tanzu/velero/blob/main/design/plugin-versioning.md From 2c037b7491b2aa4455745745c1d79e26749f0f86 Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Fri, 27 May 2022 17:28:16 -0400 Subject: [PATCH 266/366] Add missing file Signed-off-by: Hoang, Phuong --- .../v1/BackupItemAction.pb.go | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go diff --git a/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go b/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go new file mode 100644 index 0000000000..0a3e4aee51 --- /dev/null +++ b/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go @@ -0,0 +1,269 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: backupitemaction/v1/BackupItemAction.proto + +/* +Package v1 is a generated protocol buffer package. + +It is generated from these files: + backupitemaction/v1/BackupItemAction.proto + +It has these top-level messages: + ExecuteRequest + ExecuteResponse + BackupItemActionAppliesToRequest + BackupItemActionAppliesToResponse +*/ +package v1 + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ExecuteRequest struct { + Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` +} + +func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} } +func (m *ExecuteRequest) String() string { return proto.CompactTextString(m) } +func (*ExecuteRequest) ProtoMessage() {} +func (*ExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *ExecuteRequest) GetPlugin() string { + if m != nil { + return m.Plugin + } + return "" +} + +func (m *ExecuteRequest) GetItem() []byte { + if m != nil { + return m.Item + } + return nil +} + +func (m *ExecuteRequest) GetBackup() []byte { + if m != nil { + return m.Backup + } + return nil +} + +type ExecuteResponse struct { + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + AdditionalItems []*generated.ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems" json:"additionalItems,omitempty"` +} + +func (m *ExecuteResponse) Reset() { *m = ExecuteResponse{} } +func (m *ExecuteResponse) String() string { return proto.CompactTextString(m) } +func (*ExecuteResponse) ProtoMessage() {} +func (*ExecuteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *ExecuteResponse) GetItem() []byte { + if m != nil { + return m.Item + } + return nil +} + +func (m *ExecuteResponse) GetAdditionalItems() []*generated.ResourceIdentifier { + if m != nil { + return m.AdditionalItems + } + return nil +} + +type BackupItemActionAppliesToRequest struct { + Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` +} + +func (m *BackupItemActionAppliesToRequest) Reset() { *m = BackupItemActionAppliesToRequest{} } +func (m *BackupItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } +func (*BackupItemActionAppliesToRequest) ProtoMessage() {} +func (*BackupItemActionAppliesToRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{2} +} + +func (m *BackupItemActionAppliesToRequest) GetPlugin() string { + if m != nil { + return m.Plugin + } + return "" +} + +type BackupItemActionAppliesToResponse struct { + ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` +} + +func (m *BackupItemActionAppliesToResponse) Reset() { *m = BackupItemActionAppliesToResponse{} } +func (m *BackupItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } +func (*BackupItemActionAppliesToResponse) ProtoMessage() {} +func (*BackupItemActionAppliesToResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{3} +} + +func (m *BackupItemActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { + if m != nil { + return m.ResourceSelector + } + return nil +} + +func init() { + proto.RegisterType((*ExecuteRequest)(nil), "v1.ExecuteRequest") + proto.RegisterType((*ExecuteResponse)(nil), "v1.ExecuteResponse") + proto.RegisterType((*BackupItemActionAppliesToRequest)(nil), "v1.BackupItemActionAppliesToRequest") + proto.RegisterType((*BackupItemActionAppliesToResponse)(nil), "v1.BackupItemActionAppliesToResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for BackupItemAction service + +type BackupItemActionClient interface { + AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) + Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) +} + +type backupItemActionClient struct { + cc *grpc.ClientConn +} + +func NewBackupItemActionClient(cc *grpc.ClientConn) BackupItemActionClient { + return &backupItemActionClient{cc} +} + +func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) { + out := new(BackupItemActionAppliesToResponse) + err := grpc.Invoke(ctx, "/v1.BackupItemAction/AppliesTo", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backupItemActionClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) { + out := new(ExecuteResponse) + err := grpc.Invoke(ctx, "/v1.BackupItemAction/Execute", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for BackupItemAction service + +type BackupItemActionServer interface { + AppliesTo(context.Context, *BackupItemActionAppliesToRequest) (*BackupItemActionAppliesToResponse, error) + Execute(context.Context, *ExecuteRequest) (*ExecuteResponse, error) +} + +func RegisterBackupItemActionServer(s *grpc.Server, srv BackupItemActionServer) { + s.RegisterService(&_BackupItemAction_serviceDesc, srv) +} + +func _BackupItemAction_AppliesTo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BackupItemActionAppliesToRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackupItemActionServer).AppliesTo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1.BackupItemAction/AppliesTo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackupItemActionServer).AppliesTo(ctx, req.(*BackupItemActionAppliesToRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BackupItemAction_Execute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackupItemActionServer).Execute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1.BackupItemAction/Execute", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackupItemActionServer).Execute(ctx, req.(*ExecuteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _BackupItemAction_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1.BackupItemAction", + HandlerType: (*BackupItemActionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AppliesTo", + Handler: _BackupItemAction_AppliesTo_Handler, + }, + { + MethodName: "Execute", + Handler: _BackupItemAction_Execute_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "backupitemaction/v1/BackupItemAction.proto", +} + +func init() { proto.RegisterFile("backupitemaction/v1/BackupItemAction.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0x5f, 0x4b, 0xfb, 0x30, + 0x14, 0xa5, 0xdb, 0x8f, 0xfd, 0x58, 0x36, 0xdc, 0x88, 0x20, 0x65, 0x22, 0xd4, 0xa2, 0x30, 0x04, + 0x1b, 0x56, 0xdf, 0x7c, 0xdb, 0x40, 0xc6, 0x5e, 0xbb, 0x3d, 0x88, 0x6f, 0x59, 0x7b, 0xed, 0xc2, + 0xda, 0x24, 0xa6, 0x49, 0x15, 0x3f, 0x8d, 0x1f, 0x55, 0xfa, 0x67, 0xc5, 0xcd, 0xe1, 0xde, 0x72, + 0x0f, 0xf7, 0xdc, 0x73, 0x4e, 0xee, 0x45, 0x77, 0x6b, 0x1a, 0x6e, 0x8d, 0x64, 0x1a, 0x52, 0x1a, + 0x6a, 0x26, 0x38, 0xc9, 0x27, 0x64, 0x56, 0x62, 0x0b, 0x0d, 0xe9, 0xb4, 0xc4, 0x3c, 0xa9, 0x84, + 0x16, 0xb8, 0x95, 0x4f, 0x46, 0xfd, 0xe5, 0x86, 0x2a, 0x88, 0x2a, 0xc4, 0x5d, 0xa1, 0xb3, 0xa7, + 0x0f, 0x08, 0x8d, 0x86, 0x00, 0xde, 0x0c, 0x64, 0x1a, 0x5f, 0xa0, 0x8e, 0x4c, 0x4c, 0xcc, 0xb8, + 0x6d, 0x39, 0xd6, 0xb8, 0x1b, 0xd4, 0x15, 0xc6, 0xe8, 0x5f, 0xa1, 0x61, 0xb7, 0x1c, 0x6b, 0xdc, + 0x0f, 0xca, 0x77, 0xd1, 0x5b, 0xa9, 0xdb, 0xed, 0x12, 0xad, 0x2b, 0x97, 0xa3, 0x41, 0x33, 0x35, + 0x93, 0x82, 0x67, 0xd0, 0xd0, 0xad, 0x1f, 0xf4, 0x39, 0x1a, 0xd0, 0x28, 0x62, 0x85, 0x41, 0x9a, + 0x14, 0x66, 0x33, 0xbb, 0xe5, 0xb4, 0xc7, 0x3d, 0xff, 0xca, 0x8b, 0x81, 0x83, 0xa2, 0x1a, 0x22, + 0x2f, 0x80, 0x4c, 0x18, 0x15, 0xc2, 0x22, 0x02, 0xae, 0xd9, 0x2b, 0x03, 0x15, 0x1c, 0xb2, 0xdc, + 0x47, 0xe4, 0x1c, 0x26, 0x9e, 0x4a, 0x99, 0x30, 0xc8, 0x56, 0xe2, 0x44, 0x2e, 0x37, 0x41, 0xd7, + 0x7f, 0x70, 0x6b, 0xf7, 0x73, 0x34, 0xdc, 0xf9, 0x58, 0x42, 0x02, 0xa1, 0x16, 0xaa, 0x1c, 0xd3, + 0xf3, 0x2f, 0x8f, 0x58, 0xdd, 0xb5, 0x04, 0xbf, 0x48, 0xfe, 0x97, 0x85, 0x86, 0x87, 0x72, 0xf8, + 0x19, 0x75, 0x1b, 0x49, 0x7c, 0xe3, 0xe5, 0x13, 0xef, 0x54, 0x9a, 0xd1, 0xed, 0x89, 0xae, 0xda, + 0xb7, 0x8f, 0xfe, 0xd7, 0x8b, 0xc0, 0xb8, 0x60, 0xec, 0xef, 0x7a, 0x74, 0xbe, 0x87, 0x55, 0x9c, + 0xd9, 0xe2, 0x65, 0x1e, 0x33, 0xbd, 0x31, 0x6b, 0x2f, 0x14, 0x29, 0xc9, 0xd3, 0x77, 0xaa, 0xe0, + 0x5e, 0x53, 0xfe, 0x69, 0x48, 0x0e, 0x09, 0x28, 0x41, 0xe4, 0x36, 0x26, 0xd5, 0xef, 0x91, 0x26, + 0x3c, 0x39, 0x72, 0x86, 0xeb, 0x4e, 0x79, 0x64, 0x0f, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd7, + 0x9d, 0xff, 0x70, 0xa4, 0x02, 0x00, 0x00, +} From 6b8353081aa97f747cc3fffcfb4c5da66abb15c7 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 25 Aug 2022 14:02:50 -0400 Subject: [PATCH 267/366] renamed update files so that protoc is run before CRD update. If generating protoc go files from scratch, `make update` fails if CRD generation happens first, since the protoc-generated files are imported by the api go files. protoc generation needs to happen earlier. Signed-off-by: Scott Seago --- hack/{update-fmt.sh => update-1fmt.sh} | 0 hack/{update-proto.sh => update-2proto.sh} | 0 ...date-generated-crd-code.sh => update-3generated-crd-code.sh} | 0 ...ed-issue-template.sh => update-4generated-issue-template.sh} | 0 hack/verify-fmt.sh | 2 +- hack/verify-generated-crd-code.sh | 2 +- hack/verify-generated-issue-template.sh | 2 +- 7 files changed, 3 insertions(+), 3 deletions(-) rename hack/{update-fmt.sh => update-1fmt.sh} (100%) rename hack/{update-proto.sh => update-2proto.sh} (100%) rename hack/{update-generated-crd-code.sh => update-3generated-crd-code.sh} (100%) rename hack/{update-generated-issue-template.sh => update-4generated-issue-template.sh} (100%) diff --git a/hack/update-fmt.sh b/hack/update-1fmt.sh similarity index 100% rename from hack/update-fmt.sh rename to hack/update-1fmt.sh diff --git a/hack/update-proto.sh b/hack/update-2proto.sh similarity index 100% rename from hack/update-proto.sh rename to hack/update-2proto.sh diff --git a/hack/update-generated-crd-code.sh b/hack/update-3generated-crd-code.sh similarity index 100% rename from hack/update-generated-crd-code.sh rename to hack/update-3generated-crd-code.sh diff --git a/hack/update-generated-issue-template.sh b/hack/update-4generated-issue-template.sh similarity index 100% rename from hack/update-generated-issue-template.sh rename to hack/update-4generated-issue-template.sh diff --git a/hack/verify-fmt.sh b/hack/verify-fmt.sh index e592ed0cfa..f3858cf24b 100755 --- a/hack/verify-fmt.sh +++ b/hack/verify-fmt.sh @@ -15,4 +15,4 @@ # limitations under the License. HACK_DIR=$(dirname "${BASH_SOURCE[0]}") -"${HACK_DIR}"/update-fmt.sh --verify +"${HACK_DIR}"/update-1fmt.sh --verify diff --git a/hack/verify-generated-crd-code.sh b/hack/verify-generated-crd-code.sh index d4c097c799..387acd74e7 100755 --- a/hack/verify-generated-crd-code.sh +++ b/hack/verify-generated-crd-code.sh @@ -16,7 +16,7 @@ HACK_DIR=$(dirname "${BASH_SOURCE}") -${HACK_DIR}/update-generated-crd-code.sh +${HACK_DIR}/update-3generated-crd-code.sh # ensure no changes to generated CRDs if ! git diff --exit-code config/crd/v1/crds/crds.go >/dev/null; then diff --git a/hack/verify-generated-issue-template.sh b/hack/verify-generated-issue-template.sh index 44e68e05a8..7d5cbf4efd 100755 --- a/hack/verify-generated-issue-template.sh +++ b/hack/verify-generated-issue-template.sh @@ -27,7 +27,7 @@ cleanup() { } echo "Verifying generated Github issue template" -${HACK_DIR}/update-generated-issue-template.sh ${OUT_TMP_FILE} > /dev/null +${HACK_DIR}/update-4generated-issue-template.sh ${OUT_TMP_FILE} > /dev/null output=$(echo "`diff ${ISSUE_TEMPLATE_FILE} ${OUT_TMP_FILE}`") if [[ -n "${output}" ]] ; then From 4f2c2d2679527565b3d9db8d8bda48874d88066b Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 25 Aug 2022 18:24:48 -0400 Subject: [PATCH 268/366] updated to newer protoc/protoc-gen-go Signed-off-by: Scott Seago --- go.mod | 2 +- hack/build-image/Dockerfile | 6 +- hack/update-2proto.sh | 7 +- pkg/plugin/generated/DeleteItemAction.pb.go | 442 ++++-- pkg/plugin/generated/ItemSnapshotter.pb.go | 1408 +++++++++++++---- pkg/plugin/generated/ObjectStore.pb.go | 1256 ++++++++++++--- pkg/plugin/generated/PluginLister.pb.go | 295 +++- pkg/plugin/generated/RestoreItemAction.pb.go | 471 ++++-- pkg/plugin/generated/Shared.pb.go | 516 ++++-- pkg/plugin/generated/VolumeSnapshotter.pb.go | 1172 +++++++++++--- .../v1/BackupItemAction.pb.go | 470 ++++-- pkg/plugin/proto/DeleteItemAction.proto | 1 + pkg/plugin/proto/ItemSnapshotter.proto | 1 + pkg/plugin/proto/ObjectStore.proto | 1 + pkg/plugin/proto/PluginLister.proto | 1 + pkg/plugin/proto/RestoreItemAction.proto | 1 + pkg/plugin/proto/VolumeSnapshotter.proto | 1 + 17 files changed, 4711 insertions(+), 1340 deletions(-) diff --git a/go.mod b/go.mod index d01c71d58f..ae4e988f6a 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/api v0.74.0 google.golang.org/grpc v1.45.0 + google.golang.org/protobuf v1.28.0 k8s.io/api v0.22.2 k8s.io/apiextensions-apiserver v0.22.2 k8s.io/apimachinery v0.22.2 @@ -132,7 +133,6 @@ require ( gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect - google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 5f35646310..5baa4365e2 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -45,11 +45,11 @@ RUN go get golang.org/x/tools/cmd/goimports@11e9d9cc0042e6bd10337d4d2c3e5d929550 # get protoc compiler and golang plugin WORKDIR /root RUN apt-get update && apt-get install -y unzip -RUN wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v3.9.1/protoc-3.9.1-linux-x86_64.zip && \ - unzip protoc-3.9.1-linux-x86_64.zip && \ +RUN wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip && \ + unzip protoc-3.14.0-linux-x86_64.zip && \ mv bin/protoc /usr/bin/protoc && \ chmod +x /usr/bin/protoc -RUN go get github.com/golang/protobuf/protoc-gen-go@v1.0.0 +RUN go get github.com/golang/protobuf/protoc-gen-go@v1.4.3 # get goreleaser RUN wget --quiet https://github.com/goreleaser/goreleaser/releases/download/v0.120.8/goreleaser_Linux_x86_64.tar.gz && \ diff --git a/hack/update-2proto.sh b/hack/update-2proto.sh index bc8d3a14e6..b3c8e0ba29 100755 --- a/hack/update-2proto.sh +++ b/hack/update-2proto.sh @@ -18,9 +18,8 @@ HACK_DIR=$(dirname "${BASH_SOURCE}") echo "Updating plugin proto" -protoc pkg/plugin/proto/backupitemaction/v1/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ -I pkg/plugin/proto/ -protoc pkg/plugin/proto/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ -I pkg/plugin/proto/ -cp -rf pkg/plugin/generated/github.com/vmware-tanzu/velero/pkg/plugin/generated/* pkg/plugin/generated -rm -rf pkg/plugin/generated/github.com +echo protoc --version +protoc pkg/plugin/proto/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ --go_opt=module=github.com/vmware-tanzu/velero/pkg/plugin/generated -I pkg/plugin/proto/ +protoc pkg/plugin/proto/backupitemaction/v1/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ --go_opt=module=github.com/vmware-tanzu/velero/pkg/plugin/generated -I pkg/plugin/proto/ echo "Updating plugin proto - done!" diff --git a/pkg/plugin/generated/DeleteItemAction.pb.go b/pkg/plugin/generated/DeleteItemAction.pb.go index 5302d651ad..be5759f381 100644 --- a/pkg/plugin/generated/DeleteItemAction.pb.go +++ b/pkg/plugin/generated/DeleteItemAction.pb.go @@ -1,193 +1,357 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: DeleteItemAction.proto -/* -Package generated is a generated protocol buffer package. - -It is generated from these files: - DeleteItemAction.proto - ItemSnapshotter.proto - ObjectStore.proto - PluginLister.proto - RestoreItemAction.proto - Shared.proto - VolumeSnapshotter.proto - -It has these top-level messages: - DeleteItemActionExecuteRequest - DeleteItemActionAppliesToRequest - DeleteItemActionAppliesToResponse - ItemSnapshotterAppliesToRequest - ItemSnapshotterAppliesToResponse - AlsoHandlesRequest - AlsoHandlesResponse - SnapshotItemRequest - SnapshotItemResponse - ProgressRequest - ProgressResponse - DeleteItemSnapshotRequest - CreateItemFromSnapshotRequest - CreateItemFromSnapshotResponse - ItemSnapshotterInitRequest - PutObjectRequest - ObjectExistsRequest - ObjectExistsResponse - GetObjectRequest - Bytes - ListCommonPrefixesRequest - ListCommonPrefixesResponse - ListObjectsRequest - ListObjectsResponse - DeleteObjectRequest - CreateSignedURLRequest - CreateSignedURLResponse - ObjectStoreInitRequest - PluginIdentifier - ListPluginsResponse - RestoreItemActionExecuteRequest - RestoreItemActionExecuteResponse - RestoreItemActionAppliesToRequest - RestoreItemActionAppliesToResponse - Empty - Stack - StackFrame - ResourceIdentifier - ResourceSelector - CreateVolumeRequest - CreateVolumeResponse - GetVolumeInfoRequest - GetVolumeInfoResponse - CreateSnapshotRequest - CreateSnapshotResponse - DeleteSnapshotRequest - GetVolumeIDRequest - GetVolumeIDResponse - SetVolumeIDRequest - SetVolumeIDResponse - VolumeSnapshotterInitRequest -*/ package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type DeleteItemActionExecuteRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` } -func (m *DeleteItemActionExecuteRequest) Reset() { *m = DeleteItemActionExecuteRequest{} } -func (m *DeleteItemActionExecuteRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteItemActionExecuteRequest) ProtoMessage() {} -func (*DeleteItemActionExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (x *DeleteItemActionExecuteRequest) Reset() { + *x = DeleteItemActionExecuteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_DeleteItemAction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteItemActionExecuteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteItemActionExecuteRequest) ProtoMessage() {} + +func (x *DeleteItemActionExecuteRequest) ProtoReflect() protoreflect.Message { + mi := &file_DeleteItemAction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteItemActionExecuteRequest.ProtoReflect.Descriptor instead. +func (*DeleteItemActionExecuteRequest) Descriptor() ([]byte, []int) { + return file_DeleteItemAction_proto_rawDescGZIP(), []int{0} +} -func (m *DeleteItemActionExecuteRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *DeleteItemActionExecuteRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *DeleteItemActionExecuteRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *DeleteItemActionExecuteRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *DeleteItemActionExecuteRequest) GetBackup() []byte { - if m != nil { - return m.Backup +func (x *DeleteItemActionExecuteRequest) GetBackup() []byte { + if x != nil { + return x.Backup } return nil } type DeleteItemActionAppliesToRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` +} + +func (x *DeleteItemActionAppliesToRequest) Reset() { + *x = DeleteItemActionAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_DeleteItemAction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteItemActionAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *DeleteItemActionAppliesToRequest) Reset() { *m = DeleteItemActionAppliesToRequest{} } -func (m *DeleteItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteItemActionAppliesToRequest) ProtoMessage() {} +func (*DeleteItemActionAppliesToRequest) ProtoMessage() {} + +func (x *DeleteItemActionAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_DeleteItemAction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteItemActionAppliesToRequest.ProtoReflect.Descriptor instead. func (*DeleteItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{1} + return file_DeleteItemAction_proto_rawDescGZIP(), []int{1} } -func (m *DeleteItemActionAppliesToRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *DeleteItemActionAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } type DeleteItemActionAppliesToResponse struct { - ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *DeleteItemActionAppliesToResponse) Reset() { + *x = DeleteItemActionAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_DeleteItemAction_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteItemActionAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteItemActionAppliesToResponse) ProtoMessage() {} + +func (x *DeleteItemActionAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_DeleteItemAction_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *DeleteItemActionAppliesToResponse) Reset() { *m = DeleteItemActionAppliesToResponse{} } -func (m *DeleteItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } -func (*DeleteItemActionAppliesToResponse) ProtoMessage() {} +// Deprecated: Use DeleteItemActionAppliesToResponse.ProtoReflect.Descriptor instead. func (*DeleteItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{2} + return file_DeleteItemAction_proto_rawDescGZIP(), []int{2} } -func (m *DeleteItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { - if m != nil { - return m.ResourceSelector +func (x *DeleteItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { + if x != nil { + return x.ResourceSelector } return nil } -func init() { - proto.RegisterType((*DeleteItemActionExecuteRequest)(nil), "generated.DeleteItemActionExecuteRequest") - proto.RegisterType((*DeleteItemActionAppliesToRequest)(nil), "generated.DeleteItemActionAppliesToRequest") - proto.RegisterType((*DeleteItemActionAppliesToResponse)(nil), "generated.DeleteItemActionAppliesToResponse") +var File_DeleteItemAction_proto protoreflect.FileDescriptor + +var file_DeleteItemAction_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x64, 0x0a, 0x1e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, + 0x16, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x3a, 0x0a, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x22, 0x6c, 0x0a, 0x21, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, + 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x32, 0xc2, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x73, 0x54, 0x6f, 0x12, 0x2b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, + 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, + 0x75, 0x2f, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_DeleteItemAction_proto_rawDescOnce sync.Once + file_DeleteItemAction_proto_rawDescData = file_DeleteItemAction_proto_rawDesc +) + +func file_DeleteItemAction_proto_rawDescGZIP() []byte { + file_DeleteItemAction_proto_rawDescOnce.Do(func() { + file_DeleteItemAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_DeleteItemAction_proto_rawDescData) + }) + return file_DeleteItemAction_proto_rawDescData +} + +var file_DeleteItemAction_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_DeleteItemAction_proto_goTypes = []interface{}{ + (*DeleteItemActionExecuteRequest)(nil), // 0: generated.DeleteItemActionExecuteRequest + (*DeleteItemActionAppliesToRequest)(nil), // 1: generated.DeleteItemActionAppliesToRequest + (*DeleteItemActionAppliesToResponse)(nil), // 2: generated.DeleteItemActionAppliesToResponse + (*ResourceSelector)(nil), // 3: generated.ResourceSelector + (*Empty)(nil), // 4: generated.Empty +} +var file_DeleteItemAction_proto_depIdxs = []int32{ + 3, // 0: generated.DeleteItemActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 1, // 1: generated.DeleteItemAction.AppliesTo:input_type -> generated.DeleteItemActionAppliesToRequest + 0, // 2: generated.DeleteItemAction.Execute:input_type -> generated.DeleteItemActionExecuteRequest + 2, // 3: generated.DeleteItemAction.AppliesTo:output_type -> generated.DeleteItemActionAppliesToResponse + 4, // 4: generated.DeleteItemAction.Execute:output_type -> generated.Empty + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_DeleteItemAction_proto_init() } +func file_DeleteItemAction_proto_init() { + if File_DeleteItemAction_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_DeleteItemAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteItemActionExecuteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_DeleteItemAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteItemActionAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_DeleteItemAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteItemActionAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_DeleteItemAction_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_DeleteItemAction_proto_goTypes, + DependencyIndexes: file_DeleteItemAction_proto_depIdxs, + MessageInfos: file_DeleteItemAction_proto_msgTypes, + }.Build() + File_DeleteItemAction_proto = out.File + file_DeleteItemAction_proto_rawDesc = nil + file_DeleteItemAction_proto_goTypes = nil + file_DeleteItemAction_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for DeleteItemAction service +const _ = grpc.SupportPackageIsVersion6 +// DeleteItemActionClient is the client API for DeleteItemAction service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type DeleteItemActionClient interface { AppliesTo(ctx context.Context, in *DeleteItemActionAppliesToRequest, opts ...grpc.CallOption) (*DeleteItemActionAppliesToResponse, error) Execute(ctx context.Context, in *DeleteItemActionExecuteRequest, opts ...grpc.CallOption) (*Empty, error) } type deleteItemActionClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewDeleteItemActionClient(cc *grpc.ClientConn) DeleteItemActionClient { +func NewDeleteItemActionClient(cc grpc.ClientConnInterface) DeleteItemActionClient { return &deleteItemActionClient{cc} } func (c *deleteItemActionClient) AppliesTo(ctx context.Context, in *DeleteItemActionAppliesToRequest, opts ...grpc.CallOption) (*DeleteItemActionAppliesToResponse, error) { out := new(DeleteItemActionAppliesToResponse) - err := grpc.Invoke(ctx, "/generated.DeleteItemAction/AppliesTo", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.DeleteItemAction/AppliesTo", in, out, opts...) if err != nil { return nil, err } @@ -196,20 +360,30 @@ func (c *deleteItemActionClient) AppliesTo(ctx context.Context, in *DeleteItemAc func (c *deleteItemActionClient) Execute(ctx context.Context, in *DeleteItemActionExecuteRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.DeleteItemAction/Execute", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.DeleteItemAction/Execute", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for DeleteItemAction service - +// DeleteItemActionServer is the server API for DeleteItemAction service. type DeleteItemActionServer interface { AppliesTo(context.Context, *DeleteItemActionAppliesToRequest) (*DeleteItemActionAppliesToResponse, error) Execute(context.Context, *DeleteItemActionExecuteRequest) (*Empty, error) } +// UnimplementedDeleteItemActionServer can be embedded to have forward compatible implementations. +type UnimplementedDeleteItemActionServer struct { +} + +func (*UnimplementedDeleteItemActionServer) AppliesTo(context.Context, *DeleteItemActionAppliesToRequest) (*DeleteItemActionAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (*UnimplementedDeleteItemActionServer) Execute(context.Context, *DeleteItemActionExecuteRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Execute not implemented") +} + func RegisterDeleteItemActionServer(s *grpc.Server, srv DeleteItemActionServer) { s.RegisterService(&_DeleteItemAction_serviceDesc, srv) } @@ -266,25 +440,3 @@ var _DeleteItemAction_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "DeleteItemAction.proto", } - -func init() { proto.RegisterFile("DeleteItemAction.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 253 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x41, 0x4b, 0xc3, 0x40, - 0x14, 0x84, 0x89, 0x4a, 0x25, 0xcf, 0x1e, 0xc2, 0x1e, 0x4a, 0x88, 0x20, 0x31, 0xa7, 0x8a, 0x92, - 0x43, 0xbd, 0x79, 0x2b, 0x58, 0xc5, 0x6b, 0xea, 0x1f, 0x48, 0x37, 0x63, 0x0d, 0x6e, 0xb2, 0xeb, - 0xee, 0x5b, 0xd0, 0xbf, 0xe7, 0x2f, 0x13, 0x63, 0x28, 0x35, 0x42, 0xdb, 0xdb, 0xbe, 0xdd, 0x99, - 0xf7, 0x31, 0x3b, 0x34, 0xb9, 0x87, 0x02, 0xe3, 0x89, 0xd1, 0xcc, 0x25, 0xd7, 0xba, 0xcd, 0x8d, - 0xd5, 0xac, 0x45, 0xb8, 0x46, 0x0b, 0x5b, 0x32, 0xaa, 0x64, 0xbc, 0x7c, 0x2d, 0x2d, 0xaa, 0xdf, - 0x87, 0xac, 0xa2, 0x8b, 0xa1, 0x65, 0xf1, 0x01, 0xe9, 0x19, 0x05, 0xde, 0x3d, 0x1c, 0x8b, 0x09, - 0x8d, 0x8c, 0xf2, 0xeb, 0xba, 0x8d, 0x83, 0x34, 0x98, 0x86, 0x45, 0x3f, 0x09, 0x41, 0x27, 0x35, - 0xa3, 0x89, 0x8f, 0xd2, 0x60, 0x3a, 0x2e, 0xba, 0xf3, 0x8f, 0x76, 0x55, 0xca, 0x37, 0x6f, 0xe2, - 0xe3, 0xee, 0xb6, 0x9f, 0xb2, 0x3b, 0x4a, 0x87, 0x94, 0xb9, 0x31, 0xaa, 0x86, 0x7b, 0xd6, 0x7b, - 0x38, 0x99, 0xa2, 0xcb, 0x1d, 0x5e, 0x67, 0x74, 0xeb, 0x20, 0x1e, 0x29, 0x2a, 0xe0, 0xb4, 0xb7, - 0x12, 0x4b, 0x28, 0x48, 0xd6, 0xb6, 0x5b, 0x73, 0x36, 0x3b, 0xcf, 0x37, 0xd1, 0xf3, 0xa1, 0xa4, - 0xf8, 0x67, 0x9a, 0x7d, 0x05, 0x14, 0x0d, 0x71, 0xe2, 0x85, 0xc2, 0x0d, 0x52, 0x5c, 0x6f, 0x2d, - 0xdc, 0x17, 0x2a, 0xb9, 0x39, 0x4c, 0xdc, 0xa7, 0x78, 0xa0, 0xd3, 0xfe, 0xf3, 0xc5, 0xd5, 0x0e, - 0xe3, 0xdf, 0x82, 0x92, 0x68, 0x4b, 0xba, 0x68, 0x0c, 0x7f, 0xae, 0x46, 0x5d, 0xb7, 0xb7, 0xdf, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x31, 0x0b, 0xd3, 0x0e, 0x02, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/ItemSnapshotter.pb.go b/pkg/plugin/generated/ItemSnapshotter.pb.go index 79c99094ce..f1fdf27c4a 100644 --- a/pkg/plugin/generated/ItemSnapshotter.pb.go +++ b/pkg/plugin/generated/ItemSnapshotter.pb.go @@ -1,501 +1,1322 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: ItemSnapshotter.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type ItemSnapshotterAppliesToRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` +} + +func (x *ItemSnapshotterAppliesToRequest) Reset() { + *x = ItemSnapshotterAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemSnapshotterAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ItemSnapshotterAppliesToRequest) Reset() { *m = ItemSnapshotterAppliesToRequest{} } -func (m *ItemSnapshotterAppliesToRequest) String() string { return proto.CompactTextString(m) } -func (*ItemSnapshotterAppliesToRequest) ProtoMessage() {} +func (*ItemSnapshotterAppliesToRequest) ProtoMessage() {} + +func (x *ItemSnapshotterAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemSnapshotterAppliesToRequest.ProtoReflect.Descriptor instead. func (*ItemSnapshotterAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor1, []int{0} + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{0} } -func (m *ItemSnapshotterAppliesToRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ItemSnapshotterAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } type ItemSnapshotterAppliesToResponse struct { - ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *ItemSnapshotterAppliesToResponse) Reset() { + *x = ItemSnapshotterAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemSnapshotterAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemSnapshotterAppliesToResponse) ProtoMessage() {} + +func (x *ItemSnapshotterAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *ItemSnapshotterAppliesToResponse) Reset() { *m = ItemSnapshotterAppliesToResponse{} } -func (m *ItemSnapshotterAppliesToResponse) String() string { return proto.CompactTextString(m) } -func (*ItemSnapshotterAppliesToResponse) ProtoMessage() {} +// Deprecated: Use ItemSnapshotterAppliesToResponse.ProtoReflect.Descriptor instead. func (*ItemSnapshotterAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor1, []int{1} + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{1} } -func (m *ItemSnapshotterAppliesToResponse) GetResourceSelector() *ResourceSelector { - if m != nil { - return m.ResourceSelector +func (x *ItemSnapshotterAppliesToResponse) GetResourceSelector() *ResourceSelector { + if x != nil { + return x.ResourceSelector } return nil } type AlsoHandlesRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` } -func (m *AlsoHandlesRequest) Reset() { *m = AlsoHandlesRequest{} } -func (m *AlsoHandlesRequest) String() string { return proto.CompactTextString(m) } -func (*AlsoHandlesRequest) ProtoMessage() {} -func (*AlsoHandlesRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } +func (x *AlsoHandlesRequest) Reset() { + *x = AlsoHandlesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AlsoHandlesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *AlsoHandlesRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (*AlsoHandlesRequest) ProtoMessage() {} + +func (x *AlsoHandlesRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AlsoHandlesRequest.ProtoReflect.Descriptor instead. +func (*AlsoHandlesRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{2} +} + +func (x *AlsoHandlesRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *AlsoHandlesRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *AlsoHandlesRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *AlsoHandlesRequest) GetBackup() []byte { - if m != nil { - return m.Backup +func (x *AlsoHandlesRequest) GetBackup() []byte { + if x != nil { + return x.Backup } return nil } type AlsoHandlesResponse struct { - HandledItems []*ResourceIdentifier `protobuf:"bytes,1,rep,name=handledItems" json:"handledItems,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HandledItems []*ResourceIdentifier `protobuf:"bytes,1,rep,name=handledItems,proto3" json:"handledItems,omitempty"` } -func (m *AlsoHandlesResponse) Reset() { *m = AlsoHandlesResponse{} } -func (m *AlsoHandlesResponse) String() string { return proto.CompactTextString(m) } -func (*AlsoHandlesResponse) ProtoMessage() {} -func (*AlsoHandlesResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} } +func (x *AlsoHandlesResponse) Reset() { + *x = AlsoHandlesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *AlsoHandlesResponse) GetHandledItems() []*ResourceIdentifier { - if m != nil { - return m.HandledItems +func (x *AlsoHandlesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AlsoHandlesResponse) ProtoMessage() {} + +func (x *AlsoHandlesResponse) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AlsoHandlesResponse.ProtoReflect.Descriptor instead. +func (*AlsoHandlesResponse) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{3} +} + +func (x *AlsoHandlesResponse) GetHandledItems() []*ResourceIdentifier { + if x != nil { + return x.HandledItems } return nil } type SnapshotItemRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` - Params map[string]string `protobuf:"bytes,3,rep,name=params" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Params map[string]string `protobuf:"bytes,3,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Backup []byte `protobuf:"bytes,4,opt,name=backup,proto3" json:"backup,omitempty"` } -func (m *SnapshotItemRequest) Reset() { *m = SnapshotItemRequest{} } -func (m *SnapshotItemRequest) String() string { return proto.CompactTextString(m) } -func (*SnapshotItemRequest) ProtoMessage() {} -func (*SnapshotItemRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} } +func (x *SnapshotItemRequest) Reset() { + *x = SnapshotItemRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SnapshotItemRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SnapshotItemRequest) ProtoMessage() {} + +func (x *SnapshotItemRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SnapshotItemRequest.ProtoReflect.Descriptor instead. +func (*SnapshotItemRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{4} +} -func (m *SnapshotItemRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *SnapshotItemRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *SnapshotItemRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *SnapshotItemRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *SnapshotItemRequest) GetParams() map[string]string { - if m != nil { - return m.Params +func (x *SnapshotItemRequest) GetParams() map[string]string { + if x != nil { + return x.Params } return nil } -func (m *SnapshotItemRequest) GetBackup() []byte { - if m != nil { - return m.Backup +func (x *SnapshotItemRequest) GetBackup() []byte { + if x != nil { + return x.Backup } return nil } type SnapshotItemResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID" json:"snapshotID,omitempty"` - SnapshotMetadata map[string]string `protobuf:"bytes,3,rep,name=snapshotMetadata" json:"snapshotMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - AdditionalItems []*ResourceIdentifier `protobuf:"bytes,4,rep,name=additionalItems" json:"additionalItems,omitempty"` - HandledItems []*ResourceIdentifier `protobuf:"bytes,5,rep,name=handledItems" json:"handledItems,omitempty"` + SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` + SnapshotMetadata map[string]string `protobuf:"bytes,3,rep,name=snapshotMetadata,proto3" json:"snapshotMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + AdditionalItems []*ResourceIdentifier `protobuf:"bytes,4,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` + HandledItems []*ResourceIdentifier `protobuf:"bytes,5,rep,name=handledItems,proto3" json:"handledItems,omitempty"` +} + +func (x *SnapshotItemResponse) Reset() { + *x = SnapshotItemResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SnapshotItemResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SnapshotItemResponse) ProtoMessage() {} + +func (x *SnapshotItemResponse) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *SnapshotItemResponse) Reset() { *m = SnapshotItemResponse{} } -func (m *SnapshotItemResponse) String() string { return proto.CompactTextString(m) } -func (*SnapshotItemResponse) ProtoMessage() {} -func (*SnapshotItemResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } +// Deprecated: Use SnapshotItemResponse.ProtoReflect.Descriptor instead. +func (*SnapshotItemResponse) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{5} +} -func (m *SnapshotItemResponse) GetItem() []byte { - if m != nil { - return m.Item +func (x *SnapshotItemResponse) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *SnapshotItemResponse) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *SnapshotItemResponse) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } -func (m *SnapshotItemResponse) GetSnapshotMetadata() map[string]string { - if m != nil { - return m.SnapshotMetadata +func (x *SnapshotItemResponse) GetSnapshotMetadata() map[string]string { + if x != nil { + return x.SnapshotMetadata } return nil } -func (m *SnapshotItemResponse) GetAdditionalItems() []*ResourceIdentifier { - if m != nil { - return m.AdditionalItems +func (x *SnapshotItemResponse) GetAdditionalItems() []*ResourceIdentifier { + if x != nil { + return x.AdditionalItems } return nil } -func (m *SnapshotItemResponse) GetHandledItems() []*ResourceIdentifier { - if m != nil { - return m.HandledItems +func (x *SnapshotItemResponse) GetHandledItems() []*ResourceIdentifier { + if x != nil { + return x.HandledItems } return nil } type ProgressRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - ItemID *ResourceIdentifier `protobuf:"bytes,2,opt,name=itemID" json:"itemID,omitempty"` - SnapshotID string `protobuf:"bytes,3,opt,name=snapshotID" json:"snapshotID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + ItemID *ResourceIdentifier `protobuf:"bytes,2,opt,name=itemID,proto3" json:"itemID,omitempty"` + SnapshotID string `protobuf:"bytes,3,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` Backup []byte `protobuf:"bytes,4,opt,name=backup,proto3" json:"backup,omitempty"` } -func (m *ProgressRequest) Reset() { *m = ProgressRequest{} } -func (m *ProgressRequest) String() string { return proto.CompactTextString(m) } -func (*ProgressRequest) ProtoMessage() {} -func (*ProgressRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } +func (x *ProgressRequest) Reset() { + *x = ProgressRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProgressRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProgressRequest) ProtoMessage() {} + +func (x *ProgressRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProgressRequest.ProtoReflect.Descriptor instead. +func (*ProgressRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{6} +} -func (m *ProgressRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ProgressRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ProgressRequest) GetItemID() *ResourceIdentifier { - if m != nil { - return m.ItemID +func (x *ProgressRequest) GetItemID() *ResourceIdentifier { + if x != nil { + return x.ItemID } return nil } -func (m *ProgressRequest) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *ProgressRequest) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } -func (m *ProgressRequest) GetBackup() []byte { - if m != nil { - return m.Backup +func (x *ProgressRequest) GetBackup() []byte { + if x != nil { + return x.Backup } return nil } type ProgressResponse struct { - Phase string `protobuf:"bytes,1,opt,name=phase" json:"phase,omitempty"` - ItemsCompleted int64 `protobuf:"varint,2,opt,name=itemsCompleted" json:"itemsCompleted,omitempty"` - ItemsToComplete int64 `protobuf:"varint,3,opt,name=itemsToComplete" json:"itemsToComplete,omitempty"` - Started int64 `protobuf:"varint,4,opt,name=started" json:"started,omitempty"` - StartedNano int64 `protobuf:"varint,5,opt,name=startedNano" json:"startedNano,omitempty"` - Updated int64 `protobuf:"varint,6,opt,name=updated" json:"updated,omitempty"` - UpdatedNano int64 `protobuf:"varint,7,opt,name=updatedNano" json:"updatedNano,omitempty"` - Err string `protobuf:"bytes,8,opt,name=err" json:"err,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Phase string `protobuf:"bytes,1,opt,name=phase,proto3" json:"phase,omitempty"` + ItemsCompleted int64 `protobuf:"varint,2,opt,name=itemsCompleted,proto3" json:"itemsCompleted,omitempty"` + ItemsToComplete int64 `protobuf:"varint,3,opt,name=itemsToComplete,proto3" json:"itemsToComplete,omitempty"` + Started int64 `protobuf:"varint,4,opt,name=started,proto3" json:"started,omitempty"` + StartedNano int64 `protobuf:"varint,5,opt,name=startedNano,proto3" json:"startedNano,omitempty"` + Updated int64 `protobuf:"varint,6,opt,name=updated,proto3" json:"updated,omitempty"` + UpdatedNano int64 `protobuf:"varint,7,opt,name=updatedNano,proto3" json:"updatedNano,omitempty"` + Err string `protobuf:"bytes,8,opt,name=err,proto3" json:"err,omitempty"` +} + +func (x *ProgressResponse) Reset() { + *x = ProgressResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProgressResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ProgressResponse) Reset() { *m = ProgressResponse{} } -func (m *ProgressResponse) String() string { return proto.CompactTextString(m) } -func (*ProgressResponse) ProtoMessage() {} -func (*ProgressResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } +func (*ProgressResponse) ProtoMessage() {} -func (m *ProgressResponse) GetPhase() string { - if m != nil { - return m.Phase +func (x *ProgressResponse) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProgressResponse.ProtoReflect.Descriptor instead. +func (*ProgressResponse) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{7} +} + +func (x *ProgressResponse) GetPhase() string { + if x != nil { + return x.Phase } return "" } -func (m *ProgressResponse) GetItemsCompleted() int64 { - if m != nil { - return m.ItemsCompleted +func (x *ProgressResponse) GetItemsCompleted() int64 { + if x != nil { + return x.ItemsCompleted } return 0 } -func (m *ProgressResponse) GetItemsToComplete() int64 { - if m != nil { - return m.ItemsToComplete +func (x *ProgressResponse) GetItemsToComplete() int64 { + if x != nil { + return x.ItemsToComplete } return 0 } -func (m *ProgressResponse) GetStarted() int64 { - if m != nil { - return m.Started +func (x *ProgressResponse) GetStarted() int64 { + if x != nil { + return x.Started } return 0 } -func (m *ProgressResponse) GetStartedNano() int64 { - if m != nil { - return m.StartedNano +func (x *ProgressResponse) GetStartedNano() int64 { + if x != nil { + return x.StartedNano } return 0 } -func (m *ProgressResponse) GetUpdated() int64 { - if m != nil { - return m.Updated +func (x *ProgressResponse) GetUpdated() int64 { + if x != nil { + return x.Updated } return 0 } -func (m *ProgressResponse) GetUpdatedNano() int64 { - if m != nil { - return m.UpdatedNano +func (x *ProgressResponse) GetUpdatedNano() int64 { + if x != nil { + return x.UpdatedNano } return 0 } -func (m *ProgressResponse) GetErr() string { - if m != nil { - return m.Err +func (x *ProgressResponse) GetErr() string { + if x != nil { + return x.Err } return "" } type DeleteItemSnapshotRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID" json:"snapshotID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` ItemFromBackup []byte `protobuf:"bytes,3,opt,name=itemFromBackup,proto3" json:"itemFromBackup,omitempty"` - Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Params map[string]string `protobuf:"bytes,5,rep,name=params" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Params map[string]string `protobuf:"bytes,5,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *DeleteItemSnapshotRequest) Reset() { + *x = DeleteItemSnapshotRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *DeleteItemSnapshotRequest) Reset() { *m = DeleteItemSnapshotRequest{} } -func (m *DeleteItemSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteItemSnapshotRequest) ProtoMessage() {} -func (*DeleteItemSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } +func (x *DeleteItemSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteItemSnapshotRequest) ProtoMessage() {} + +func (x *DeleteItemSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *DeleteItemSnapshotRequest) GetPlugin() string { - if m != nil { - return m.Plugin +// Deprecated: Use DeleteItemSnapshotRequest.ProtoReflect.Descriptor instead. +func (*DeleteItemSnapshotRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteItemSnapshotRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *DeleteItemSnapshotRequest) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *DeleteItemSnapshotRequest) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } -func (m *DeleteItemSnapshotRequest) GetItemFromBackup() []byte { - if m != nil { - return m.ItemFromBackup +func (x *DeleteItemSnapshotRequest) GetItemFromBackup() []byte { + if x != nil { + return x.ItemFromBackup } return nil } -func (m *DeleteItemSnapshotRequest) GetMetadata() map[string]string { - if m != nil { - return m.Metadata +func (x *DeleteItemSnapshotRequest) GetMetadata() map[string]string { + if x != nil { + return x.Metadata } return nil } -func (m *DeleteItemSnapshotRequest) GetParams() map[string]string { - if m != nil { - return m.Params +func (x *DeleteItemSnapshotRequest) GetParams() map[string]string { + if x != nil { + return x.Params } return nil } type CreateItemFromSnapshotRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` - SnapshotID string `protobuf:"bytes,3,opt,name=snapshotID" json:"snapshotID,omitempty"` + SnapshotID string `protobuf:"bytes,3,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` ItemFromBackup []byte `protobuf:"bytes,4,opt,name=itemFromBackup,proto3" json:"itemFromBackup,omitempty"` - SnapshotMetadata map[string]string `protobuf:"bytes,5,rep,name=snapshotMetadata" json:"snapshotMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Params map[string]string `protobuf:"bytes,6,rep,name=params" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + SnapshotMetadata map[string]string `protobuf:"bytes,5,rep,name=snapshotMetadata,proto3" json:"snapshotMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Params map[string]string `protobuf:"bytes,6,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Restore []byte `protobuf:"bytes,7,opt,name=restore,proto3" json:"restore,omitempty"` } -func (m *CreateItemFromSnapshotRequest) Reset() { *m = CreateItemFromSnapshotRequest{} } -func (m *CreateItemFromSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*CreateItemFromSnapshotRequest) ProtoMessage() {} -func (*CreateItemFromSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } +func (x *CreateItemFromSnapshotRequest) Reset() { + *x = CreateItemFromSnapshotRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *CreateItemFromSnapshotRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *CreateItemFromSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateItemFromSnapshotRequest) ProtoMessage() {} + +func (x *CreateItemFromSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateItemFromSnapshotRequest.ProtoReflect.Descriptor instead. +func (*CreateItemFromSnapshotRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{9} +} + +func (x *CreateItemFromSnapshotRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *CreateItemFromSnapshotRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *CreateItemFromSnapshotRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *CreateItemFromSnapshotRequest) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *CreateItemFromSnapshotRequest) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } -func (m *CreateItemFromSnapshotRequest) GetItemFromBackup() []byte { - if m != nil { - return m.ItemFromBackup +func (x *CreateItemFromSnapshotRequest) GetItemFromBackup() []byte { + if x != nil { + return x.ItemFromBackup } return nil } -func (m *CreateItemFromSnapshotRequest) GetSnapshotMetadata() map[string]string { - if m != nil { - return m.SnapshotMetadata +func (x *CreateItemFromSnapshotRequest) GetSnapshotMetadata() map[string]string { + if x != nil { + return x.SnapshotMetadata } return nil } -func (m *CreateItemFromSnapshotRequest) GetParams() map[string]string { - if m != nil { - return m.Params +func (x *CreateItemFromSnapshotRequest) GetParams() map[string]string { + if x != nil { + return x.Params } return nil } -func (m *CreateItemFromSnapshotRequest) GetRestore() []byte { - if m != nil { - return m.Restore +func (x *CreateItemFromSnapshotRequest) GetRestore() []byte { + if x != nil { + return x.Restore } return nil } type CreateItemFromSnapshotResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems" json:"additionalItems,omitempty"` - SkipRestore bool `protobuf:"varint,3,opt,name=skipRestore" json:"skipRestore,omitempty"` + AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` + SkipRestore bool `protobuf:"varint,3,opt,name=skipRestore,proto3" json:"skipRestore,omitempty"` } -func (m *CreateItemFromSnapshotResponse) Reset() { *m = CreateItemFromSnapshotResponse{} } -func (m *CreateItemFromSnapshotResponse) String() string { return proto.CompactTextString(m) } -func (*CreateItemFromSnapshotResponse) ProtoMessage() {} +func (x *CreateItemFromSnapshotResponse) Reset() { + *x = CreateItemFromSnapshotResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateItemFromSnapshotResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateItemFromSnapshotResponse) ProtoMessage() {} + +func (x *CreateItemFromSnapshotResponse) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateItemFromSnapshotResponse.ProtoReflect.Descriptor instead. func (*CreateItemFromSnapshotResponse) Descriptor() ([]byte, []int) { - return fileDescriptor1, []int{10} + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{10} } -func (m *CreateItemFromSnapshotResponse) GetItem() []byte { - if m != nil { - return m.Item +func (x *CreateItemFromSnapshotResponse) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *CreateItemFromSnapshotResponse) GetAdditionalItems() []*ResourceIdentifier { - if m != nil { - return m.AdditionalItems +func (x *CreateItemFromSnapshotResponse) GetAdditionalItems() []*ResourceIdentifier { + if x != nil { + return x.AdditionalItems } return nil } -func (m *CreateItemFromSnapshotResponse) GetSkipRestore() bool { - if m != nil { - return m.SkipRestore +func (x *CreateItemFromSnapshotResponse) GetSkipRestore() bool { + if x != nil { + return x.SkipRestore } return false } type ItemSnapshotterInitRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Config map[string]string `protobuf:"bytes,2,rep,name=config" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (m *ItemSnapshotterInitRequest) Reset() { *m = ItemSnapshotterInitRequest{} } -func (m *ItemSnapshotterInitRequest) String() string { return proto.CompactTextString(m) } -func (*ItemSnapshotterInitRequest) ProtoMessage() {} -func (*ItemSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{11} } +func (x *ItemSnapshotterInitRequest) Reset() { + *x = ItemSnapshotterInitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ItemSnapshotter_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemSnapshotterInitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemSnapshotterInitRequest) ProtoMessage() {} -func (m *ItemSnapshotterInitRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ItemSnapshotterInitRequest) ProtoReflect() protoreflect.Message { + mi := &file_ItemSnapshotter_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemSnapshotterInitRequest.ProtoReflect.Descriptor instead. +func (*ItemSnapshotterInitRequest) Descriptor() ([]byte, []int) { + return file_ItemSnapshotter_proto_rawDescGZIP(), []int{11} +} + +func (x *ItemSnapshotterInitRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ItemSnapshotterInitRequest) GetConfig() map[string]string { - if m != nil { - return m.Config +func (x *ItemSnapshotterInitRequest) GetConfig() map[string]string { + if x != nil { + return x.Config } return nil } -func init() { - proto.RegisterType((*ItemSnapshotterAppliesToRequest)(nil), "generated.ItemSnapshotterAppliesToRequest") - proto.RegisterType((*ItemSnapshotterAppliesToResponse)(nil), "generated.ItemSnapshotterAppliesToResponse") - proto.RegisterType((*AlsoHandlesRequest)(nil), "generated.AlsoHandlesRequest") - proto.RegisterType((*AlsoHandlesResponse)(nil), "generated.AlsoHandlesResponse") - proto.RegisterType((*SnapshotItemRequest)(nil), "generated.SnapshotItemRequest") - proto.RegisterType((*SnapshotItemResponse)(nil), "generated.SnapshotItemResponse") - proto.RegisterType((*ProgressRequest)(nil), "generated.ProgressRequest") - proto.RegisterType((*ProgressResponse)(nil), "generated.ProgressResponse") - proto.RegisterType((*DeleteItemSnapshotRequest)(nil), "generated.DeleteItemSnapshotRequest") - proto.RegisterType((*CreateItemFromSnapshotRequest)(nil), "generated.CreateItemFromSnapshotRequest") - proto.RegisterType((*CreateItemFromSnapshotResponse)(nil), "generated.CreateItemFromSnapshotResponse") - proto.RegisterType((*ItemSnapshotterInitRequest)(nil), "generated.ItemSnapshotterInitRequest") +var File_ItemSnapshotter_proto protoreflect.FileDescriptor + +var file_ItemSnapshotter_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x39, 0x0a, 0x1f, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x74, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0x6b, 0x0a, 0x20, 0x49, + 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x58, 0x0a, 0x12, 0x41, 0x6c, 0x73, 0x6f, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x22, 0x58, 0x0a, 0x13, 0x41, 0x6c, 0x73, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0c, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x22, 0xd8, 0x01, 0x0a, + 0x13, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x12, 0x42, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x1a, 0x39, 0x0a, 0x0b, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfe, 0x02, 0x0a, 0x14, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x49, 0x44, 0x12, 0x61, 0x0a, 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, + 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, + 0x12, 0x41, 0x0a, 0x0c, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0c, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x49, 0x74, + 0x65, 0x6d, 0x73, 0x1a, 0x43, 0x0a, 0x15, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x98, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x22, 0x84, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x26, + 0x0a, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x54, + 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6e, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x18, 0x0a, 0x07, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x4e, 0x61, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x8d, 0x03, 0x0a, 0x19, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, + 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x46, 0x72, + 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x4e, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x48, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x53, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x39, 0x0a, 0x0b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe7, 0x03, 0x0a, 0x1d, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x74, 0x65, 0x6d, + 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x12, 0x6a, 0x0a, 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, 0x0a, 0x06, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, + 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x43, 0x0a, 0x15, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9f, 0x01, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, + 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x47, 0x0a, 0x0f, 0x61, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, + 0x74, 0x65, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x49, 0x74, 0x65, 0x6d, 0x53, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x49, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x32, 0xd5, 0x04, 0x0a, 0x0f, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, + 0x25, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, + 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x64, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, + 0x69, 0x65, 0x73, 0x54, 0x6f, 0x12, 0x2a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, + 0x72, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x74, + 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, + 0x0a, 0x0b, 0x41, 0x6c, 0x73, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x41, 0x6c, 0x73, 0x6f, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x41, 0x6c, 0x73, 0x6f, 0x48, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, + 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, + 0x08, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x12, 0x24, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x6d, 0x0a, 0x16, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x28, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, + 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, + 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ItemSnapshotter_proto_rawDescOnce sync.Once + file_ItemSnapshotter_proto_rawDescData = file_ItemSnapshotter_proto_rawDesc +) + +func file_ItemSnapshotter_proto_rawDescGZIP() []byte { + file_ItemSnapshotter_proto_rawDescOnce.Do(func() { + file_ItemSnapshotter_proto_rawDescData = protoimpl.X.CompressGZIP(file_ItemSnapshotter_proto_rawDescData) + }) + return file_ItemSnapshotter_proto_rawDescData +} + +var file_ItemSnapshotter_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_ItemSnapshotter_proto_goTypes = []interface{}{ + (*ItemSnapshotterAppliesToRequest)(nil), // 0: generated.ItemSnapshotterAppliesToRequest + (*ItemSnapshotterAppliesToResponse)(nil), // 1: generated.ItemSnapshotterAppliesToResponse + (*AlsoHandlesRequest)(nil), // 2: generated.AlsoHandlesRequest + (*AlsoHandlesResponse)(nil), // 3: generated.AlsoHandlesResponse + (*SnapshotItemRequest)(nil), // 4: generated.SnapshotItemRequest + (*SnapshotItemResponse)(nil), // 5: generated.SnapshotItemResponse + (*ProgressRequest)(nil), // 6: generated.ProgressRequest + (*ProgressResponse)(nil), // 7: generated.ProgressResponse + (*DeleteItemSnapshotRequest)(nil), // 8: generated.DeleteItemSnapshotRequest + (*CreateItemFromSnapshotRequest)(nil), // 9: generated.CreateItemFromSnapshotRequest + (*CreateItemFromSnapshotResponse)(nil), // 10: generated.CreateItemFromSnapshotResponse + (*ItemSnapshotterInitRequest)(nil), // 11: generated.ItemSnapshotterInitRequest + nil, // 12: generated.SnapshotItemRequest.ParamsEntry + nil, // 13: generated.SnapshotItemResponse.SnapshotMetadataEntry + nil, // 14: generated.DeleteItemSnapshotRequest.MetadataEntry + nil, // 15: generated.DeleteItemSnapshotRequest.ParamsEntry + nil, // 16: generated.CreateItemFromSnapshotRequest.SnapshotMetadataEntry + nil, // 17: generated.CreateItemFromSnapshotRequest.ParamsEntry + nil, // 18: generated.ItemSnapshotterInitRequest.ConfigEntry + (*ResourceSelector)(nil), // 19: generated.ResourceSelector + (*ResourceIdentifier)(nil), // 20: generated.ResourceIdentifier + (*Empty)(nil), // 21: generated.Empty +} +var file_ItemSnapshotter_proto_depIdxs = []int32{ + 19, // 0: generated.ItemSnapshotterAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 20, // 1: generated.AlsoHandlesResponse.handledItems:type_name -> generated.ResourceIdentifier + 12, // 2: generated.SnapshotItemRequest.params:type_name -> generated.SnapshotItemRequest.ParamsEntry + 13, // 3: generated.SnapshotItemResponse.snapshotMetadata:type_name -> generated.SnapshotItemResponse.SnapshotMetadataEntry + 20, // 4: generated.SnapshotItemResponse.additionalItems:type_name -> generated.ResourceIdentifier + 20, // 5: generated.SnapshotItemResponse.handledItems:type_name -> generated.ResourceIdentifier + 20, // 6: generated.ProgressRequest.itemID:type_name -> generated.ResourceIdentifier + 14, // 7: generated.DeleteItemSnapshotRequest.metadata:type_name -> generated.DeleteItemSnapshotRequest.MetadataEntry + 15, // 8: generated.DeleteItemSnapshotRequest.params:type_name -> generated.DeleteItemSnapshotRequest.ParamsEntry + 16, // 9: generated.CreateItemFromSnapshotRequest.snapshotMetadata:type_name -> generated.CreateItemFromSnapshotRequest.SnapshotMetadataEntry + 17, // 10: generated.CreateItemFromSnapshotRequest.params:type_name -> generated.CreateItemFromSnapshotRequest.ParamsEntry + 20, // 11: generated.CreateItemFromSnapshotResponse.additionalItems:type_name -> generated.ResourceIdentifier + 18, // 12: generated.ItemSnapshotterInitRequest.config:type_name -> generated.ItemSnapshotterInitRequest.ConfigEntry + 11, // 13: generated.ItemSnapshotter.Init:input_type -> generated.ItemSnapshotterInitRequest + 0, // 14: generated.ItemSnapshotter.AppliesTo:input_type -> generated.ItemSnapshotterAppliesToRequest + 2, // 15: generated.ItemSnapshotter.AlsoHandles:input_type -> generated.AlsoHandlesRequest + 4, // 16: generated.ItemSnapshotter.SnapshotItem:input_type -> generated.SnapshotItemRequest + 6, // 17: generated.ItemSnapshotter.Progress:input_type -> generated.ProgressRequest + 8, // 18: generated.ItemSnapshotter.DeleteSnapshot:input_type -> generated.DeleteItemSnapshotRequest + 9, // 19: generated.ItemSnapshotter.CreateItemFromSnapshot:input_type -> generated.CreateItemFromSnapshotRequest + 21, // 20: generated.ItemSnapshotter.Init:output_type -> generated.Empty + 1, // 21: generated.ItemSnapshotter.AppliesTo:output_type -> generated.ItemSnapshotterAppliesToResponse + 3, // 22: generated.ItemSnapshotter.AlsoHandles:output_type -> generated.AlsoHandlesResponse + 5, // 23: generated.ItemSnapshotter.SnapshotItem:output_type -> generated.SnapshotItemResponse + 7, // 24: generated.ItemSnapshotter.Progress:output_type -> generated.ProgressResponse + 21, // 25: generated.ItemSnapshotter.DeleteSnapshot:output_type -> generated.Empty + 10, // 26: generated.ItemSnapshotter.CreateItemFromSnapshot:output_type -> generated.CreateItemFromSnapshotResponse + 20, // [20:27] is the sub-list for method output_type + 13, // [13:20] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_ItemSnapshotter_proto_init() } +func file_ItemSnapshotter_proto_init() { + if File_ItemSnapshotter_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_ItemSnapshotter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemSnapshotterAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemSnapshotterAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlsoHandlesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlsoHandlesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SnapshotItemRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SnapshotItemResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProgressRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProgressResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteItemSnapshotRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateItemFromSnapshotRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateItemFromSnapshotResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ItemSnapshotter_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemSnapshotterInitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ItemSnapshotter_proto_rawDesc, + NumEnums: 0, + NumMessages: 19, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ItemSnapshotter_proto_goTypes, + DependencyIndexes: file_ItemSnapshotter_proto_depIdxs, + MessageInfos: file_ItemSnapshotter_proto_msgTypes, + }.Build() + File_ItemSnapshotter_proto = out.File + file_ItemSnapshotter_proto_rawDesc = nil + file_ItemSnapshotter_proto_goTypes = nil + file_ItemSnapshotter_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for ItemSnapshotter service +const _ = grpc.SupportPackageIsVersion6 +// ItemSnapshotterClient is the client API for ItemSnapshotter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ItemSnapshotterClient interface { Init(ctx context.Context, in *ItemSnapshotterInitRequest, opts ...grpc.CallOption) (*Empty, error) AppliesTo(ctx context.Context, in *ItemSnapshotterAppliesToRequest, opts ...grpc.CallOption) (*ItemSnapshotterAppliesToResponse, error) @@ -507,16 +1328,16 @@ type ItemSnapshotterClient interface { } type itemSnapshotterClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewItemSnapshotterClient(cc *grpc.ClientConn) ItemSnapshotterClient { +func NewItemSnapshotterClient(cc grpc.ClientConnInterface) ItemSnapshotterClient { return &itemSnapshotterClient{cc} } func (c *itemSnapshotterClient) Init(ctx context.Context, in *ItemSnapshotterInitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/Init", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/Init", in, out, opts...) if err != nil { return nil, err } @@ -525,7 +1346,7 @@ func (c *itemSnapshotterClient) Init(ctx context.Context, in *ItemSnapshotterIni func (c *itemSnapshotterClient) AppliesTo(ctx context.Context, in *ItemSnapshotterAppliesToRequest, opts ...grpc.CallOption) (*ItemSnapshotterAppliesToResponse, error) { out := new(ItemSnapshotterAppliesToResponse) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/AppliesTo", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/AppliesTo", in, out, opts...) if err != nil { return nil, err } @@ -534,7 +1355,7 @@ func (c *itemSnapshotterClient) AppliesTo(ctx context.Context, in *ItemSnapshott func (c *itemSnapshotterClient) AlsoHandles(ctx context.Context, in *AlsoHandlesRequest, opts ...grpc.CallOption) (*AlsoHandlesResponse, error) { out := new(AlsoHandlesResponse) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/AlsoHandles", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/AlsoHandles", in, out, opts...) if err != nil { return nil, err } @@ -543,7 +1364,7 @@ func (c *itemSnapshotterClient) AlsoHandles(ctx context.Context, in *AlsoHandles func (c *itemSnapshotterClient) SnapshotItem(ctx context.Context, in *SnapshotItemRequest, opts ...grpc.CallOption) (*SnapshotItemResponse, error) { out := new(SnapshotItemResponse) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/SnapshotItem", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/SnapshotItem", in, out, opts...) if err != nil { return nil, err } @@ -552,7 +1373,7 @@ func (c *itemSnapshotterClient) SnapshotItem(ctx context.Context, in *SnapshotIt func (c *itemSnapshotterClient) Progress(ctx context.Context, in *ProgressRequest, opts ...grpc.CallOption) (*ProgressResponse, error) { out := new(ProgressResponse) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/Progress", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/Progress", in, out, opts...) if err != nil { return nil, err } @@ -561,7 +1382,7 @@ func (c *itemSnapshotterClient) Progress(ctx context.Context, in *ProgressReques func (c *itemSnapshotterClient) DeleteSnapshot(ctx context.Context, in *DeleteItemSnapshotRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/DeleteSnapshot", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/DeleteSnapshot", in, out, opts...) if err != nil { return nil, err } @@ -570,15 +1391,14 @@ func (c *itemSnapshotterClient) DeleteSnapshot(ctx context.Context, in *DeleteIt func (c *itemSnapshotterClient) CreateItemFromSnapshot(ctx context.Context, in *CreateItemFromSnapshotRequest, opts ...grpc.CallOption) (*CreateItemFromSnapshotResponse, error) { out := new(CreateItemFromSnapshotResponse) - err := grpc.Invoke(ctx, "/generated.ItemSnapshotter/CreateItemFromSnapshot", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ItemSnapshotter/CreateItemFromSnapshot", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for ItemSnapshotter service - +// ItemSnapshotterServer is the server API for ItemSnapshotter service. type ItemSnapshotterServer interface { Init(context.Context, *ItemSnapshotterInitRequest) (*Empty, error) AppliesTo(context.Context, *ItemSnapshotterAppliesToRequest) (*ItemSnapshotterAppliesToResponse, error) @@ -589,6 +1409,32 @@ type ItemSnapshotterServer interface { CreateItemFromSnapshot(context.Context, *CreateItemFromSnapshotRequest) (*CreateItemFromSnapshotResponse, error) } +// UnimplementedItemSnapshotterServer can be embedded to have forward compatible implementations. +type UnimplementedItemSnapshotterServer struct { +} + +func (*UnimplementedItemSnapshotterServer) Init(context.Context, *ItemSnapshotterInitRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") +} +func (*UnimplementedItemSnapshotterServer) AppliesTo(context.Context, *ItemSnapshotterAppliesToRequest) (*ItemSnapshotterAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (*UnimplementedItemSnapshotterServer) AlsoHandles(context.Context, *AlsoHandlesRequest) (*AlsoHandlesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AlsoHandles not implemented") +} +func (*UnimplementedItemSnapshotterServer) SnapshotItem(context.Context, *SnapshotItemRequest) (*SnapshotItemResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SnapshotItem not implemented") +} +func (*UnimplementedItemSnapshotterServer) Progress(context.Context, *ProgressRequest) (*ProgressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Progress not implemented") +} +func (*UnimplementedItemSnapshotterServer) DeleteSnapshot(context.Context, *DeleteItemSnapshotRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteSnapshot not implemented") +} +func (*UnimplementedItemSnapshotterServer) CreateItemFromSnapshot(context.Context, *CreateItemFromSnapshotRequest) (*CreateItemFromSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateItemFromSnapshot not implemented") +} + func RegisterItemSnapshotterServer(s *grpc.Server, srv ItemSnapshotterServer) { s.RegisterService(&_ItemSnapshotter_serviceDesc, srv) } @@ -755,65 +1601,3 @@ var _ItemSnapshotter_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "ItemSnapshotter.proto", } - -func init() { proto.RegisterFile("ItemSnapshotter.proto", fileDescriptor1) } - -var fileDescriptor1 = []byte{ - // 887 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x5f, 0x8f, 0xdb, 0x44, - 0x10, 0x97, 0xe3, 0x5c, 0xee, 0x6e, 0x12, 0x7a, 0xd1, 0xf6, 0x5a, 0x19, 0x57, 0xbd, 0x46, 0x16, - 0xa0, 0x50, 0xa4, 0x08, 0x0e, 0x2a, 0x51, 0x90, 0x40, 0xd7, 0xb4, 0xf4, 0x4e, 0x2a, 0xa5, 0xf2, - 0xf5, 0xa1, 0xaf, 0xdb, 0x78, 0x9b, 0x98, 0xd8, 0x5e, 0xb3, 0xbb, 0x41, 0xba, 0x77, 0x5e, 0x79, - 0xe7, 0x8d, 0xef, 0xc1, 0xf7, 0x40, 0xe2, 0x1b, 0xf0, 0x2d, 0x10, 0xda, 0x3f, 0x0e, 0x1b, 0xc7, - 0x3e, 0xfb, 0xee, 0x78, 0xf3, 0xce, 0xce, 0xcc, 0xfe, 0x66, 0x7e, 0xb3, 0xb3, 0x63, 0xb8, 0x73, - 0x26, 0x48, 0x7a, 0x9e, 0xe1, 0x9c, 0x2f, 0xa8, 0x10, 0x84, 0x4d, 0x72, 0x46, 0x05, 0x45, 0xfb, - 0x73, 0x92, 0x11, 0x86, 0x05, 0x89, 0xfc, 0xc1, 0xf9, 0x02, 0x33, 0x12, 0xe9, 0x8d, 0xe0, 0x31, - 0x3c, 0x28, 0x59, 0x9c, 0xe4, 0x79, 0x12, 0x13, 0xfe, 0x9a, 0x86, 0xe4, 0xa7, 0x15, 0xe1, 0x02, - 0xdd, 0x85, 0x5e, 0x9e, 0xac, 0xe6, 0x71, 0xe6, 0x39, 0x23, 0x67, 0xbc, 0x1f, 0x9a, 0x55, 0xb0, - 0x84, 0x51, 0xbd, 0x29, 0xcf, 0x69, 0xc6, 0x09, 0x7a, 0x0e, 0xc3, 0x90, 0x70, 0xba, 0x62, 0x33, - 0x72, 0x4e, 0x12, 0x32, 0x13, 0x94, 0x29, 0x2f, 0xfd, 0xe3, 0x7b, 0x93, 0x35, 0xa4, 0x49, 0x59, - 0x25, 0xdc, 0x32, 0x0a, 0xde, 0x00, 0x3a, 0x49, 0x38, 0x3d, 0xc5, 0x59, 0x94, 0x10, 0xde, 0x00, - 0x0d, 0x21, 0xe8, 0xc6, 0x82, 0xa4, 0x5e, 0x67, 0xe4, 0x8c, 0x07, 0xa1, 0xfa, 0x96, 0xba, 0x6f, - 0xf1, 0x6c, 0xb9, 0xca, 0x3d, 0x57, 0x49, 0xcd, 0x2a, 0x78, 0x03, 0xb7, 0x37, 0x3c, 0x1b, 0xe4, - 0x27, 0x30, 0x58, 0x28, 0x51, 0x24, 0x83, 0xe4, 0x9e, 0x33, 0x72, 0xc7, 0xfd, 0xe3, 0xfb, 0x15, - 0xa8, 0xcf, 0x22, 0x92, 0x89, 0xf8, 0x5d, 0x4c, 0x58, 0xb8, 0x61, 0x12, 0xfc, 0xe5, 0xc0, 0xed, - 0x22, 0x3b, 0x52, 0x72, 0x1d, 0xd4, 0x4f, 0xa0, 0x97, 0x63, 0x86, 0x53, 0xee, 0xb9, 0x0a, 0xc0, - 0x43, 0x0b, 0x40, 0x85, 0xef, 0xc9, 0x2b, 0xa5, 0xfc, 0x2c, 0x13, 0xec, 0x22, 0x34, 0x96, 0x56, - 0xe4, 0x5d, 0x3b, 0x72, 0xff, 0x31, 0xf4, 0x2d, 0x75, 0x34, 0x04, 0x77, 0x49, 0x2e, 0x0c, 0x26, - 0xf9, 0x89, 0x0e, 0x61, 0xe7, 0x67, 0x9c, 0xac, 0x88, 0x42, 0xb4, 0x1f, 0xea, 0xc5, 0x57, 0x9d, - 0x2f, 0x9d, 0xe0, 0x9f, 0x0e, 0x1c, 0x6e, 0x1e, 0x6f, 0xd2, 0x56, 0xc4, 0xe0, 0x58, 0x31, 0x1c, - 0x01, 0xf0, 0x42, 0xf7, 0xa9, 0xf1, 0x65, 0x49, 0x10, 0x86, 0x61, 0xb1, 0xfa, 0x9e, 0x08, 0x1c, - 0x61, 0x81, 0x4d, 0xb4, 0x8f, 0x6a, 0xa3, 0xd5, 0xc7, 0xad, 0x85, 0x85, 0x9d, 0x0e, 0x7c, 0xcb, - 0x1d, 0x7a, 0x0e, 0x07, 0x38, 0x8a, 0x62, 0x11, 0xd3, 0x0c, 0x27, 0x9a, 0xd0, 0x6e, 0x1b, 0x42, - 0xcb, 0x56, 0x5b, 0x65, 0xb1, 0x73, 0xe5, 0xb2, 0xf0, 0xa7, 0x70, 0xa7, 0x12, 0xf6, 0x95, 0x08, - 0xf8, 0xcd, 0x81, 0x83, 0x57, 0x8c, 0xce, 0x19, 0xe1, 0x8d, 0xb7, 0xe1, 0x11, 0xf4, 0x24, 0x0f, - 0x26, 0xf7, 0x8d, 0x68, 0x8d, 0x72, 0x89, 0x36, 0x77, 0x8b, 0xb6, 0x9a, 0xb2, 0x0a, 0x7e, 0xe9, - 0xc0, 0xf0, 0x3f, 0x68, 0xa6, 0x2e, 0x0e, 0x61, 0x27, 0x5f, 0x60, 0x4e, 0x0c, 0x34, 0xbd, 0x40, - 0x1f, 0xc1, 0x2d, 0x79, 0x18, 0x9f, 0xd2, 0x34, 0x4f, 0x88, 0x20, 0x91, 0x42, 0xe8, 0x86, 0x25, - 0x29, 0x1a, 0xc3, 0x81, 0x92, 0xbc, 0xa6, 0x85, 0x4c, 0xe1, 0x71, 0xc3, 0xb2, 0x18, 0x79, 0xb0, - 0xcb, 0x05, 0x66, 0xd2, 0x55, 0x57, 0x69, 0x14, 0x4b, 0x34, 0x82, 0xbe, 0xf9, 0x7c, 0x89, 0x33, - 0xea, 0xed, 0xa8, 0x5d, 0x5b, 0x24, 0x6d, 0x57, 0x79, 0x24, 0xd3, 0xe2, 0xf5, 0xb4, 0xad, 0x59, - 0x4a, 0x5b, 0xf3, 0xa9, 0x6c, 0x77, 0xb5, 0xad, 0x25, 0x92, 0xdc, 0x11, 0xc6, 0xbc, 0x3d, 0xcd, - 0x1d, 0x61, 0x2c, 0xf8, 0xd5, 0x85, 0xf7, 0x9f, 0x12, 0x09, 0xca, 0xee, 0x92, 0x4d, 0x5c, 0x35, - 0xdd, 0x15, 0x93, 0xb1, 0xef, 0x18, 0x4d, 0x9f, 0xd8, 0xdd, 0xac, 0x24, 0x45, 0x2f, 0x61, 0x2f, - 0x2d, 0xee, 0x92, 0xae, 0xf4, 0x63, 0x8b, 0xf5, 0x5a, 0x5c, 0x93, 0xcd, 0x8b, 0xb4, 0xf6, 0x81, - 0x4e, 0xd7, 0x7d, 0x48, 0x57, 0xfc, 0xa7, 0xad, 0xbc, 0x55, 0x74, 0x23, 0xff, 0x6b, 0x78, 0xef, - 0xda, 0x65, 0x7f, 0x93, 0x96, 0xf5, 0xb7, 0x0b, 0xf7, 0xa7, 0x8c, 0x60, 0x8d, 0x54, 0xa6, 0xaa, - 0x2d, 0x27, 0x55, 0x7d, 0xb9, 0xe9, 0x72, 0x6c, 0xf3, 0xd4, 0xad, 0xe4, 0xe9, 0xc7, 0x8a, 0xde, - 0xa7, 0x33, 0xfc, 0x8d, 0x95, 0xe1, 0x4b, 0x71, 0xb7, 0x6e, 0x82, 0x2f, 0xd6, 0x1c, 0xf6, 0xd4, - 0x09, 0x5f, 0xb4, 0x3e, 0xa1, 0xea, 0x55, 0xf1, 0x60, 0x97, 0x11, 0x2e, 0x28, 0x23, 0xea, 0x3e, - 0x0c, 0xc2, 0x62, 0xf9, 0xbf, 0x34, 0xb8, 0x9b, 0x30, 0xfd, 0xbb, 0x03, 0x47, 0x75, 0xf1, 0x5c, - 0xf2, 0x4c, 0x55, 0xbc, 0x11, 0x9d, 0x6b, 0xbd, 0x11, 0xb2, 0xd3, 0x2c, 0xe3, 0x3c, 0x34, 0xd9, - 0x91, 0xc5, 0xb1, 0x17, 0xda, 0xa2, 0xe0, 0x0f, 0x07, 0xfc, 0xd2, 0xec, 0x74, 0x96, 0xc5, 0x8d, - 0x85, 0x78, 0x06, 0xbd, 0x19, 0xcd, 0xde, 0xc5, 0x73, 0x03, 0xec, 0x33, 0x0b, 0x58, 0xbd, 0xbb, - 0xc9, 0x54, 0xd9, 0x18, 0xf6, 0xb4, 0x03, 0x99, 0x5e, 0x4b, 0x7c, 0x95, 0xf4, 0x1e, 0xff, 0xd9, - 0x85, 0x83, 0xd2, 0x69, 0xe8, 0x5b, 0xe8, 0xca, 0x13, 0xd1, 0x87, 0xad, 0x10, 0xf9, 0x43, 0x4b, - 0xed, 0x59, 0x9a, 0x8b, 0x0b, 0x14, 0xc1, 0xfe, 0x7a, 0x7a, 0x44, 0x0f, 0xeb, 0xbd, 0x94, 0xa7, - 0x53, 0xff, 0x93, 0x56, 0xba, 0x86, 0xf6, 0x17, 0xd0, 0xb7, 0x66, 0x3d, 0x64, 0x13, 0xbb, 0x3d, - 0x5d, 0xfa, 0x47, 0x75, 0xdb, 0xc6, 0xdb, 0x0f, 0x30, 0xb0, 0x87, 0x12, 0x74, 0x74, 0xf9, 0x6c, - 0xe6, 0x3f, 0x68, 0x98, 0x66, 0xd0, 0x14, 0xf6, 0x8a, 0x87, 0x13, 0xf9, 0x96, 0x72, 0xe9, 0xa1, - 0xf7, 0xef, 0x55, 0xee, 0x19, 0x27, 0xa7, 0x70, 0x4b, 0x37, 0xe4, 0xe2, 0x08, 0xf4, 0x41, 0x9b, - 0x5e, 0x5d, 0xc1, 0x49, 0x0a, 0x77, 0xab, 0xaf, 0x11, 0x1a, 0xb7, 0xed, 0x1c, 0xfe, 0xc7, 0x2d, - 0x34, 0x35, 0xf0, 0xb7, 0x3d, 0xf5, 0x47, 0xf2, 0xf9, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x51, - 0xd9, 0x74, 0xbb, 0xc3, 0x0c, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/ObjectStore.pb.go b/pkg/plugin/generated/ObjectStore.pb.go index 121d70d456..837f65b33e 100644 --- a/pkg/plugin/generated/ObjectStore.pb.go +++ b/pkg/plugin/generated/ObjectStore.pb.go @@ -1,400 +1,1159 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: ObjectStore.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type PutObjectRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` } -func (m *PutObjectRequest) Reset() { *m = PutObjectRequest{} } -func (m *PutObjectRequest) String() string { return proto.CompactTextString(m) } -func (*PutObjectRequest) ProtoMessage() {} -func (*PutObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } +func (x *PutObjectRequest) Reset() { + *x = PutObjectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PutObjectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *PutObjectRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (*PutObjectRequest) ProtoMessage() {} + +func (x *PutObjectRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PutObjectRequest.ProtoReflect.Descriptor instead. +func (*PutObjectRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{0} +} + +func (x *PutObjectRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *PutObjectRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *PutObjectRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *PutObjectRequest) GetKey() string { - if m != nil { - return m.Key +func (x *PutObjectRequest) GetKey() string { + if x != nil { + return x.Key } return "" } -func (m *PutObjectRequest) GetBody() []byte { - if m != nil { - return m.Body +func (x *PutObjectRequest) GetBody() []byte { + if x != nil { + return x.Body } return nil } type ObjectExistsRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *ObjectExistsRequest) Reset() { + *x = ObjectExistsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ObjectExistsRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ObjectExistsRequest) Reset() { *m = ObjectExistsRequest{} } -func (m *ObjectExistsRequest) String() string { return proto.CompactTextString(m) } -func (*ObjectExistsRequest) ProtoMessage() {} -func (*ObjectExistsRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{1} } +func (*ObjectExistsRequest) ProtoMessage() {} -func (m *ObjectExistsRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ObjectExistsRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectExistsRequest.ProtoReflect.Descriptor instead. +func (*ObjectExistsRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{1} +} + +func (x *ObjectExistsRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ObjectExistsRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *ObjectExistsRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *ObjectExistsRequest) GetKey() string { - if m != nil { - return m.Key +func (x *ObjectExistsRequest) GetKey() string { + if x != nil { + return x.Key } return "" } type ObjectExistsResponse struct { - Exists bool `protobuf:"varint,1,opt,name=exists" json:"exists,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exists bool `protobuf:"varint,1,opt,name=exists,proto3" json:"exists,omitempty"` +} + +func (x *ObjectExistsResponse) Reset() { + *x = ObjectExistsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ObjectExistsResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ObjectExistsResponse) Reset() { *m = ObjectExistsResponse{} } -func (m *ObjectExistsResponse) String() string { return proto.CompactTextString(m) } -func (*ObjectExistsResponse) ProtoMessage() {} -func (*ObjectExistsResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{2} } +func (*ObjectExistsResponse) ProtoMessage() {} -func (m *ObjectExistsResponse) GetExists() bool { - if m != nil { - return m.Exists +func (x *ObjectExistsResponse) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectExistsResponse.ProtoReflect.Descriptor instead. +func (*ObjectExistsResponse) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{2} +} + +func (x *ObjectExistsResponse) GetExists() bool { + if x != nil { + return x.Exists } return false } type GetObjectRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` } -func (m *GetObjectRequest) Reset() { *m = GetObjectRequest{} } -func (m *GetObjectRequest) String() string { return proto.CompactTextString(m) } -func (*GetObjectRequest) ProtoMessage() {} -func (*GetObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} } +func (x *GetObjectRequest) Reset() { + *x = GetObjectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetObjectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *GetObjectRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (*GetObjectRequest) ProtoMessage() {} + +func (x *GetObjectRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetObjectRequest.ProtoReflect.Descriptor instead. +func (*GetObjectRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{3} +} + +func (x *GetObjectRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *GetObjectRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *GetObjectRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *GetObjectRequest) GetKey() string { - if m != nil { - return m.Key +func (x *GetObjectRequest) GetKey() string { + if x != nil { + return x.Key } return "" } type Bytes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` } -func (m *Bytes) Reset() { *m = Bytes{} } -func (m *Bytes) String() string { return proto.CompactTextString(m) } -func (*Bytes) ProtoMessage() {} -func (*Bytes) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} } +func (x *Bytes) Reset() { + *x = Bytes{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bytes) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *Bytes) GetData() []byte { - if m != nil { - return m.Data +func (*Bytes) ProtoMessage() {} + +func (x *Bytes) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Bytes.ProtoReflect.Descriptor instead. +func (*Bytes) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{4} +} + +func (x *Bytes) GetData() []byte { + if x != nil { + return x.Data } return nil } type ListCommonPrefixesRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Delimiter string `protobuf:"bytes,3,opt,name=delimiter" json:"delimiter,omitempty"` - Prefix string `protobuf:"bytes,4,opt,name=prefix" json:"prefix,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Delimiter string `protobuf:"bytes,3,opt,name=delimiter,proto3" json:"delimiter,omitempty"` + Prefix string `protobuf:"bytes,4,opt,name=prefix,proto3" json:"prefix,omitempty"` } -func (m *ListCommonPrefixesRequest) Reset() { *m = ListCommonPrefixesRequest{} } -func (m *ListCommonPrefixesRequest) String() string { return proto.CompactTextString(m) } -func (*ListCommonPrefixesRequest) ProtoMessage() {} -func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} } +func (x *ListCommonPrefixesRequest) Reset() { + *x = ListCommonPrefixesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *ListCommonPrefixesRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ListCommonPrefixesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCommonPrefixesRequest) ProtoMessage() {} + +func (x *ListCommonPrefixesRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListCommonPrefixesRequest.ProtoReflect.Descriptor instead. +func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{5} +} + +func (x *ListCommonPrefixesRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ListCommonPrefixesRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *ListCommonPrefixesRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *ListCommonPrefixesRequest) GetDelimiter() string { - if m != nil { - return m.Delimiter +func (x *ListCommonPrefixesRequest) GetDelimiter() string { + if x != nil { + return x.Delimiter } return "" } -func (m *ListCommonPrefixesRequest) GetPrefix() string { - if m != nil { - return m.Prefix +func (x *ListCommonPrefixesRequest) GetPrefix() string { + if x != nil { + return x.Prefix } return "" } type ListCommonPrefixesResponse struct { - Prefixes []string `protobuf:"bytes,1,rep,name=prefixes" json:"prefixes,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefixes []string `protobuf:"bytes,1,rep,name=prefixes,proto3" json:"prefixes,omitempty"` +} + +func (x *ListCommonPrefixesResponse) Reset() { + *x = ListCommonPrefixesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListCommonPrefixesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCommonPrefixesResponse) ProtoMessage() {} + +func (x *ListCommonPrefixesResponse) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *ListCommonPrefixesResponse) Reset() { *m = ListCommonPrefixesResponse{} } -func (m *ListCommonPrefixesResponse) String() string { return proto.CompactTextString(m) } -func (*ListCommonPrefixesResponse) ProtoMessage() {} -func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} } +// Deprecated: Use ListCommonPrefixesResponse.ProtoReflect.Descriptor instead. +func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{6} +} -func (m *ListCommonPrefixesResponse) GetPrefixes() []string { - if m != nil { - return m.Prefixes +func (x *ListCommonPrefixesResponse) GetPrefixes() []string { + if x != nil { + return x.Prefixes } return nil } type ListObjectsRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Prefix string `protobuf:"bytes,3,opt,name=prefix" json:"prefix,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Prefix string `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix,omitempty"` } -func (m *ListObjectsRequest) Reset() { *m = ListObjectsRequest{} } -func (m *ListObjectsRequest) String() string { return proto.CompactTextString(m) } -func (*ListObjectsRequest) ProtoMessage() {} -func (*ListObjectsRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{7} } +func (x *ListObjectsRequest) Reset() { + *x = ListObjectsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListObjectsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *ListObjectsRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (*ListObjectsRequest) ProtoMessage() {} + +func (x *ListObjectsRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListObjectsRequest.ProtoReflect.Descriptor instead. +func (*ListObjectsRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{7} +} + +func (x *ListObjectsRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ListObjectsRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *ListObjectsRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *ListObjectsRequest) GetPrefix() string { - if m != nil { - return m.Prefix +func (x *ListObjectsRequest) GetPrefix() string { + if x != nil { + return x.Prefix } return "" } type ListObjectsResponse struct { - Keys []string `protobuf:"bytes,1,rep,name=keys" json:"keys,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` } -func (m *ListObjectsResponse) Reset() { *m = ListObjectsResponse{} } -func (m *ListObjectsResponse) String() string { return proto.CompactTextString(m) } -func (*ListObjectsResponse) ProtoMessage() {} -func (*ListObjectsResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{8} } +func (x *ListObjectsResponse) Reset() { + *x = ListObjectsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListObjectsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListObjectsResponse) ProtoMessage() {} + +func (x *ListObjectsResponse) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *ListObjectsResponse) GetKeys() []string { - if m != nil { - return m.Keys +// Deprecated: Use ListObjectsResponse.ProtoReflect.Descriptor instead. +func (*ListObjectsResponse) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{8} +} + +func (x *ListObjectsResponse) GetKeys() []string { + if x != nil { + return x.Keys } return nil } type DeleteObjectRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` } -func (m *DeleteObjectRequest) Reset() { *m = DeleteObjectRequest{} } -func (m *DeleteObjectRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteObjectRequest) ProtoMessage() {} -func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{9} } +func (x *DeleteObjectRequest) Reset() { + *x = DeleteObjectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteObjectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteObjectRequest) ProtoMessage() {} + +func (x *DeleteObjectRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteObjectRequest.ProtoReflect.Descriptor instead. +func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{9} +} -func (m *DeleteObjectRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *DeleteObjectRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *DeleteObjectRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *DeleteObjectRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *DeleteObjectRequest) GetKey() string { - if m != nil { - return m.Key +func (x *DeleteObjectRequest) GetKey() string { + if x != nil { + return x.Key } return "" } type CreateSignedURLRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` - Ttl int64 `protobuf:"varint,4,opt,name=ttl" json:"ttl,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + Ttl int64 `protobuf:"varint,4,opt,name=ttl,proto3" json:"ttl,omitempty"` +} + +func (x *CreateSignedURLRequest) Reset() { + *x = CreateSignedURLRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateSignedURLRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSignedURLRequest) ProtoMessage() {} + +func (x *CreateSignedURLRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *CreateSignedURLRequest) Reset() { *m = CreateSignedURLRequest{} } -func (m *CreateSignedURLRequest) String() string { return proto.CompactTextString(m) } -func (*CreateSignedURLRequest) ProtoMessage() {} -func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{10} } +// Deprecated: Use CreateSignedURLRequest.ProtoReflect.Descriptor instead. +func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{10} +} -func (m *CreateSignedURLRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *CreateSignedURLRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *CreateSignedURLRequest) GetBucket() string { - if m != nil { - return m.Bucket +func (x *CreateSignedURLRequest) GetBucket() string { + if x != nil { + return x.Bucket } return "" } -func (m *CreateSignedURLRequest) GetKey() string { - if m != nil { - return m.Key +func (x *CreateSignedURLRequest) GetKey() string { + if x != nil { + return x.Key } return "" } -func (m *CreateSignedURLRequest) GetTtl() int64 { - if m != nil { - return m.Ttl +func (x *CreateSignedURLRequest) GetTtl() int64 { + if x != nil { + return x.Ttl } return 0 } type CreateSignedURLResponse struct { - Url string `protobuf:"bytes,1,opt,name=url" json:"url,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` +} + +func (x *CreateSignedURLResponse) Reset() { + *x = CreateSignedURLResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *CreateSignedURLResponse) Reset() { *m = CreateSignedURLResponse{} } -func (m *CreateSignedURLResponse) String() string { return proto.CompactTextString(m) } -func (*CreateSignedURLResponse) ProtoMessage() {} -func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{11} } +func (x *CreateSignedURLResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *CreateSignedURLResponse) GetUrl() string { - if m != nil { - return m.Url +func (*CreateSignedURLResponse) ProtoMessage() {} + +func (x *CreateSignedURLResponse) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSignedURLResponse.ProtoReflect.Descriptor instead. +func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{11} +} + +func (x *CreateSignedURLResponse) GetUrl() string { + if x != nil { + return x.Url } return "" } type ObjectStoreInitRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Config map[string]string `protobuf:"bytes,2,rep,name=config" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ObjectStoreInitRequest) Reset() { + *x = ObjectStoreInitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ObjectStore_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ObjectStoreInitRequest) Reset() { *m = ObjectStoreInitRequest{} } -func (m *ObjectStoreInitRequest) String() string { return proto.CompactTextString(m) } -func (*ObjectStoreInitRequest) ProtoMessage() {} -func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{12} } +func (x *ObjectStoreInitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectStoreInitRequest) ProtoMessage() {} -func (m *ObjectStoreInitRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ObjectStoreInitRequest) ProtoReflect() protoreflect.Message { + mi := &file_ObjectStore_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectStoreInitRequest.ProtoReflect.Descriptor instead. +func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { + return file_ObjectStore_proto_rawDescGZIP(), []int{12} +} + +func (x *ObjectStoreInitRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ObjectStoreInitRequest) GetConfig() map[string]string { - if m != nil { - return m.Config +func (x *ObjectStoreInitRequest) GetConfig() map[string]string { + if x != nil { + return x.Config } return nil } -func init() { - proto.RegisterType((*PutObjectRequest)(nil), "generated.PutObjectRequest") - proto.RegisterType((*ObjectExistsRequest)(nil), "generated.ObjectExistsRequest") - proto.RegisterType((*ObjectExistsResponse)(nil), "generated.ObjectExistsResponse") - proto.RegisterType((*GetObjectRequest)(nil), "generated.GetObjectRequest") - proto.RegisterType((*Bytes)(nil), "generated.Bytes") - proto.RegisterType((*ListCommonPrefixesRequest)(nil), "generated.ListCommonPrefixesRequest") - proto.RegisterType((*ListCommonPrefixesResponse)(nil), "generated.ListCommonPrefixesResponse") - proto.RegisterType((*ListObjectsRequest)(nil), "generated.ListObjectsRequest") - proto.RegisterType((*ListObjectsResponse)(nil), "generated.ListObjectsResponse") - proto.RegisterType((*DeleteObjectRequest)(nil), "generated.DeleteObjectRequest") - proto.RegisterType((*CreateSignedURLRequest)(nil), "generated.CreateSignedURLRequest") - proto.RegisterType((*CreateSignedURLResponse)(nil), "generated.CreateSignedURLResponse") - proto.RegisterType((*ObjectStoreInitRequest)(nil), "generated.ObjectStoreInitRequest") +var File_ObjectStore_proto protoreflect.FileDescriptor + +var file_ObjectStore_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x0c, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x68, 0x0a, 0x10, + 0x50, 0x75, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x57, 0x0a, 0x13, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, + 0x2e, 0x0a, 0x14, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x22, + 0x54, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x1b, 0x0a, 0x05, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x81, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x38, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, + 0x22, 0x5c, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x29, + 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x57, 0x0a, 0x13, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x22, 0x6c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, + 0x22, 0x2b, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xb2, 0x01, + 0x0a, 0x16, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x69, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x12, 0x45, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x32, 0xe4, 0x04, 0x0a, 0x0b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x3c, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x50, 0x75, 0x74, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x4f, 0x0a, + 0x0c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x1e, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x12, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x65, 0x73, 0x12, 0x24, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4c, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1d, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, + 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1e, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x58, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, + 0x52, 0x4c, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, 0x52, + 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, + 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ObjectStore_proto_rawDescOnce sync.Once + file_ObjectStore_proto_rawDescData = file_ObjectStore_proto_rawDesc +) + +func file_ObjectStore_proto_rawDescGZIP() []byte { + file_ObjectStore_proto_rawDescOnce.Do(func() { + file_ObjectStore_proto_rawDescData = protoimpl.X.CompressGZIP(file_ObjectStore_proto_rawDescData) + }) + return file_ObjectStore_proto_rawDescData +} + +var file_ObjectStore_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_ObjectStore_proto_goTypes = []interface{}{ + (*PutObjectRequest)(nil), // 0: generated.PutObjectRequest + (*ObjectExistsRequest)(nil), // 1: generated.ObjectExistsRequest + (*ObjectExistsResponse)(nil), // 2: generated.ObjectExistsResponse + (*GetObjectRequest)(nil), // 3: generated.GetObjectRequest + (*Bytes)(nil), // 4: generated.Bytes + (*ListCommonPrefixesRequest)(nil), // 5: generated.ListCommonPrefixesRequest + (*ListCommonPrefixesResponse)(nil), // 6: generated.ListCommonPrefixesResponse + (*ListObjectsRequest)(nil), // 7: generated.ListObjectsRequest + (*ListObjectsResponse)(nil), // 8: generated.ListObjectsResponse + (*DeleteObjectRequest)(nil), // 9: generated.DeleteObjectRequest + (*CreateSignedURLRequest)(nil), // 10: generated.CreateSignedURLRequest + (*CreateSignedURLResponse)(nil), // 11: generated.CreateSignedURLResponse + (*ObjectStoreInitRequest)(nil), // 12: generated.ObjectStoreInitRequest + nil, // 13: generated.ObjectStoreInitRequest.ConfigEntry + (*Empty)(nil), // 14: generated.Empty +} +var file_ObjectStore_proto_depIdxs = []int32{ + 13, // 0: generated.ObjectStoreInitRequest.config:type_name -> generated.ObjectStoreInitRequest.ConfigEntry + 12, // 1: generated.ObjectStore.Init:input_type -> generated.ObjectStoreInitRequest + 0, // 2: generated.ObjectStore.PutObject:input_type -> generated.PutObjectRequest + 1, // 3: generated.ObjectStore.ObjectExists:input_type -> generated.ObjectExistsRequest + 3, // 4: generated.ObjectStore.GetObject:input_type -> generated.GetObjectRequest + 5, // 5: generated.ObjectStore.ListCommonPrefixes:input_type -> generated.ListCommonPrefixesRequest + 7, // 6: generated.ObjectStore.ListObjects:input_type -> generated.ListObjectsRequest + 9, // 7: generated.ObjectStore.DeleteObject:input_type -> generated.DeleteObjectRequest + 10, // 8: generated.ObjectStore.CreateSignedURL:input_type -> generated.CreateSignedURLRequest + 14, // 9: generated.ObjectStore.Init:output_type -> generated.Empty + 14, // 10: generated.ObjectStore.PutObject:output_type -> generated.Empty + 2, // 11: generated.ObjectStore.ObjectExists:output_type -> generated.ObjectExistsResponse + 4, // 12: generated.ObjectStore.GetObject:output_type -> generated.Bytes + 6, // 13: generated.ObjectStore.ListCommonPrefixes:output_type -> generated.ListCommonPrefixesResponse + 8, // 14: generated.ObjectStore.ListObjects:output_type -> generated.ListObjectsResponse + 14, // 15: generated.ObjectStore.DeleteObject:output_type -> generated.Empty + 11, // 16: generated.ObjectStore.CreateSignedURL:output_type -> generated.CreateSignedURLResponse + 9, // [9:17] is the sub-list for method output_type + 1, // [1:9] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ObjectStore_proto_init() } +func file_ObjectStore_proto_init() { + if File_ObjectStore_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_ObjectStore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PutObjectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ObjectExistsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ObjectExistsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetObjectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bytes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListCommonPrefixesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListCommonPrefixesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListObjectsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListObjectsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteObjectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSignedURLRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSignedURLResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ObjectStore_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ObjectStoreInitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ObjectStore_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ObjectStore_proto_goTypes, + DependencyIndexes: file_ObjectStore_proto_depIdxs, + MessageInfos: file_ObjectStore_proto_msgTypes, + }.Build() + File_ObjectStore_proto = out.File + file_ObjectStore_proto_rawDesc = nil + file_ObjectStore_proto_goTypes = nil + file_ObjectStore_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for ObjectStore service +const _ = grpc.SupportPackageIsVersion6 +// ObjectStoreClient is the client API for ObjectStore service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ObjectStoreClient interface { Init(ctx context.Context, in *ObjectStoreInitRequest, opts ...grpc.CallOption) (*Empty, error) PutObject(ctx context.Context, opts ...grpc.CallOption) (ObjectStore_PutObjectClient, error) @@ -407,16 +1166,16 @@ type ObjectStoreClient interface { } type objectStoreClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewObjectStoreClient(cc *grpc.ClientConn) ObjectStoreClient { +func NewObjectStoreClient(cc grpc.ClientConnInterface) ObjectStoreClient { return &objectStoreClient{cc} } func (c *objectStoreClient) Init(ctx context.Context, in *ObjectStoreInitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.ObjectStore/Init", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/Init", in, out, opts...) if err != nil { return nil, err } @@ -424,7 +1183,7 @@ func (c *objectStoreClient) Init(ctx context.Context, in *ObjectStoreInitRequest } func (c *objectStoreClient) PutObject(ctx context.Context, opts ...grpc.CallOption) (ObjectStore_PutObjectClient, error) { - stream, err := grpc.NewClientStream(ctx, &_ObjectStore_serviceDesc.Streams[0], c.cc, "/generated.ObjectStore/PutObject", opts...) + stream, err := c.cc.NewStream(ctx, &_ObjectStore_serviceDesc.Streams[0], "/generated.ObjectStore/PutObject", opts...) if err != nil { return nil, err } @@ -459,7 +1218,7 @@ func (x *objectStorePutObjectClient) CloseAndRecv() (*Empty, error) { func (c *objectStoreClient) ObjectExists(ctx context.Context, in *ObjectExistsRequest, opts ...grpc.CallOption) (*ObjectExistsResponse, error) { out := new(ObjectExistsResponse) - err := grpc.Invoke(ctx, "/generated.ObjectStore/ObjectExists", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/ObjectExists", in, out, opts...) if err != nil { return nil, err } @@ -467,7 +1226,7 @@ func (c *objectStoreClient) ObjectExists(ctx context.Context, in *ObjectExistsRe } func (c *objectStoreClient) GetObject(ctx context.Context, in *GetObjectRequest, opts ...grpc.CallOption) (ObjectStore_GetObjectClient, error) { - stream, err := grpc.NewClientStream(ctx, &_ObjectStore_serviceDesc.Streams[1], c.cc, "/generated.ObjectStore/GetObject", opts...) + stream, err := c.cc.NewStream(ctx, &_ObjectStore_serviceDesc.Streams[1], "/generated.ObjectStore/GetObject", opts...) if err != nil { return nil, err } @@ -500,7 +1259,7 @@ func (x *objectStoreGetObjectClient) Recv() (*Bytes, error) { func (c *objectStoreClient) ListCommonPrefixes(ctx context.Context, in *ListCommonPrefixesRequest, opts ...grpc.CallOption) (*ListCommonPrefixesResponse, error) { out := new(ListCommonPrefixesResponse) - err := grpc.Invoke(ctx, "/generated.ObjectStore/ListCommonPrefixes", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/ListCommonPrefixes", in, out, opts...) if err != nil { return nil, err } @@ -509,7 +1268,7 @@ func (c *objectStoreClient) ListCommonPrefixes(ctx context.Context, in *ListComm func (c *objectStoreClient) ListObjects(ctx context.Context, in *ListObjectsRequest, opts ...grpc.CallOption) (*ListObjectsResponse, error) { out := new(ListObjectsResponse) - err := grpc.Invoke(ctx, "/generated.ObjectStore/ListObjects", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/ListObjects", in, out, opts...) if err != nil { return nil, err } @@ -518,7 +1277,7 @@ func (c *objectStoreClient) ListObjects(ctx context.Context, in *ListObjectsRequ func (c *objectStoreClient) DeleteObject(ctx context.Context, in *DeleteObjectRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.ObjectStore/DeleteObject", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/DeleteObject", in, out, opts...) if err != nil { return nil, err } @@ -527,15 +1286,14 @@ func (c *objectStoreClient) DeleteObject(ctx context.Context, in *DeleteObjectRe func (c *objectStoreClient) CreateSignedURL(ctx context.Context, in *CreateSignedURLRequest, opts ...grpc.CallOption) (*CreateSignedURLResponse, error) { out := new(CreateSignedURLResponse) - err := grpc.Invoke(ctx, "/generated.ObjectStore/CreateSignedURL", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.ObjectStore/CreateSignedURL", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for ObjectStore service - +// ObjectStoreServer is the server API for ObjectStore service. type ObjectStoreServer interface { Init(context.Context, *ObjectStoreInitRequest) (*Empty, error) PutObject(ObjectStore_PutObjectServer) error @@ -547,6 +1305,35 @@ type ObjectStoreServer interface { CreateSignedURL(context.Context, *CreateSignedURLRequest) (*CreateSignedURLResponse, error) } +// UnimplementedObjectStoreServer can be embedded to have forward compatible implementations. +type UnimplementedObjectStoreServer struct { +} + +func (*UnimplementedObjectStoreServer) Init(context.Context, *ObjectStoreInitRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") +} +func (*UnimplementedObjectStoreServer) PutObject(ObjectStore_PutObjectServer) error { + return status.Errorf(codes.Unimplemented, "method PutObject not implemented") +} +func (*UnimplementedObjectStoreServer) ObjectExists(context.Context, *ObjectExistsRequest) (*ObjectExistsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ObjectExists not implemented") +} +func (*UnimplementedObjectStoreServer) GetObject(*GetObjectRequest, ObjectStore_GetObjectServer) error { + return status.Errorf(codes.Unimplemented, "method GetObject not implemented") +} +func (*UnimplementedObjectStoreServer) ListCommonPrefixes(context.Context, *ListCommonPrefixesRequest) (*ListCommonPrefixesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListCommonPrefixes not implemented") +} +func (*UnimplementedObjectStoreServer) ListObjects(context.Context, *ListObjectsRequest) (*ListObjectsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListObjects not implemented") +} +func (*UnimplementedObjectStoreServer) DeleteObject(context.Context, *DeleteObjectRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteObject not implemented") +} +func (*UnimplementedObjectStoreServer) CreateSignedURL(context.Context, *CreateSignedURLRequest) (*CreateSignedURLResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSignedURL not implemented") +} + func RegisterObjectStoreServer(s *grpc.Server, srv ObjectStoreServer) { s.RegisterService(&_ObjectStore_serviceDesc, srv) } @@ -749,46 +1536,3 @@ var _ObjectStore_serviceDesc = grpc.ServiceDesc{ }, Metadata: "ObjectStore.proto", } - -func init() { proto.RegisterFile("ObjectStore.proto", fileDescriptor2) } - -var fileDescriptor2 = []byte{ - // 577 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xd6, 0xc6, 0x69, 0x54, 0x4f, 0x22, 0x61, 0xb6, 0x55, 0x30, 0x2e, 0x94, 0xb0, 0x02, 0x29, - 0x08, 0x11, 0xa1, 0x72, 0x29, 0xd0, 0x03, 0x22, 0x44, 0x08, 0x29, 0x52, 0x2b, 0x07, 0x04, 0x07, - 0x2e, 0x4e, 0x3c, 0x4d, 0x4d, 0x1c, 0x3b, 0xd8, 0x6b, 0x54, 0x1f, 0x79, 0x25, 0x9e, 0x89, 0x07, - 0x41, 0xbb, 0xde, 0x26, 0xde, 0xfc, 0x10, 0xa9, 0xca, 0x6d, 0x66, 0x76, 0xe7, 0x9b, 0x6f, 0x66, - 0xfd, 0x8d, 0xe1, 0xee, 0xf9, 0xf0, 0x07, 0x8e, 0xf8, 0x80, 0xc7, 0x09, 0x76, 0x66, 0x49, 0xcc, - 0x63, 0x6a, 0x8e, 0x31, 0xc2, 0xc4, 0xe3, 0xe8, 0x3b, 0x8d, 0xc1, 0x95, 0x97, 0xa0, 0x5f, 0x1c, - 0xb0, 0x2b, 0xb0, 0x2e, 0x32, 0x5e, 0x24, 0xb8, 0xf8, 0x33, 0xc3, 0x94, 0xd3, 0x26, 0xd4, 0x66, - 0x61, 0x36, 0x0e, 0x22, 0x9b, 0xb4, 0x48, 0xdb, 0x74, 0x95, 0x27, 0xe2, 0xc3, 0x6c, 0x34, 0x41, - 0x6e, 0x57, 0x8a, 0x78, 0xe1, 0x51, 0x0b, 0x8c, 0x09, 0xe6, 0xb6, 0x21, 0x83, 0xc2, 0xa4, 0x14, - 0xaa, 0xc3, 0xd8, 0xcf, 0xed, 0x6a, 0x8b, 0xb4, 0x1b, 0xae, 0xb4, 0xd9, 0x57, 0x38, 0x28, 0xca, - 0xf4, 0xae, 0x83, 0x94, 0xa7, 0x3b, 0x2b, 0xc6, 0x3a, 0x70, 0xa8, 0x03, 0xa7, 0xb3, 0x38, 0x4a, - 0x51, 0x20, 0xa0, 0x8c, 0x48, 0xe4, 0x7d, 0x57, 0x79, 0xec, 0x33, 0x58, 0x1f, 0x71, 0xd7, 0x2d, - 0xb3, 0x23, 0xd8, 0x7b, 0x9f, 0x73, 0x4c, 0x45, 0xef, 0xbe, 0xc7, 0x3d, 0x09, 0xd4, 0x70, 0xa5, - 0xcd, 0x7e, 0x13, 0xb8, 0xdf, 0x0f, 0x52, 0xde, 0x8d, 0xa7, 0xd3, 0x38, 0xba, 0x48, 0xf0, 0x32, - 0xb8, 0xc6, 0x5b, 0x8f, 0xe0, 0x01, 0x98, 0x3e, 0x86, 0xc1, 0x34, 0xe0, 0x98, 0x28, 0x0a, 0x8b, - 0x80, 0x44, 0x93, 0x05, 0xe4, 0xf4, 0x05, 0x9a, 0xf4, 0xd8, 0x29, 0x38, 0xeb, 0x28, 0xa8, 0x61, - 0x39, 0xb0, 0x3f, 0x53, 0x31, 0x9b, 0xb4, 0x8c, 0xb6, 0xe9, 0xce, 0x7d, 0xf6, 0x1d, 0xa8, 0xc8, - 0x2c, 0x26, 0x76, 0x6b, 0xd6, 0x0b, 0x5e, 0x86, 0xc6, 0xeb, 0x19, 0x1c, 0x68, 0xe8, 0x8a, 0x10, - 0x85, 0xea, 0x04, 0xf3, 0x1b, 0x32, 0xd2, 0x16, 0x9f, 0xd0, 0x07, 0x0c, 0x91, 0xe3, 0xae, 0x1f, - 0x2f, 0x84, 0x66, 0x37, 0x41, 0x8f, 0xe3, 0x20, 0x18, 0x47, 0xe8, 0x7f, 0x71, 0xfb, 0xbb, 0xd3, - 0x82, 0x05, 0x06, 0xe7, 0xa1, 0x7c, 0x0c, 0xc3, 0x15, 0x26, 0x7b, 0x0e, 0xf7, 0x56, 0xaa, 0xa9, - 0xae, 0x2d, 0x30, 0xb2, 0x24, 0x54, 0xb5, 0x84, 0xc9, 0xfe, 0x10, 0x68, 0x96, 0xf4, 0xfc, 0x29, - 0x0a, 0xb6, 0xf6, 0xdd, 0x83, 0xda, 0x28, 0x8e, 0x2e, 0x83, 0xb1, 0x5d, 0x69, 0x19, 0xed, 0xfa, - 0xc9, 0x8b, 0xce, 0x5c, 0xfd, 0x9d, 0xf5, 0x50, 0x9d, 0xae, 0xbc, 0xdf, 0x8b, 0x78, 0x92, 0xbb, - 0x2a, 0xd9, 0x79, 0x0d, 0xf5, 0x52, 0xf8, 0xa6, 0x33, 0xb2, 0xe8, 0xec, 0x10, 0xf6, 0x7e, 0x79, - 0x61, 0x86, 0x6a, 0x04, 0x85, 0xf3, 0xa6, 0x72, 0x4a, 0x4e, 0xfe, 0x56, 0xa1, 0x5e, 0xaa, 0x44, - 0xdf, 0x42, 0x55, 0x54, 0xa3, 0x8f, 0xb7, 0x32, 0x71, 0xac, 0xd2, 0x95, 0xde, 0x74, 0xc6, 0x73, - 0x7a, 0x06, 0xe6, 0x7c, 0x45, 0xd1, 0xa3, 0xd2, 0xf1, 0xf2, 0xe2, 0x5a, 0xcd, 0x6d, 0x13, 0x7a, - 0x0e, 0x8d, 0xf2, 0x76, 0xa0, 0xc7, 0x2b, 0x14, 0xb4, 0x7d, 0xe4, 0x3c, 0xda, 0x78, 0xae, 0x9e, - 0xe8, 0x0c, 0xcc, 0xf9, 0xfa, 0xd0, 0xe8, 0x2c, 0x2f, 0x15, 0x8d, 0x8e, 0xdc, 0x0d, 0x2f, 0x09, - 0xf5, 0x0a, 0x2d, 0xe9, 0x2a, 0xa4, 0x4f, 0x4a, 0x37, 0x37, 0xee, 0x09, 0xe7, 0xe9, 0x96, 0x5b, - 0x8a, 0x60, 0x1f, 0xea, 0x25, 0x41, 0xd1, 0x87, 0x4b, 0x59, 0xba, 0x8c, 0x9d, 0xe3, 0x4d, 0xc7, - 0x0a, 0xed, 0x1d, 0x34, 0xca, 0x9a, 0xd3, 0xe6, 0xb7, 0x46, 0x8c, 0x6b, 0xde, 0xef, 0x1b, 0xdc, - 0x59, 0xfa, 0xdc, 0xb5, 0xef, 0x60, 0xbd, 0xf0, 0x1c, 0xf6, 0xbf, 0x2b, 0x05, 0xb7, 0x61, 0x4d, - 0xfe, 0xc3, 0x5e, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x4e, 0x76, 0xa3, 0xf1, 0x06, 0x00, - 0x00, -} diff --git a/pkg/plugin/generated/PluginLister.pb.go b/pkg/plugin/generated/PluginLister.pb.go index 0f4fe7c878..85398bc86c 100644 --- a/pkg/plugin/generated/PluginLister.pb.go +++ b/pkg/plugin/generated/PluginLister.pb.go @@ -1,112 +1,298 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: PluginLister.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type PluginIdentifier struct { - Command string `protobuf:"bytes,1,opt,name=command" json:"command,omitempty"` - Kind string `protobuf:"bytes,2,opt,name=kind" json:"kind,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *PluginIdentifier) Reset() { + *x = PluginIdentifier{} + if protoimpl.UnsafeEnabled { + mi := &file_PluginLister_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PluginIdentifier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PluginIdentifier) ProtoMessage() {} + +func (x *PluginIdentifier) ProtoReflect() protoreflect.Message { + mi := &file_PluginLister_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *PluginIdentifier) Reset() { *m = PluginIdentifier{} } -func (m *PluginIdentifier) String() string { return proto.CompactTextString(m) } -func (*PluginIdentifier) ProtoMessage() {} -func (*PluginIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} } +// Deprecated: Use PluginIdentifier.ProtoReflect.Descriptor instead. +func (*PluginIdentifier) Descriptor() ([]byte, []int) { + return file_PluginLister_proto_rawDescGZIP(), []int{0} +} -func (m *PluginIdentifier) GetCommand() string { - if m != nil { - return m.Command +func (x *PluginIdentifier) GetCommand() string { + if x != nil { + return x.Command } return "" } -func (m *PluginIdentifier) GetKind() string { - if m != nil { - return m.Kind +func (x *PluginIdentifier) GetKind() string { + if x != nil { + return x.Kind } return "" } -func (m *PluginIdentifier) GetName() string { - if m != nil { - return m.Name +func (x *PluginIdentifier) GetName() string { + if x != nil { + return x.Name } return "" } type ListPluginsResponse struct { - Plugins []*PluginIdentifier `protobuf:"bytes,1,rep,name=plugins" json:"plugins,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugins []*PluginIdentifier `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins,omitempty"` +} + +func (x *ListPluginsResponse) Reset() { + *x = ListPluginsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_PluginLister_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListPluginsResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ListPluginsResponse) Reset() { *m = ListPluginsResponse{} } -func (m *ListPluginsResponse) String() string { return proto.CompactTextString(m) } -func (*ListPluginsResponse) ProtoMessage() {} -func (*ListPluginsResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{1} } +func (*ListPluginsResponse) ProtoMessage() {} -func (m *ListPluginsResponse) GetPlugins() []*PluginIdentifier { - if m != nil { - return m.Plugins +func (x *ListPluginsResponse) ProtoReflect() protoreflect.Message { + mi := &file_PluginLister_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListPluginsResponse.ProtoReflect.Descriptor instead. +func (*ListPluginsResponse) Descriptor() ([]byte, []int) { + return file_PluginLister_proto_rawDescGZIP(), []int{1} +} + +func (x *ListPluginsResponse) GetPlugins() []*PluginIdentifier { + if x != nil { + return x.Plugins } return nil } -func init() { - proto.RegisterType((*PluginIdentifier)(nil), "generated.PluginIdentifier") - proto.RegisterType((*ListPluginsResponse)(nil), "generated.ListPluginsResponse") +var File_PluginLister_proto protoreflect.FileDescriptor + +var file_PluginLister_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x1a, + 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, 0x0a, + 0x10, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x4c, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x73, 0x32, 0x4f, 0x0a, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x12, 0x3f, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, + 0x12, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, + 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_PluginLister_proto_rawDescOnce sync.Once + file_PluginLister_proto_rawDescData = file_PluginLister_proto_rawDesc +) + +func file_PluginLister_proto_rawDescGZIP() []byte { + file_PluginLister_proto_rawDescOnce.Do(func() { + file_PluginLister_proto_rawDescData = protoimpl.X.CompressGZIP(file_PluginLister_proto_rawDescData) + }) + return file_PluginLister_proto_rawDescData +} + +var file_PluginLister_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_PluginLister_proto_goTypes = []interface{}{ + (*PluginIdentifier)(nil), // 0: generated.PluginIdentifier + (*ListPluginsResponse)(nil), // 1: generated.ListPluginsResponse + (*Empty)(nil), // 2: generated.Empty +} +var file_PluginLister_proto_depIdxs = []int32{ + 0, // 0: generated.ListPluginsResponse.plugins:type_name -> generated.PluginIdentifier + 2, // 1: generated.PluginLister.ListPlugins:input_type -> generated.Empty + 1, // 2: generated.PluginLister.ListPlugins:output_type -> generated.ListPluginsResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_PluginLister_proto_init() } +func file_PluginLister_proto_init() { + if File_PluginLister_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_PluginLister_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PluginIdentifier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_PluginLister_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListPluginsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_PluginLister_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_PluginLister_proto_goTypes, + DependencyIndexes: file_PluginLister_proto_depIdxs, + MessageInfos: file_PluginLister_proto_msgTypes, + }.Build() + File_PluginLister_proto = out.File + file_PluginLister_proto_rawDesc = nil + file_PluginLister_proto_goTypes = nil + file_PluginLister_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for PluginLister service +const _ = grpc.SupportPackageIsVersion6 +// PluginListerClient is the client API for PluginLister service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type PluginListerClient interface { ListPlugins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListPluginsResponse, error) } type pluginListerClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewPluginListerClient(cc *grpc.ClientConn) PluginListerClient { +func NewPluginListerClient(cc grpc.ClientConnInterface) PluginListerClient { return &pluginListerClient{cc} } func (c *pluginListerClient) ListPlugins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListPluginsResponse, error) { out := new(ListPluginsResponse) - err := grpc.Invoke(ctx, "/generated.PluginLister/ListPlugins", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.PluginLister/ListPlugins", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for PluginLister service - +// PluginListerServer is the server API for PluginLister service. type PluginListerServer interface { ListPlugins(context.Context, *Empty) (*ListPluginsResponse, error) } +// UnimplementedPluginListerServer can be embedded to have forward compatible implementations. +type UnimplementedPluginListerServer struct { +} + +func (*UnimplementedPluginListerServer) ListPlugins(context.Context, *Empty) (*ListPluginsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPlugins not implemented") +} + func RegisterPluginListerServer(s *grpc.Server, srv PluginListerServer) { s.RegisterService(&_PluginLister_serviceDesc, srv) } @@ -141,22 +327,3 @@ var _PluginLister_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "PluginLister.proto", } - -func init() { proto.RegisterFile("PluginLister.proto", fileDescriptor3) } - -var fileDescriptor3 = []byte{ - // 201 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x0a, 0xc8, 0x29, 0x4d, - 0xcf, 0xcc, 0xf3, 0xc9, 0x2c, 0x2e, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, - 0x4c, 0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0x91, 0xe2, 0x09, 0xce, 0x48, 0x2c, 0x4a, - 0x4d, 0x81, 0x48, 0x28, 0x85, 0x70, 0x09, 0x40, 0x94, 0x7b, 0xa6, 0xa4, 0xe6, 0x95, 0x64, 0xa6, - 0x65, 0xa6, 0x16, 0x09, 0x49, 0x70, 0xb1, 0x27, 0xe7, 0xe7, 0xe6, 0x26, 0xe6, 0xa5, 0x48, 0x30, - 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x42, 0x42, 0x5c, 0x2c, 0xd9, 0x99, 0x79, 0x29, 0x12, - 0x4c, 0x60, 0x61, 0x30, 0x1b, 0x24, 0x96, 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0x0c, 0x11, 0x03, 0xb1, - 0x95, 0x7c, 0xb8, 0x84, 0x41, 0xd6, 0x43, 0x4c, 0x2e, 0x0e, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, - 0x4e, 0x15, 0x32, 0xe5, 0x62, 0x2f, 0x80, 0x08, 0x49, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0x49, - 0xeb, 0xc1, 0xdd, 0xa5, 0x87, 0xee, 0x8c, 0x20, 0x98, 0x5a, 0x23, 0x7f, 0x2e, 0x1e, 0x64, 0x2f, - 0x09, 0xd9, 0x73, 0x71, 0x23, 0x99, 0x2e, 0x24, 0x80, 0x64, 0x88, 0x6b, 0x6e, 0x41, 0x49, 0xa5, - 0x94, 0x1c, 0x92, 0x08, 0x16, 0x77, 0x24, 0xb1, 0x81, 0xfd, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, - 0xff, 0x0e, 0xb5, 0xe4, 0x0c, 0x2a, 0x01, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/RestoreItemAction.pb.go b/pkg/plugin/generated/RestoreItemAction.pb.go index 63a92b4c2e..fc080853a5 100644 --- a/pkg/plugin/generated/RestoreItemAction.pb.go +++ b/pkg/plugin/generated/RestoreItemAction.pb.go @@ -1,167 +1,457 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: RestoreItemAction.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type RestoreItemActionExecuteRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` Restore []byte `protobuf:"bytes,3,opt,name=restore,proto3" json:"restore,omitempty"` ItemFromBackup []byte `protobuf:"bytes,4,opt,name=itemFromBackup,proto3" json:"itemFromBackup,omitempty"` } -func (m *RestoreItemActionExecuteRequest) Reset() { *m = RestoreItemActionExecuteRequest{} } -func (m *RestoreItemActionExecuteRequest) String() string { return proto.CompactTextString(m) } -func (*RestoreItemActionExecuteRequest) ProtoMessage() {} +func (x *RestoreItemActionExecuteRequest) Reset() { + *x = RestoreItemActionExecuteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_RestoreItemAction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RestoreItemActionExecuteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreItemActionExecuteRequest) ProtoMessage() {} + +func (x *RestoreItemActionExecuteRequest) ProtoReflect() protoreflect.Message { + mi := &file_RestoreItemAction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestoreItemActionExecuteRequest.ProtoReflect.Descriptor instead. func (*RestoreItemActionExecuteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor4, []int{0} + return file_RestoreItemAction_proto_rawDescGZIP(), []int{0} } -func (m *RestoreItemActionExecuteRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *RestoreItemActionExecuteRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *RestoreItemActionExecuteRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *RestoreItemActionExecuteRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *RestoreItemActionExecuteRequest) GetRestore() []byte { - if m != nil { - return m.Restore +func (x *RestoreItemActionExecuteRequest) GetRestore() []byte { + if x != nil { + return x.Restore } return nil } -func (m *RestoreItemActionExecuteRequest) GetItemFromBackup() []byte { - if m != nil { - return m.ItemFromBackup +func (x *RestoreItemActionExecuteRequest) GetItemFromBackup() []byte { + if x != nil { + return x.ItemFromBackup } return nil } type RestoreItemActionExecuteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems" json:"additionalItems,omitempty"` - SkipRestore bool `protobuf:"varint,3,opt,name=skipRestore" json:"skipRestore,omitempty"` + AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` + SkipRestore bool `protobuf:"varint,3,opt,name=skipRestore,proto3" json:"skipRestore,omitempty"` } -func (m *RestoreItemActionExecuteResponse) Reset() { *m = RestoreItemActionExecuteResponse{} } -func (m *RestoreItemActionExecuteResponse) String() string { return proto.CompactTextString(m) } -func (*RestoreItemActionExecuteResponse) ProtoMessage() {} +func (x *RestoreItemActionExecuteResponse) Reset() { + *x = RestoreItemActionExecuteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_RestoreItemAction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RestoreItemActionExecuteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreItemActionExecuteResponse) ProtoMessage() {} + +func (x *RestoreItemActionExecuteResponse) ProtoReflect() protoreflect.Message { + mi := &file_RestoreItemAction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestoreItemActionExecuteResponse.ProtoReflect.Descriptor instead. func (*RestoreItemActionExecuteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor4, []int{1} + return file_RestoreItemAction_proto_rawDescGZIP(), []int{1} } -func (m *RestoreItemActionExecuteResponse) GetItem() []byte { - if m != nil { - return m.Item +func (x *RestoreItemActionExecuteResponse) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *RestoreItemActionExecuteResponse) GetAdditionalItems() []*ResourceIdentifier { - if m != nil { - return m.AdditionalItems +func (x *RestoreItemActionExecuteResponse) GetAdditionalItems() []*ResourceIdentifier { + if x != nil { + return x.AdditionalItems } return nil } -func (m *RestoreItemActionExecuteResponse) GetSkipRestore() bool { - if m != nil { - return m.SkipRestore +func (x *RestoreItemActionExecuteResponse) GetSkipRestore() bool { + if x != nil { + return x.SkipRestore } return false } type RestoreItemActionAppliesToRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` } -func (m *RestoreItemActionAppliesToRequest) Reset() { *m = RestoreItemActionAppliesToRequest{} } -func (m *RestoreItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } -func (*RestoreItemActionAppliesToRequest) ProtoMessage() {} +func (x *RestoreItemActionAppliesToRequest) Reset() { + *x = RestoreItemActionAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_RestoreItemAction_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RestoreItemActionAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreItemActionAppliesToRequest) ProtoMessage() {} + +func (x *RestoreItemActionAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_RestoreItemAction_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestoreItemActionAppliesToRequest.ProtoReflect.Descriptor instead. func (*RestoreItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor4, []int{2} + return file_RestoreItemAction_proto_rawDescGZIP(), []int{2} } -func (m *RestoreItemActionAppliesToRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *RestoreItemActionAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } type RestoreItemActionAppliesToResponse struct { - ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *RestoreItemActionAppliesToResponse) Reset() { + *x = RestoreItemActionAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_RestoreItemAction_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RestoreItemActionAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreItemActionAppliesToResponse) ProtoMessage() {} + +func (x *RestoreItemActionAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_RestoreItemAction_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *RestoreItemActionAppliesToResponse) Reset() { *m = RestoreItemActionAppliesToResponse{} } -func (m *RestoreItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } -func (*RestoreItemActionAppliesToResponse) ProtoMessage() {} +// Deprecated: Use RestoreItemActionAppliesToResponse.ProtoReflect.Descriptor instead. func (*RestoreItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor4, []int{3} + return file_RestoreItemAction_proto_rawDescGZIP(), []int{3} } -func (m *RestoreItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { - if m != nil { - return m.ResourceSelector +func (x *RestoreItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { + if x != nil { + return x.ResourceSelector } return nil } -func init() { - proto.RegisterType((*RestoreItemActionExecuteRequest)(nil), "generated.RestoreItemActionExecuteRequest") - proto.RegisterType((*RestoreItemActionExecuteResponse)(nil), "generated.RestoreItemActionExecuteResponse") - proto.RegisterType((*RestoreItemActionAppliesToRequest)(nil), "generated.RestoreItemActionAppliesToRequest") - proto.RegisterType((*RestoreItemActionAppliesToResponse)(nil), "generated.RestoreItemActionAppliesToResponse") +var File_RestoreItemAction_proto protoreflect.FileDescriptor + +var file_RestoreItemAction_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x8f, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x74, + 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x26, 0x0a, 0x0e, + 0x69, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x22, 0xa1, 0x01, 0x0a, 0x20, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x47, 0x0a, + 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, + 0x70, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x3b, 0x0a, 0x21, 0x52, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0x6d, 0x0a, 0x22, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x32, 0xe1, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x68, 0x0a, 0x09, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x12, 0x2c, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, + 0x2a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, + 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, + 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_RestoreItemAction_proto_rawDescOnce sync.Once + file_RestoreItemAction_proto_rawDescData = file_RestoreItemAction_proto_rawDesc +) + +func file_RestoreItemAction_proto_rawDescGZIP() []byte { + file_RestoreItemAction_proto_rawDescOnce.Do(func() { + file_RestoreItemAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_RestoreItemAction_proto_rawDescData) + }) + return file_RestoreItemAction_proto_rawDescData +} + +var file_RestoreItemAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_RestoreItemAction_proto_goTypes = []interface{}{ + (*RestoreItemActionExecuteRequest)(nil), // 0: generated.RestoreItemActionExecuteRequest + (*RestoreItemActionExecuteResponse)(nil), // 1: generated.RestoreItemActionExecuteResponse + (*RestoreItemActionAppliesToRequest)(nil), // 2: generated.RestoreItemActionAppliesToRequest + (*RestoreItemActionAppliesToResponse)(nil), // 3: generated.RestoreItemActionAppliesToResponse + (*ResourceIdentifier)(nil), // 4: generated.ResourceIdentifier + (*ResourceSelector)(nil), // 5: generated.ResourceSelector +} +var file_RestoreItemAction_proto_depIdxs = []int32{ + 4, // 0: generated.RestoreItemActionExecuteResponse.additionalItems:type_name -> generated.ResourceIdentifier + 5, // 1: generated.RestoreItemActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 2, // 2: generated.RestoreItemAction.AppliesTo:input_type -> generated.RestoreItemActionAppliesToRequest + 0, // 3: generated.RestoreItemAction.Execute:input_type -> generated.RestoreItemActionExecuteRequest + 3, // 4: generated.RestoreItemAction.AppliesTo:output_type -> generated.RestoreItemActionAppliesToResponse + 1, // 5: generated.RestoreItemAction.Execute:output_type -> generated.RestoreItemActionExecuteResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_RestoreItemAction_proto_init() } +func file_RestoreItemAction_proto_init() { + if File_RestoreItemAction_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_RestoreItemAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestoreItemActionExecuteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_RestoreItemAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestoreItemActionExecuteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_RestoreItemAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestoreItemActionAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_RestoreItemAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestoreItemActionAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_RestoreItemAction_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_RestoreItemAction_proto_goTypes, + DependencyIndexes: file_RestoreItemAction_proto_depIdxs, + MessageInfos: file_RestoreItemAction_proto_msgTypes, + }.Build() + File_RestoreItemAction_proto = out.File + file_RestoreItemAction_proto_rawDesc = nil + file_RestoreItemAction_proto_goTypes = nil + file_RestoreItemAction_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for RestoreItemAction service +const _ = grpc.SupportPackageIsVersion6 +// RestoreItemActionClient is the client API for RestoreItemAction service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type RestoreItemActionClient interface { AppliesTo(ctx context.Context, in *RestoreItemActionAppliesToRequest, opts ...grpc.CallOption) (*RestoreItemActionAppliesToResponse, error) Execute(ctx context.Context, in *RestoreItemActionExecuteRequest, opts ...grpc.CallOption) (*RestoreItemActionExecuteResponse, error) } type restoreItemActionClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewRestoreItemActionClient(cc *grpc.ClientConn) RestoreItemActionClient { +func NewRestoreItemActionClient(cc grpc.ClientConnInterface) RestoreItemActionClient { return &restoreItemActionClient{cc} } func (c *restoreItemActionClient) AppliesTo(ctx context.Context, in *RestoreItemActionAppliesToRequest, opts ...grpc.CallOption) (*RestoreItemActionAppliesToResponse, error) { out := new(RestoreItemActionAppliesToResponse) - err := grpc.Invoke(ctx, "/generated.RestoreItemAction/AppliesTo", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.RestoreItemAction/AppliesTo", in, out, opts...) if err != nil { return nil, err } @@ -170,20 +460,30 @@ func (c *restoreItemActionClient) AppliesTo(ctx context.Context, in *RestoreItem func (c *restoreItemActionClient) Execute(ctx context.Context, in *RestoreItemActionExecuteRequest, opts ...grpc.CallOption) (*RestoreItemActionExecuteResponse, error) { out := new(RestoreItemActionExecuteResponse) - err := grpc.Invoke(ctx, "/generated.RestoreItemAction/Execute", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.RestoreItemAction/Execute", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for RestoreItemAction service - +// RestoreItemActionServer is the server API for RestoreItemAction service. type RestoreItemActionServer interface { AppliesTo(context.Context, *RestoreItemActionAppliesToRequest) (*RestoreItemActionAppliesToResponse, error) Execute(context.Context, *RestoreItemActionExecuteRequest) (*RestoreItemActionExecuteResponse, error) } +// UnimplementedRestoreItemActionServer can be embedded to have forward compatible implementations. +type UnimplementedRestoreItemActionServer struct { +} + +func (*UnimplementedRestoreItemActionServer) AppliesTo(context.Context, *RestoreItemActionAppliesToRequest) (*RestoreItemActionAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (*UnimplementedRestoreItemActionServer) Execute(context.Context, *RestoreItemActionExecuteRequest) (*RestoreItemActionExecuteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Execute not implemented") +} + func RegisterRestoreItemActionServer(s *grpc.Server, srv RestoreItemActionServer) { s.RegisterService(&_RestoreItemAction_serviceDesc, srv) } @@ -240,30 +540,3 @@ var _RestoreItemAction_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "RestoreItemAction.proto", } - -func init() { proto.RegisterFile("RestoreItemAction.proto", fileDescriptor4) } - -var fileDescriptor4 = []byte{ - // 332 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xdd, 0x4e, 0xc2, 0x30, - 0x14, 0x4e, 0x81, 0x80, 0x1c, 0x88, 0x3f, 0xbd, 0xd0, 0x06, 0x63, 0x9c, 0xbb, 0x30, 0xc4, 0x1f, - 0x2e, 0xf0, 0xd2, 0x2b, 0x4c, 0x94, 0x70, 0x5b, 0x7c, 0x81, 0xb1, 0x1d, 0xa1, 0x61, 0x5b, 0x6b, - 0xdb, 0x25, 0xbe, 0x85, 0xcf, 0xe0, 0xa3, 0xf9, 0x26, 0x86, 0x31, 0x96, 0xc1, 0x74, 0x72, 0xd7, - 0x73, 0xfa, 0x7d, 0xe7, 0xfb, 0xbe, 0xf6, 0xc0, 0x19, 0x47, 0x63, 0xa5, 0xc6, 0x89, 0xc5, 0x68, - 0xe4, 0x5b, 0x21, 0xe3, 0x81, 0xd2, 0xd2, 0x4a, 0xda, 0x9e, 0x63, 0x8c, 0xda, 0xb3, 0x18, 0xf4, - 0xba, 0xd3, 0x85, 0xa7, 0x31, 0x58, 0x5f, 0xb8, 0x9f, 0x04, 0x2e, 0x4b, 0xa4, 0xe7, 0x0f, 0xf4, - 0x13, 0x8b, 0x1c, 0xdf, 0x13, 0x34, 0x96, 0x9e, 0x42, 0x53, 0x85, 0xc9, 0x5c, 0xc4, 0x8c, 0x38, - 0xa4, 0xdf, 0xe6, 0x59, 0x45, 0x29, 0x34, 0x84, 0xc5, 0x88, 0xd5, 0x1c, 0xd2, 0xef, 0xf2, 0xf4, - 0x4c, 0x19, 0xb4, 0xf4, 0x7a, 0x1c, 0xab, 0xa7, 0xed, 0x4d, 0x49, 0xaf, 0xe1, 0x70, 0x85, 0x78, - 0xd1, 0x32, 0x7a, 0xf2, 0xfc, 0x65, 0xa2, 0x58, 0x23, 0x05, 0xec, 0x74, 0xdd, 0x2f, 0x02, 0xce, - 0xdf, 0x8e, 0x8c, 0x92, 0xb1, 0xc1, 0x5c, 0x9a, 0x14, 0xa4, 0xc7, 0x70, 0xe4, 0x05, 0x81, 0x58, - 0xc1, 0xbd, 0x70, 0x45, 0x35, 0xac, 0xe6, 0xd4, 0xfb, 0x9d, 0xe1, 0xc5, 0x20, 0x4f, 0x3f, 0xe0, - 0x68, 0x64, 0xa2, 0x7d, 0x9c, 0x04, 0x18, 0x5b, 0xf1, 0x26, 0x50, 0xf3, 0x5d, 0x16, 0x75, 0xa0, - 0x63, 0x96, 0x42, 0xf1, 0x42, 0x8e, 0x03, 0x5e, 0x6c, 0xb9, 0x8f, 0x70, 0x55, 0xb2, 0x38, 0x52, - 0x2a, 0x14, 0x68, 0x5e, 0xe5, 0x3f, 0xcf, 0xe6, 0x46, 0xe0, 0x56, 0x91, 0xb3, 0x84, 0x63, 0x38, - 0xde, 0x78, 0x9d, 0x62, 0x88, 0xbe, 0x95, 0x3a, 0x9d, 0xd3, 0x19, 0x9e, 0xff, 0x12, 0x67, 0x03, - 0xe1, 0x25, 0xd2, 0xf0, 0x9b, 0xc0, 0x49, 0x49, 0x8f, 0x2e, 0xa0, 0x9d, 0x6b, 0xd2, 0xbb, 0xed, - 0x89, 0xd5, 0xb9, 0x7a, 0xf7, 0x7b, 0xa2, 0xb3, 0x20, 0x33, 0x68, 0x65, 0xbf, 0x47, 0x6f, 0xaa, - 0x98, 0xdb, 0x4b, 0xd7, 0xbb, 0xdd, 0x0b, 0xbb, 0xd6, 0x98, 0x35, 0xd3, 0x65, 0x7e, 0xf8, 0x09, - 0x00, 0x00, 0xff, 0xff, 0x1b, 0x4c, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/Shared.pb.go b/pkg/plugin/generated/Shared.pb.go index 8f7fc6c710..1c84177dc8 100644 --- a/pkg/plugin/generated/Shared.pb.go +++ b/pkg/plugin/generated/Shared.pb.go @@ -1,193 +1,483 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: Shared.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *Empty) Reset() { *m = Empty{} } -func (m *Empty) String() string { return proto.CompactTextString(m) } -func (*Empty) ProtoMessage() {} -func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} } +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_Shared_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_Shared_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_Shared_proto_rawDescGZIP(), []int{0} +} type Stack struct { - Frames []*StackFrame `protobuf:"bytes,1,rep,name=frames" json:"frames,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Frames []*StackFrame `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"` +} + +func (x *Stack) Reset() { + *x = Stack{} + if protoimpl.UnsafeEnabled { + mi := &file_Shared_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Stack) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Stack) Reset() { *m = Stack{} } -func (m *Stack) String() string { return proto.CompactTextString(m) } -func (*Stack) ProtoMessage() {} -func (*Stack) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{1} } +func (*Stack) ProtoMessage() {} -func (m *Stack) GetFrames() []*StackFrame { - if m != nil { - return m.Frames +func (x *Stack) ProtoReflect() protoreflect.Message { + mi := &file_Shared_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Stack.ProtoReflect.Descriptor instead. +func (*Stack) Descriptor() ([]byte, []int) { + return file_Shared_proto_rawDescGZIP(), []int{1} +} + +func (x *Stack) GetFrames() []*StackFrame { + if x != nil { + return x.Frames } return nil } type StackFrame struct { - File string `protobuf:"bytes,1,opt,name=file" json:"file,omitempty"` - Line int32 `protobuf:"varint,2,opt,name=line" json:"line,omitempty"` - Function string `protobuf:"bytes,3,opt,name=function" json:"function,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + File string `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"` + Line int32 `protobuf:"varint,2,opt,name=line,proto3" json:"line,omitempty"` + Function string `protobuf:"bytes,3,opt,name=function,proto3" json:"function,omitempty"` +} + +func (x *StackFrame) Reset() { + *x = StackFrame{} + if protoimpl.UnsafeEnabled { + mi := &file_Shared_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StackFrame) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StackFrame) ProtoMessage() {} + +func (x *StackFrame) ProtoReflect() protoreflect.Message { + mi := &file_Shared_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *StackFrame) Reset() { *m = StackFrame{} } -func (m *StackFrame) String() string { return proto.CompactTextString(m) } -func (*StackFrame) ProtoMessage() {} -func (*StackFrame) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{2} } +// Deprecated: Use StackFrame.ProtoReflect.Descriptor instead. +func (*StackFrame) Descriptor() ([]byte, []int) { + return file_Shared_proto_rawDescGZIP(), []int{2} +} -func (m *StackFrame) GetFile() string { - if m != nil { - return m.File +func (x *StackFrame) GetFile() string { + if x != nil { + return x.File } return "" } -func (m *StackFrame) GetLine() int32 { - if m != nil { - return m.Line +func (x *StackFrame) GetLine() int32 { + if x != nil { + return x.Line } return 0 } -func (m *StackFrame) GetFunction() string { - if m != nil { - return m.Function +func (x *StackFrame) GetFunction() string { + if x != nil { + return x.Function } return "" } type ResourceIdentifier struct { - Group string `protobuf:"bytes,1,opt,name=group" json:"group,omitempty"` - Resource string `protobuf:"bytes,2,opt,name=resource" json:"resource,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name" json:"name,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` + Resource string `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` } -func (m *ResourceIdentifier) Reset() { *m = ResourceIdentifier{} } -func (m *ResourceIdentifier) String() string { return proto.CompactTextString(m) } -func (*ResourceIdentifier) ProtoMessage() {} -func (*ResourceIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{3} } +func (x *ResourceIdentifier) Reset() { + *x = ResourceIdentifier{} + if protoimpl.UnsafeEnabled { + mi := &file_Shared_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceIdentifier) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *ResourceIdentifier) GetGroup() string { - if m != nil { - return m.Group +func (*ResourceIdentifier) ProtoMessage() {} + +func (x *ResourceIdentifier) ProtoReflect() protoreflect.Message { + mi := &file_Shared_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceIdentifier.ProtoReflect.Descriptor instead. +func (*ResourceIdentifier) Descriptor() ([]byte, []int) { + return file_Shared_proto_rawDescGZIP(), []int{3} +} + +func (x *ResourceIdentifier) GetGroup() string { + if x != nil { + return x.Group } return "" } -func (m *ResourceIdentifier) GetResource() string { - if m != nil { - return m.Resource +func (x *ResourceIdentifier) GetResource() string { + if x != nil { + return x.Resource } return "" } -func (m *ResourceIdentifier) GetNamespace() string { - if m != nil { - return m.Namespace +func (x *ResourceIdentifier) GetNamespace() string { + if x != nil { + return x.Namespace } return "" } -func (m *ResourceIdentifier) GetName() string { - if m != nil { - return m.Name +func (x *ResourceIdentifier) GetName() string { + if x != nil { + return x.Name } return "" } type ResourceSelector struct { - IncludedNamespaces []string `protobuf:"bytes,1,rep,name=includedNamespaces" json:"includedNamespaces,omitempty"` - ExcludedNamespaces []string `protobuf:"bytes,2,rep,name=excludedNamespaces" json:"excludedNamespaces,omitempty"` - IncludedResources []string `protobuf:"bytes,3,rep,name=includedResources" json:"includedResources,omitempty"` - ExcludedResources []string `protobuf:"bytes,4,rep,name=excludedResources" json:"excludedResources,omitempty"` - Selector string `protobuf:"bytes,5,opt,name=selector" json:"selector,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IncludedNamespaces []string `protobuf:"bytes,1,rep,name=includedNamespaces,proto3" json:"includedNamespaces,omitempty"` + ExcludedNamespaces []string `protobuf:"bytes,2,rep,name=excludedNamespaces,proto3" json:"excludedNamespaces,omitempty"` + IncludedResources []string `protobuf:"bytes,3,rep,name=includedResources,proto3" json:"includedResources,omitempty"` + ExcludedResources []string `protobuf:"bytes,4,rep,name=excludedResources,proto3" json:"excludedResources,omitempty"` + Selector string `protobuf:"bytes,5,opt,name=selector,proto3" json:"selector,omitempty"` +} + +func (x *ResourceSelector) Reset() { + *x = ResourceSelector{} + if protoimpl.UnsafeEnabled { + mi := &file_Shared_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceSelector) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ResourceSelector) Reset() { *m = ResourceSelector{} } -func (m *ResourceSelector) String() string { return proto.CompactTextString(m) } -func (*ResourceSelector) ProtoMessage() {} -func (*ResourceSelector) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{4} } +func (*ResourceSelector) ProtoMessage() {} -func (m *ResourceSelector) GetIncludedNamespaces() []string { - if m != nil { - return m.IncludedNamespaces +func (x *ResourceSelector) ProtoReflect() protoreflect.Message { + mi := &file_Shared_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceSelector.ProtoReflect.Descriptor instead. +func (*ResourceSelector) Descriptor() ([]byte, []int) { + return file_Shared_proto_rawDescGZIP(), []int{4} +} + +func (x *ResourceSelector) GetIncludedNamespaces() []string { + if x != nil { + return x.IncludedNamespaces } return nil } -func (m *ResourceSelector) GetExcludedNamespaces() []string { - if m != nil { - return m.ExcludedNamespaces +func (x *ResourceSelector) GetExcludedNamespaces() []string { + if x != nil { + return x.ExcludedNamespaces } return nil } -func (m *ResourceSelector) GetIncludedResources() []string { - if m != nil { - return m.IncludedResources +func (x *ResourceSelector) GetIncludedResources() []string { + if x != nil { + return x.IncludedResources } return nil } -func (m *ResourceSelector) GetExcludedResources() []string { - if m != nil { - return m.ExcludedResources +func (x *ResourceSelector) GetExcludedResources() []string { + if x != nil { + return x.ExcludedResources } return nil } -func (m *ResourceSelector) GetSelector() string { - if m != nil { - return m.Selector +func (x *ResourceSelector) GetSelector() string { + if x != nil { + return x.Selector } return "" } -func init() { - proto.RegisterType((*Empty)(nil), "generated.Empty") - proto.RegisterType((*Stack)(nil), "generated.Stack") - proto.RegisterType((*StackFrame)(nil), "generated.StackFrame") - proto.RegisterType((*ResourceIdentifier)(nil), "generated.ResourceIdentifier") - proto.RegisterType((*ResourceSelector)(nil), "generated.ResourceSelector") -} - -func init() { proto.RegisterFile("Shared.proto", fileDescriptor5) } - -var fileDescriptor5 = []byte{ - // 337 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0xcd, 0x4e, 0xeb, 0x30, - 0x10, 0x85, 0x95, 0xb6, 0xe9, 0xbd, 0x19, 0x58, 0x80, 0x05, 0x52, 0x84, 0x58, 0x54, 0x59, 0x75, - 0x41, 0x13, 0x89, 0x0a, 0x1e, 0x00, 0x09, 0x24, 0x36, 0x08, 0xa5, 0x3b, 0x76, 0xae, 0x33, 0x49, - 0xad, 0x26, 0x76, 0xe4, 0xd8, 0xa5, 0xf0, 0xc8, 0x3c, 0x05, 0xb2, 0xf3, 0xd3, 0x45, 0xd8, 0xcd, - 0x99, 0xf3, 0xe5, 0xcc, 0x64, 0x64, 0x38, 0xdf, 0xec, 0xa8, 0xc2, 0x2c, 0xae, 0x95, 0xd4, 0x92, - 0x04, 0x05, 0x0a, 0x54, 0x54, 0x63, 0x16, 0xfd, 0x03, 0xff, 0xb9, 0xaa, 0xf5, 0x57, 0xf4, 0x08, - 0xfe, 0x46, 0x53, 0xb6, 0x27, 0x2b, 0x98, 0xe7, 0x8a, 0x56, 0xd8, 0x84, 0xde, 0x62, 0xba, 0x3c, - 0xbb, 0xbf, 0x8e, 0x07, 0x3a, 0x76, 0xc4, 0x8b, 0x75, 0xd3, 0x0e, 0x8a, 0xde, 0x01, 0x4e, 0x5d, - 0x42, 0x60, 0x96, 0xf3, 0x12, 0x43, 0x6f, 0xe1, 0x2d, 0x83, 0xd4, 0xd5, 0xb6, 0x57, 0x72, 0x81, - 0xe1, 0x64, 0xe1, 0x2d, 0xfd, 0xd4, 0xd5, 0xe4, 0x06, 0xfe, 0xe7, 0x46, 0x30, 0xcd, 0xa5, 0x08, - 0xa7, 0x8e, 0x1d, 0x74, 0x74, 0x04, 0x92, 0x62, 0x23, 0x8d, 0x62, 0xf8, 0x9a, 0xa1, 0xd0, 0x3c, - 0xe7, 0xa8, 0xc8, 0x15, 0xf8, 0x85, 0x92, 0xa6, 0xee, 0xa2, 0x5b, 0x61, 0x73, 0x54, 0xc7, 0xba, - 0xfc, 0x20, 0x1d, 0x34, 0xb9, 0x85, 0x40, 0xd8, 0x15, 0x6b, 0xca, 0xb0, 0x1b, 0x72, 0x6a, 0xd8, - 0xad, 0xac, 0x08, 0x67, 0xed, 0xa6, 0xb6, 0x8e, 0x7e, 0x3c, 0xb8, 0xe8, 0x47, 0x6f, 0xb0, 0x44, - 0xa6, 0xa5, 0x22, 0x31, 0x10, 0x2e, 0x58, 0x69, 0x32, 0xcc, 0xde, 0xfa, 0xaf, 0xdb, 0xdb, 0x04, - 0xe9, 0x1f, 0x8e, 0xe5, 0xf1, 0x38, 0xe2, 0x27, 0x2d, 0x3f, 0x76, 0xc8, 0x1d, 0x5c, 0xf6, 0x29, - 0xfd, 0xec, 0x26, 0x9c, 0x3a, 0x7c, 0x6c, 0x58, 0xba, 0xcf, 0x38, 0xd1, 0xb3, 0x96, 0x1e, 0x19, - 0xf6, 0x3c, 0x4d, 0xf7, 0x1f, 0xa1, 0xdf, 0x9e, 0xa7, 0xd7, 0x4f, 0x0f, 0x1f, 0xeb, 0x82, 0xeb, - 0x9d, 0xd9, 0xc6, 0x4c, 0x56, 0xc9, 0xa1, 0xfa, 0xa4, 0x0a, 0x57, 0x9a, 0x8a, 0x6f, 0x93, 0x1c, - 0xb0, 0x44, 0x25, 0x93, 0x7a, 0x5f, 0x24, 0x75, 0x69, 0x0a, 0x2e, 0x92, 0xe1, 0x09, 0x6c, 0xe7, - 0xee, 0x09, 0xad, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x58, 0x4c, 0x5c, 0x87, 0x52, 0x02, 0x00, - 0x00, +var File_Shared_proto protoreflect.FileDescriptor + +var file_Shared_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x36, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x2d, 0x0a, 0x06, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x52, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x78, 0x0a, 0x12, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xea, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x12, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x65, + 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x78, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, + 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_Shared_proto_rawDescOnce sync.Once + file_Shared_proto_rawDescData = file_Shared_proto_rawDesc +) + +func file_Shared_proto_rawDescGZIP() []byte { + file_Shared_proto_rawDescOnce.Do(func() { + file_Shared_proto_rawDescData = protoimpl.X.CompressGZIP(file_Shared_proto_rawDescData) + }) + return file_Shared_proto_rawDescData +} + +var file_Shared_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_Shared_proto_goTypes = []interface{}{ + (*Empty)(nil), // 0: generated.Empty + (*Stack)(nil), // 1: generated.Stack + (*StackFrame)(nil), // 2: generated.StackFrame + (*ResourceIdentifier)(nil), // 3: generated.ResourceIdentifier + (*ResourceSelector)(nil), // 4: generated.ResourceSelector +} +var file_Shared_proto_depIdxs = []int32{ + 2, // 0: generated.Stack.frames:type_name -> generated.StackFrame + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_Shared_proto_init() } +func file_Shared_proto_init() { + if File_Shared_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_Shared_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Shared_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stack); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Shared_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StackFrame); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Shared_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceIdentifier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Shared_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceSelector); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Shared_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Shared_proto_goTypes, + DependencyIndexes: file_Shared_proto_depIdxs, + MessageInfos: file_Shared_proto_msgTypes, + }.Build() + File_Shared_proto = out.File + file_Shared_proto_rawDesc = nil + file_Shared_proto_goTypes = nil + file_Shared_proto_depIdxs = nil } diff --git a/pkg/plugin/generated/VolumeSnapshotter.pb.go b/pkg/plugin/generated/VolumeSnapshotter.pb.go index b814bae9a6..f6b754de78 100644 --- a/pkg/plugin/generated/VolumeSnapshotter.pb.go +++ b/pkg/plugin/generated/VolumeSnapshotter.pb.go @@ -1,359 +1,1084 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: VolumeSnapshotter.proto package generated -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type CreateVolumeRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID" json:"snapshotID,omitempty"` - VolumeType string `protobuf:"bytes,3,opt,name=volumeType" json:"volumeType,omitempty"` - VolumeAZ string `protobuf:"bytes,4,opt,name=volumeAZ" json:"volumeAZ,omitempty"` - Iops int64 `protobuf:"varint,5,opt,name=iops" json:"iops,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` + VolumeType string `protobuf:"bytes,3,opt,name=volumeType,proto3" json:"volumeType,omitempty"` + VolumeAZ string `protobuf:"bytes,4,opt,name=volumeAZ,proto3" json:"volumeAZ,omitempty"` + Iops int64 `protobuf:"varint,5,opt,name=iops,proto3" json:"iops,omitempty"` +} + +func (x *CreateVolumeRequest) Reset() { + *x = CreateVolumeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateVolumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateVolumeRequest) ProtoMessage() {} + +func (x *CreateVolumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *CreateVolumeRequest) Reset() { *m = CreateVolumeRequest{} } -func (m *CreateVolumeRequest) String() string { return proto.CompactTextString(m) } -func (*CreateVolumeRequest) ProtoMessage() {} -func (*CreateVolumeRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{0} } +// Deprecated: Use CreateVolumeRequest.ProtoReflect.Descriptor instead. +func (*CreateVolumeRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{0} +} -func (m *CreateVolumeRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *CreateVolumeRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *CreateVolumeRequest) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *CreateVolumeRequest) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } -func (m *CreateVolumeRequest) GetVolumeType() string { - if m != nil { - return m.VolumeType +func (x *CreateVolumeRequest) GetVolumeType() string { + if x != nil { + return x.VolumeType } return "" } -func (m *CreateVolumeRequest) GetVolumeAZ() string { - if m != nil { - return m.VolumeAZ +func (x *CreateVolumeRequest) GetVolumeAZ() string { + if x != nil { + return x.VolumeAZ } return "" } -func (m *CreateVolumeRequest) GetIops() int64 { - if m != nil { - return m.Iops +func (x *CreateVolumeRequest) GetIops() int64 { + if x != nil { + return x.Iops } return 0 } type CreateVolumeResponse struct { - VolumeID string `protobuf:"bytes,1,opt,name=volumeID" json:"volumeID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + VolumeID string `protobuf:"bytes,1,opt,name=volumeID,proto3" json:"volumeID,omitempty"` +} + +func (x *CreateVolumeResponse) Reset() { + *x = CreateVolumeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateVolumeResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *CreateVolumeResponse) Reset() { *m = CreateVolumeResponse{} } -func (m *CreateVolumeResponse) String() string { return proto.CompactTextString(m) } -func (*CreateVolumeResponse) ProtoMessage() {} -func (*CreateVolumeResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{1} } +func (*CreateVolumeResponse) ProtoMessage() {} -func (m *CreateVolumeResponse) GetVolumeID() string { - if m != nil { - return m.VolumeID +func (x *CreateVolumeResponse) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateVolumeResponse.ProtoReflect.Descriptor instead. +func (*CreateVolumeResponse) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateVolumeResponse) GetVolumeID() string { + if x != nil { + return x.VolumeID } return "" } type GetVolumeInfoRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - VolumeID string `protobuf:"bytes,2,opt,name=volumeID" json:"volumeID,omitempty"` - VolumeAZ string `protobuf:"bytes,3,opt,name=volumeAZ" json:"volumeAZ,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + VolumeID string `protobuf:"bytes,2,opt,name=volumeID,proto3" json:"volumeID,omitempty"` + VolumeAZ string `protobuf:"bytes,3,opt,name=volumeAZ,proto3" json:"volumeAZ,omitempty"` +} + +func (x *GetVolumeInfoRequest) Reset() { + *x = GetVolumeInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetVolumeInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVolumeInfoRequest) ProtoMessage() {} + +func (x *GetVolumeInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *GetVolumeInfoRequest) Reset() { *m = GetVolumeInfoRequest{} } -func (m *GetVolumeInfoRequest) String() string { return proto.CompactTextString(m) } -func (*GetVolumeInfoRequest) ProtoMessage() {} -func (*GetVolumeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{2} } +// Deprecated: Use GetVolumeInfoRequest.ProtoReflect.Descriptor instead. +func (*GetVolumeInfoRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{2} +} -func (m *GetVolumeInfoRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *GetVolumeInfoRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *GetVolumeInfoRequest) GetVolumeID() string { - if m != nil { - return m.VolumeID +func (x *GetVolumeInfoRequest) GetVolumeID() string { + if x != nil { + return x.VolumeID } return "" } -func (m *GetVolumeInfoRequest) GetVolumeAZ() string { - if m != nil { - return m.VolumeAZ +func (x *GetVolumeInfoRequest) GetVolumeAZ() string { + if x != nil { + return x.VolumeAZ } return "" } type GetVolumeInfoResponse struct { - VolumeType string `protobuf:"bytes,1,opt,name=volumeType" json:"volumeType,omitempty"` - Iops int64 `protobuf:"varint,2,opt,name=iops" json:"iops,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + VolumeType string `protobuf:"bytes,1,opt,name=volumeType,proto3" json:"volumeType,omitempty"` + Iops int64 `protobuf:"varint,2,opt,name=iops,proto3" json:"iops,omitempty"` +} + +func (x *GetVolumeInfoResponse) Reset() { + *x = GetVolumeInfoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *GetVolumeInfoResponse) Reset() { *m = GetVolumeInfoResponse{} } -func (m *GetVolumeInfoResponse) String() string { return proto.CompactTextString(m) } -func (*GetVolumeInfoResponse) ProtoMessage() {} -func (*GetVolumeInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{3} } +func (x *GetVolumeInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVolumeInfoResponse) ProtoMessage() {} + +func (x *GetVolumeInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *GetVolumeInfoResponse) GetVolumeType() string { - if m != nil { - return m.VolumeType +// Deprecated: Use GetVolumeInfoResponse.ProtoReflect.Descriptor instead. +func (*GetVolumeInfoResponse) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{3} +} + +func (x *GetVolumeInfoResponse) GetVolumeType() string { + if x != nil { + return x.VolumeType } return "" } -func (m *GetVolumeInfoResponse) GetIops() int64 { - if m != nil { - return m.Iops +func (x *GetVolumeInfoResponse) GetIops() int64 { + if x != nil { + return x.Iops } return 0 } type CreateSnapshotRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - VolumeID string `protobuf:"bytes,2,opt,name=volumeID" json:"volumeID,omitempty"` - VolumeAZ string `protobuf:"bytes,3,opt,name=volumeAZ" json:"volumeAZ,omitempty"` - Tags map[string]string `protobuf:"bytes,4,rep,name=tags" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + VolumeID string `protobuf:"bytes,2,opt,name=volumeID,proto3" json:"volumeID,omitempty"` + VolumeAZ string `protobuf:"bytes,3,opt,name=volumeAZ,proto3" json:"volumeAZ,omitempty"` + Tags map[string]string `protobuf:"bytes,4,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CreateSnapshotRequest) Reset() { + *x = CreateSnapshotRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *CreateSnapshotRequest) Reset() { *m = CreateSnapshotRequest{} } -func (m *CreateSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*CreateSnapshotRequest) ProtoMessage() {} -func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{4} } +func (x *CreateSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSnapshotRequest) ProtoMessage() {} + +func (x *CreateSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *CreateSnapshotRequest) GetPlugin() string { - if m != nil { - return m.Plugin +// Deprecated: Use CreateSnapshotRequest.ProtoReflect.Descriptor instead. +func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateSnapshotRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *CreateSnapshotRequest) GetVolumeID() string { - if m != nil { - return m.VolumeID +func (x *CreateSnapshotRequest) GetVolumeID() string { + if x != nil { + return x.VolumeID } return "" } -func (m *CreateSnapshotRequest) GetVolumeAZ() string { - if m != nil { - return m.VolumeAZ +func (x *CreateSnapshotRequest) GetVolumeAZ() string { + if x != nil { + return x.VolumeAZ } return "" } -func (m *CreateSnapshotRequest) GetTags() map[string]string { - if m != nil { - return m.Tags +func (x *CreateSnapshotRequest) GetTags() map[string]string { + if x != nil { + return x.Tags } return nil } type CreateSnapshotResponse struct { - SnapshotID string `protobuf:"bytes,1,opt,name=snapshotID" json:"snapshotID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SnapshotID string `protobuf:"bytes,1,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` +} + +func (x *CreateSnapshotResponse) Reset() { + *x = CreateSnapshotResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateSnapshotResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSnapshotResponse) ProtoMessage() {} + +func (x *CreateSnapshotResponse) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *CreateSnapshotResponse) Reset() { *m = CreateSnapshotResponse{} } -func (m *CreateSnapshotResponse) String() string { return proto.CompactTextString(m) } -func (*CreateSnapshotResponse) ProtoMessage() {} -func (*CreateSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{5} } +// Deprecated: Use CreateSnapshotResponse.ProtoReflect.Descriptor instead. +func (*CreateSnapshotResponse) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{5} +} -func (m *CreateSnapshotResponse) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *CreateSnapshotResponse) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } type DeleteSnapshotRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID" json:"snapshotID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + SnapshotID string `protobuf:"bytes,2,opt,name=snapshotID,proto3" json:"snapshotID,omitempty"` +} + +func (x *DeleteSnapshotRequest) Reset() { + *x = DeleteSnapshotRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSnapshotRequest) ProtoMessage() {} + +func (x *DeleteSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *DeleteSnapshotRequest) Reset() { *m = DeleteSnapshotRequest{} } -func (m *DeleteSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteSnapshotRequest) ProtoMessage() {} -func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{6} } +// Deprecated: Use DeleteSnapshotRequest.ProtoReflect.Descriptor instead. +func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{6} +} -func (m *DeleteSnapshotRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *DeleteSnapshotRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *DeleteSnapshotRequest) GetSnapshotID() string { - if m != nil { - return m.SnapshotID +func (x *DeleteSnapshotRequest) GetSnapshotID() string { + if x != nil { + return x.SnapshotID } return "" } type GetVolumeIDRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` PersistentVolume []byte `protobuf:"bytes,2,opt,name=persistentVolume,proto3" json:"persistentVolume,omitempty"` } -func (m *GetVolumeIDRequest) Reset() { *m = GetVolumeIDRequest{} } -func (m *GetVolumeIDRequest) String() string { return proto.CompactTextString(m) } -func (*GetVolumeIDRequest) ProtoMessage() {} -func (*GetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{7} } +func (x *GetVolumeIDRequest) Reset() { + *x = GetVolumeIDRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetVolumeIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVolumeIDRequest) ProtoMessage() {} + +func (x *GetVolumeIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetVolumeIDRequest.ProtoReflect.Descriptor instead. +func (*GetVolumeIDRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{7} +} -func (m *GetVolumeIDRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *GetVolumeIDRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *GetVolumeIDRequest) GetPersistentVolume() []byte { - if m != nil { - return m.PersistentVolume +func (x *GetVolumeIDRequest) GetPersistentVolume() []byte { + if x != nil { + return x.PersistentVolume } return nil } type GetVolumeIDResponse struct { - VolumeID string `protobuf:"bytes,1,opt,name=volumeID" json:"volumeID,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + VolumeID string `protobuf:"bytes,1,opt,name=volumeID,proto3" json:"volumeID,omitempty"` +} + +func (x *GetVolumeIDResponse) Reset() { + *x = GetVolumeIDResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetVolumeIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVolumeIDResponse) ProtoMessage() {} + +func (x *GetVolumeIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *GetVolumeIDResponse) Reset() { *m = GetVolumeIDResponse{} } -func (m *GetVolumeIDResponse) String() string { return proto.CompactTextString(m) } -func (*GetVolumeIDResponse) ProtoMessage() {} -func (*GetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{8} } +// Deprecated: Use GetVolumeIDResponse.ProtoReflect.Descriptor instead. +func (*GetVolumeIDResponse) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{8} +} -func (m *GetVolumeIDResponse) GetVolumeID() string { - if m != nil { - return m.VolumeID +func (x *GetVolumeIDResponse) GetVolumeID() string { + if x != nil { + return x.VolumeID } return "" } type SetVolumeIDRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` PersistentVolume []byte `protobuf:"bytes,2,opt,name=persistentVolume,proto3" json:"persistentVolume,omitempty"` - VolumeID string `protobuf:"bytes,3,opt,name=volumeID" json:"volumeID,omitempty"` + VolumeID string `protobuf:"bytes,3,opt,name=volumeID,proto3" json:"volumeID,omitempty"` +} + +func (x *SetVolumeIDRequest) Reset() { + *x = SetVolumeIDRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetVolumeIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetVolumeIDRequest) ProtoMessage() {} + +func (x *SetVolumeIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *SetVolumeIDRequest) Reset() { *m = SetVolumeIDRequest{} } -func (m *SetVolumeIDRequest) String() string { return proto.CompactTextString(m) } -func (*SetVolumeIDRequest) ProtoMessage() {} -func (*SetVolumeIDRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{9} } +// Deprecated: Use SetVolumeIDRequest.ProtoReflect.Descriptor instead. +func (*SetVolumeIDRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{9} +} -func (m *SetVolumeIDRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *SetVolumeIDRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *SetVolumeIDRequest) GetPersistentVolume() []byte { - if m != nil { - return m.PersistentVolume +func (x *SetVolumeIDRequest) GetPersistentVolume() []byte { + if x != nil { + return x.PersistentVolume } return nil } -func (m *SetVolumeIDRequest) GetVolumeID() string { - if m != nil { - return m.VolumeID +func (x *SetVolumeIDRequest) GetVolumeID() string { + if x != nil { + return x.VolumeID } return "" } type SetVolumeIDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + PersistentVolume []byte `protobuf:"bytes,1,opt,name=persistentVolume,proto3" json:"persistentVolume,omitempty"` } -func (m *SetVolumeIDResponse) Reset() { *m = SetVolumeIDResponse{} } -func (m *SetVolumeIDResponse) String() string { return proto.CompactTextString(m) } -func (*SetVolumeIDResponse) ProtoMessage() {} -func (*SetVolumeIDResponse) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{10} } +func (x *SetVolumeIDResponse) Reset() { + *x = SetVolumeIDResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetVolumeIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetVolumeIDResponse) ProtoMessage() {} + +func (x *SetVolumeIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetVolumeIDResponse.ProtoReflect.Descriptor instead. +func (*SetVolumeIDResponse) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{10} +} -func (m *SetVolumeIDResponse) GetPersistentVolume() []byte { - if m != nil { - return m.PersistentVolume +func (x *SetVolumeIDResponse) GetPersistentVolume() []byte { + if x != nil { + return x.PersistentVolume } return nil } type VolumeSnapshotterInitRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` - Config map[string]string `protobuf:"bytes,2,rep,name=config" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *VolumeSnapshotterInitRequest) Reset() { + *x = VolumeSnapshotterInitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_VolumeSnapshotter_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VolumeSnapshotterInitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VolumeSnapshotterInitRequest) ProtoMessage() {} + +func (x *VolumeSnapshotterInitRequest) ProtoReflect() protoreflect.Message { + mi := &file_VolumeSnapshotter_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *VolumeSnapshotterInitRequest) Reset() { *m = VolumeSnapshotterInitRequest{} } -func (m *VolumeSnapshotterInitRequest) String() string { return proto.CompactTextString(m) } -func (*VolumeSnapshotterInitRequest) ProtoMessage() {} -func (*VolumeSnapshotterInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor6, []int{11} } +// Deprecated: Use VolumeSnapshotterInitRequest.ProtoReflect.Descriptor instead. +func (*VolumeSnapshotterInitRequest) Descriptor() ([]byte, []int) { + return file_VolumeSnapshotter_proto_rawDescGZIP(), []int{11} +} -func (m *VolumeSnapshotterInitRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *VolumeSnapshotterInitRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *VolumeSnapshotterInitRequest) GetConfig() map[string]string { - if m != nil { - return m.Config +func (x *VolumeSnapshotterInitRequest) GetConfig() map[string]string { + if x != nil { + return x.Config } return nil } -func init() { - proto.RegisterType((*CreateVolumeRequest)(nil), "generated.CreateVolumeRequest") - proto.RegisterType((*CreateVolumeResponse)(nil), "generated.CreateVolumeResponse") - proto.RegisterType((*GetVolumeInfoRequest)(nil), "generated.GetVolumeInfoRequest") - proto.RegisterType((*GetVolumeInfoResponse)(nil), "generated.GetVolumeInfoResponse") - proto.RegisterType((*CreateSnapshotRequest)(nil), "generated.CreateSnapshotRequest") - proto.RegisterType((*CreateSnapshotResponse)(nil), "generated.CreateSnapshotResponse") - proto.RegisterType((*DeleteSnapshotRequest)(nil), "generated.DeleteSnapshotRequest") - proto.RegisterType((*GetVolumeIDRequest)(nil), "generated.GetVolumeIDRequest") - proto.RegisterType((*GetVolumeIDResponse)(nil), "generated.GetVolumeIDResponse") - proto.RegisterType((*SetVolumeIDRequest)(nil), "generated.SetVolumeIDRequest") - proto.RegisterType((*SetVolumeIDResponse)(nil), "generated.SetVolumeIDResponse") - proto.RegisterType((*VolumeSnapshotterInitRequest)(nil), "generated.VolumeSnapshotterInitRequest") +var File_VolumeSnapshotter_proto protoreflect.FileDescriptor + +var file_VolumeSnapshotter_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x6f, 0x70, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x69, 0x6f, + 0x70, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x22, 0x66, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x22, 0x4b, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6f, 0x70, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x69, 0x6f, 0x70, 0x73, 0x22, 0xe0, 0x01, 0x0a, 0x15, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, + 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x41, 0x5a, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x38, + 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x22, 0x4f, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x44, 0x22, 0x58, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x70, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x10, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x22, 0x74, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, + 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x22, 0x41, 0x0a, 0x13, + 0x53, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, + 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x22, + 0xbe, 0x01, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x4b, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x32, 0xc0, 0x04, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x27, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x5b, 0x0a, 0x18, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x20, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x12, 0x20, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4c, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x49, 0x44, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x53, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x53, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, + 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_VolumeSnapshotter_proto_rawDescOnce sync.Once + file_VolumeSnapshotter_proto_rawDescData = file_VolumeSnapshotter_proto_rawDesc +) + +func file_VolumeSnapshotter_proto_rawDescGZIP() []byte { + file_VolumeSnapshotter_proto_rawDescOnce.Do(func() { + file_VolumeSnapshotter_proto_rawDescData = protoimpl.X.CompressGZIP(file_VolumeSnapshotter_proto_rawDescData) + }) + return file_VolumeSnapshotter_proto_rawDescData +} + +var file_VolumeSnapshotter_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_VolumeSnapshotter_proto_goTypes = []interface{}{ + (*CreateVolumeRequest)(nil), // 0: generated.CreateVolumeRequest + (*CreateVolumeResponse)(nil), // 1: generated.CreateVolumeResponse + (*GetVolumeInfoRequest)(nil), // 2: generated.GetVolumeInfoRequest + (*GetVolumeInfoResponse)(nil), // 3: generated.GetVolumeInfoResponse + (*CreateSnapshotRequest)(nil), // 4: generated.CreateSnapshotRequest + (*CreateSnapshotResponse)(nil), // 5: generated.CreateSnapshotResponse + (*DeleteSnapshotRequest)(nil), // 6: generated.DeleteSnapshotRequest + (*GetVolumeIDRequest)(nil), // 7: generated.GetVolumeIDRequest + (*GetVolumeIDResponse)(nil), // 8: generated.GetVolumeIDResponse + (*SetVolumeIDRequest)(nil), // 9: generated.SetVolumeIDRequest + (*SetVolumeIDResponse)(nil), // 10: generated.SetVolumeIDResponse + (*VolumeSnapshotterInitRequest)(nil), // 11: generated.VolumeSnapshotterInitRequest + nil, // 12: generated.CreateSnapshotRequest.TagsEntry + nil, // 13: generated.VolumeSnapshotterInitRequest.ConfigEntry + (*Empty)(nil), // 14: generated.Empty +} +var file_VolumeSnapshotter_proto_depIdxs = []int32{ + 12, // 0: generated.CreateSnapshotRequest.tags:type_name -> generated.CreateSnapshotRequest.TagsEntry + 13, // 1: generated.VolumeSnapshotterInitRequest.config:type_name -> generated.VolumeSnapshotterInitRequest.ConfigEntry + 11, // 2: generated.VolumeSnapshotter.Init:input_type -> generated.VolumeSnapshotterInitRequest + 0, // 3: generated.VolumeSnapshotter.CreateVolumeFromSnapshot:input_type -> generated.CreateVolumeRequest + 2, // 4: generated.VolumeSnapshotter.GetVolumeInfo:input_type -> generated.GetVolumeInfoRequest + 4, // 5: generated.VolumeSnapshotter.CreateSnapshot:input_type -> generated.CreateSnapshotRequest + 6, // 6: generated.VolumeSnapshotter.DeleteSnapshot:input_type -> generated.DeleteSnapshotRequest + 7, // 7: generated.VolumeSnapshotter.GetVolumeID:input_type -> generated.GetVolumeIDRequest + 9, // 8: generated.VolumeSnapshotter.SetVolumeID:input_type -> generated.SetVolumeIDRequest + 14, // 9: generated.VolumeSnapshotter.Init:output_type -> generated.Empty + 1, // 10: generated.VolumeSnapshotter.CreateVolumeFromSnapshot:output_type -> generated.CreateVolumeResponse + 3, // 11: generated.VolumeSnapshotter.GetVolumeInfo:output_type -> generated.GetVolumeInfoResponse + 5, // 12: generated.VolumeSnapshotter.CreateSnapshot:output_type -> generated.CreateSnapshotResponse + 14, // 13: generated.VolumeSnapshotter.DeleteSnapshot:output_type -> generated.Empty + 8, // 14: generated.VolumeSnapshotter.GetVolumeID:output_type -> generated.GetVolumeIDResponse + 10, // 15: generated.VolumeSnapshotter.SetVolumeID:output_type -> generated.SetVolumeIDResponse + 9, // [9:16] is the sub-list for method output_type + 2, // [2:9] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_VolumeSnapshotter_proto_init() } +func file_VolumeSnapshotter_proto_init() { + if File_VolumeSnapshotter_proto != nil { + return + } + file_Shared_proto_init() + if !protoimpl.UnsafeEnabled { + file_VolumeSnapshotter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateVolumeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateVolumeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetVolumeInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetVolumeInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSnapshotRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSnapshotResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSnapshotRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetVolumeIDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetVolumeIDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetVolumeIDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetVolumeIDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_VolumeSnapshotter_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VolumeSnapshotterInitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_VolumeSnapshotter_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_VolumeSnapshotter_proto_goTypes, + DependencyIndexes: file_VolumeSnapshotter_proto_depIdxs, + MessageInfos: file_VolumeSnapshotter_proto_msgTypes, + }.Build() + File_VolumeSnapshotter_proto = out.File + file_VolumeSnapshotter_proto_rawDesc = nil + file_VolumeSnapshotter_proto_goTypes = nil + file_VolumeSnapshotter_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for VolumeSnapshotter service +const _ = grpc.SupportPackageIsVersion6 +// VolumeSnapshotterClient is the client API for VolumeSnapshotter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type VolumeSnapshotterClient interface { Init(ctx context.Context, in *VolumeSnapshotterInitRequest, opts ...grpc.CallOption) (*Empty, error) CreateVolumeFromSnapshot(ctx context.Context, in *CreateVolumeRequest, opts ...grpc.CallOption) (*CreateVolumeResponse, error) @@ -365,16 +1090,16 @@ type VolumeSnapshotterClient interface { } type volumeSnapshotterClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewVolumeSnapshotterClient(cc *grpc.ClientConn) VolumeSnapshotterClient { +func NewVolumeSnapshotterClient(cc grpc.ClientConnInterface) VolumeSnapshotterClient { return &volumeSnapshotterClient{cc} } func (c *volumeSnapshotterClient) Init(ctx context.Context, in *VolumeSnapshotterInitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/Init", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/Init", in, out, opts...) if err != nil { return nil, err } @@ -383,7 +1108,7 @@ func (c *volumeSnapshotterClient) Init(ctx context.Context, in *VolumeSnapshotte func (c *volumeSnapshotterClient) CreateVolumeFromSnapshot(ctx context.Context, in *CreateVolumeRequest, opts ...grpc.CallOption) (*CreateVolumeResponse, error) { out := new(CreateVolumeResponse) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/CreateVolumeFromSnapshot", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/CreateVolumeFromSnapshot", in, out, opts...) if err != nil { return nil, err } @@ -392,7 +1117,7 @@ func (c *volumeSnapshotterClient) CreateVolumeFromSnapshot(ctx context.Context, func (c *volumeSnapshotterClient) GetVolumeInfo(ctx context.Context, in *GetVolumeInfoRequest, opts ...grpc.CallOption) (*GetVolumeInfoResponse, error) { out := new(GetVolumeInfoResponse) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/GetVolumeInfo", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/GetVolumeInfo", in, out, opts...) if err != nil { return nil, err } @@ -401,7 +1126,7 @@ func (c *volumeSnapshotterClient) GetVolumeInfo(ctx context.Context, in *GetVolu func (c *volumeSnapshotterClient) CreateSnapshot(ctx context.Context, in *CreateSnapshotRequest, opts ...grpc.CallOption) (*CreateSnapshotResponse, error) { out := new(CreateSnapshotResponse) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/CreateSnapshot", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/CreateSnapshot", in, out, opts...) if err != nil { return nil, err } @@ -410,7 +1135,7 @@ func (c *volumeSnapshotterClient) CreateSnapshot(ctx context.Context, in *Create func (c *volumeSnapshotterClient) DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/DeleteSnapshot", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/DeleteSnapshot", in, out, opts...) if err != nil { return nil, err } @@ -419,7 +1144,7 @@ func (c *volumeSnapshotterClient) DeleteSnapshot(ctx context.Context, in *Delete func (c *volumeSnapshotterClient) GetVolumeID(ctx context.Context, in *GetVolumeIDRequest, opts ...grpc.CallOption) (*GetVolumeIDResponse, error) { out := new(GetVolumeIDResponse) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/GetVolumeID", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/GetVolumeID", in, out, opts...) if err != nil { return nil, err } @@ -428,15 +1153,14 @@ func (c *volumeSnapshotterClient) GetVolumeID(ctx context.Context, in *GetVolume func (c *volumeSnapshotterClient) SetVolumeID(ctx context.Context, in *SetVolumeIDRequest, opts ...grpc.CallOption) (*SetVolumeIDResponse, error) { out := new(SetVolumeIDResponse) - err := grpc.Invoke(ctx, "/generated.VolumeSnapshotter/SetVolumeID", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/generated.VolumeSnapshotter/SetVolumeID", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for VolumeSnapshotter service - +// VolumeSnapshotterServer is the server API for VolumeSnapshotter service. type VolumeSnapshotterServer interface { Init(context.Context, *VolumeSnapshotterInitRequest) (*Empty, error) CreateVolumeFromSnapshot(context.Context, *CreateVolumeRequest) (*CreateVolumeResponse, error) @@ -447,6 +1171,32 @@ type VolumeSnapshotterServer interface { SetVolumeID(context.Context, *SetVolumeIDRequest) (*SetVolumeIDResponse, error) } +// UnimplementedVolumeSnapshotterServer can be embedded to have forward compatible implementations. +type UnimplementedVolumeSnapshotterServer struct { +} + +func (*UnimplementedVolumeSnapshotterServer) Init(context.Context, *VolumeSnapshotterInitRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) CreateVolumeFromSnapshot(context.Context, *CreateVolumeRequest) (*CreateVolumeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateVolumeFromSnapshot not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) GetVolumeInfo(context.Context, *GetVolumeInfoRequest) (*GetVolumeInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetVolumeInfo not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) CreateSnapshot(context.Context, *CreateSnapshotRequest) (*CreateSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSnapshot not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteSnapshot not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) GetVolumeID(context.Context, *GetVolumeIDRequest) (*GetVolumeIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetVolumeID not implemented") +} +func (*UnimplementedVolumeSnapshotterServer) SetVolumeID(context.Context, *SetVolumeIDRequest) (*SetVolumeIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetVolumeID not implemented") +} + func RegisterVolumeSnapshotterServer(s *grpc.Server, srv VolumeSnapshotterServer) { s.RegisterService(&_VolumeSnapshotter_serviceDesc, srv) } @@ -613,45 +1363,3 @@ var _VolumeSnapshotter_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "VolumeSnapshotter.proto", } - -func init() { proto.RegisterFile("VolumeSnapshotter.proto", fileDescriptor6) } - -var fileDescriptor6 = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xc1, 0x6e, 0xd3, 0x40, - 0x10, 0xd5, 0xda, 0x6e, 0x44, 0x26, 0xa5, 0x0a, 0x9b, 0xa4, 0x58, 0x16, 0x04, 0xe3, 0x0b, 0x51, - 0x0f, 0x96, 0x48, 0x0f, 0x14, 0x0e, 0x48, 0x51, 0x5d, 0x50, 0xd4, 0x4a, 0x48, 0x76, 0x41, 0x08, - 0x4e, 0x86, 0x6e, 0x5c, 0x8b, 0xc4, 0x36, 0xde, 0x4d, 0xa5, 0x7c, 0x0c, 0xbf, 0x82, 0xf8, 0x14, - 0x3e, 0x05, 0xc5, 0xde, 0x24, 0xbb, 0xb1, 0x1d, 0x87, 0x43, 0x6f, 0xde, 0x99, 0x9d, 0x37, 0x6f, - 0x66, 0xdf, 0x8c, 0xe1, 0xf1, 0xa7, 0x78, 0x3a, 0x9f, 0x11, 0x2f, 0xf2, 0x13, 0x7a, 0x1b, 0x33, - 0x46, 0x52, 0x3b, 0x49, 0x63, 0x16, 0xe3, 0x66, 0x40, 0x22, 0x92, 0xfa, 0x8c, 0xdc, 0x18, 0x87, - 0xde, 0xad, 0x9f, 0x92, 0x9b, 0xdc, 0x61, 0xfd, 0x42, 0xd0, 0x39, 0x4f, 0x89, 0xcf, 0x48, 0x1e, - 0xea, 0x92, 0x9f, 0x73, 0x42, 0x19, 0x3e, 0x86, 0x46, 0x32, 0x9d, 0x07, 0x61, 0xa4, 0x23, 0x13, - 0x0d, 0x9a, 0x2e, 0x3f, 0xe1, 0x3e, 0x00, 0xe5, 0xe8, 0x63, 0x47, 0x57, 0x32, 0x9f, 0x60, 0x59, - 0xfa, 0xef, 0x32, 0xa0, 0xeb, 0x45, 0x42, 0x74, 0x35, 0xf7, 0x6f, 0x2c, 0xd8, 0x80, 0x07, 0xf9, - 0x69, 0xf4, 0x45, 0xd7, 0x32, 0xef, 0xfa, 0x8c, 0x31, 0x68, 0x61, 0x9c, 0x50, 0xfd, 0xc0, 0x44, - 0x03, 0xd5, 0xcd, 0xbe, 0xad, 0x21, 0x74, 0x65, 0x7a, 0x34, 0x89, 0x23, 0x2a, 0xe0, 0x8c, 0x1d, - 0xce, 0x70, 0x7d, 0xb6, 0x26, 0xd0, 0x7d, 0x4f, 0x58, 0x1e, 0x30, 0x8e, 0x26, 0x71, 0x5d, 0x4d, - 0x22, 0x96, 0x22, 0x63, 0x49, 0x7c, 0x55, 0x99, 0xaf, 0x75, 0x09, 0xbd, 0xad, 0x3c, 0x9c, 0x9c, - 0xdc, 0x04, 0x54, 0x68, 0xc2, 0xaa, 0x50, 0x45, 0x28, 0xf4, 0x2f, 0x82, 0x5e, 0x5e, 0xe9, 0xea, - 0xf5, 0xee, 0x89, 0x36, 0x7e, 0x0b, 0x1a, 0xf3, 0x03, 0xaa, 0x6b, 0xa6, 0x3a, 0x68, 0x0d, 0x4f, - 0xec, 0xb5, 0x34, 0xec, 0xd2, 0xfc, 0xf6, 0xb5, 0x1f, 0xd0, 0x8b, 0x88, 0xa5, 0x0b, 0x37, 0x8b, - 0x33, 0x5e, 0x41, 0x73, 0x6d, 0xc2, 0x6d, 0x50, 0x7f, 0x90, 0x05, 0x67, 0xb6, 0xfc, 0xc4, 0x5d, - 0x38, 0xb8, 0xf3, 0xa7, 0x73, 0xc2, 0x39, 0xe5, 0x87, 0x37, 0xca, 0x19, 0xb2, 0xce, 0xe0, 0x78, - 0x3b, 0xc3, 0xa6, 0x61, 0x82, 0xaa, 0xd0, 0xb6, 0xaa, 0xac, 0x0f, 0xd0, 0x73, 0xc8, 0x94, 0xec, - 0xdf, 0x9b, 0x1a, 0x99, 0x5a, 0x9f, 0x01, 0x6f, 0x9e, 0xce, 0xa9, 0x43, 0x3b, 0x81, 0x76, 0x42, - 0x52, 0x1a, 0x52, 0x46, 0x22, 0x1e, 0x94, 0x61, 0x1e, 0xba, 0x05, 0xbb, 0xf5, 0x12, 0x3a, 0x12, - 0xf2, 0x1e, 0x7a, 0x65, 0x80, 0xbd, 0x7b, 0x21, 0x23, 0x65, 0x55, 0xb7, 0xb2, 0x8e, 0xa0, 0xe3, - 0x95, 0x10, 0x2d, 0x83, 0x47, 0x15, 0xb5, 0xfe, 0x46, 0xf0, 0xa4, 0xb0, 0x71, 0xc6, 0x51, 0x58, - 0xfb, 0x3c, 0x97, 0xd0, 0xf8, 0x1e, 0x47, 0x93, 0x30, 0xd0, 0x95, 0x4c, 0x84, 0xa7, 0x82, 0x08, - 0x77, 0x01, 0xda, 0xe7, 0x59, 0x54, 0xae, 0x46, 0x0e, 0x61, 0xbc, 0x86, 0x96, 0x60, 0xfe, 0x1f, - 0x45, 0x0e, 0xff, 0x68, 0xf0, 0xa8, 0x90, 0x0f, 0x8f, 0x40, 0x5b, 0xe6, 0xc4, 0x2f, 0xf6, 0x64, - 0x65, 0xb4, 0x85, 0x8b, 0x17, 0xb3, 0x84, 0x2d, 0xf0, 0x57, 0xd0, 0xc5, 0xb5, 0xf5, 0x2e, 0x8d, - 0x67, 0xab, 0x58, 0xdc, 0x2f, 0x4c, 0x9c, 0xb4, 0x7a, 0x8d, 0x67, 0x95, 0x7e, 0xfe, 0x44, 0x2e, - 0x3c, 0x94, 0xf6, 0x0e, 0x16, 0x23, 0xca, 0x36, 0x9f, 0x61, 0x56, 0x5f, 0xe0, 0x98, 0x1f, 0xe1, - 0x48, 0x9e, 0x4d, 0x6c, 0xd6, 0x2d, 0x06, 0xe3, 0xf9, 0x8e, 0x1b, 0x1c, 0xd6, 0x81, 0x23, 0x79, - 0x70, 0x25, 0xd8, 0xd2, 0x99, 0x2e, 0xe9, 0xe6, 0x15, 0xb4, 0x84, 0x99, 0xc2, 0x4f, 0x4b, 0xab, - 0x59, 0x0d, 0x8e, 0xd1, 0xaf, 0x72, 0x73, 0x4e, 0x57, 0xd0, 0xf2, 0x2a, 0xd0, 0xbc, 0xdd, 0x68, - 0x25, 0xf3, 0xf2, 0xad, 0x91, 0xfd, 0x47, 0x4f, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xc3, 0xf2, - 0xb8, 0x2f, 0x7b, 0x07, 0x00, 0x00, -} diff --git a/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go b/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go index 0a3e4aee51..9e820c69fc 100644 --- a/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go +++ b/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go @@ -1,166 +1,432 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.14.0 // source: backupitemaction/v1/BackupItemAction.proto -/* -Package v1 is a generated protocol buffer package. - -It is generated from these files: - backupitemaction/v1/BackupItemAction.proto - -It has these top-level messages: - ExecuteRequest - ExecuteResponse - BackupItemActionAppliesToRequest - BackupItemActionAppliesToResponse -*/ package v1 -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" + generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type ExecuteRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` } -func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} } -func (m *ExecuteRequest) String() string { return proto.CompactTextString(m) } -func (*ExecuteRequest) ProtoMessage() {} -func (*ExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (x *ExecuteRequest) Reset() { + *x = ExecuteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteRequest) ProtoMessage() {} -func (m *ExecuteRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *ExecuteRequest) ProtoReflect() protoreflect.Message { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteRequest.ProtoReflect.Descriptor instead. +func (*ExecuteRequest) Descriptor() ([]byte, []int) { + return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{0} +} + +func (x *ExecuteRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } -func (m *ExecuteRequest) GetItem() []byte { - if m != nil { - return m.Item +func (x *ExecuteRequest) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *ExecuteRequest) GetBackup() []byte { - if m != nil { - return m.Backup +func (x *ExecuteRequest) GetBackup() []byte { + if x != nil { + return x.Backup } return nil } type ExecuteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - AdditionalItems []*generated.ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems" json:"additionalItems,omitempty"` + AdditionalItems []*generated.ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` } -func (m *ExecuteResponse) Reset() { *m = ExecuteResponse{} } -func (m *ExecuteResponse) String() string { return proto.CompactTextString(m) } -func (*ExecuteResponse) ProtoMessage() {} -func (*ExecuteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (x *ExecuteResponse) Reset() { + *x = ExecuteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteResponse) ProtoMessage() {} -func (m *ExecuteResponse) GetItem() []byte { - if m != nil { - return m.Item +func (x *ExecuteResponse) ProtoReflect() protoreflect.Message { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteResponse.ProtoReflect.Descriptor instead. +func (*ExecuteResponse) Descriptor() ([]byte, []int) { + return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{1} +} + +func (x *ExecuteResponse) GetItem() []byte { + if x != nil { + return x.Item } return nil } -func (m *ExecuteResponse) GetAdditionalItems() []*generated.ResourceIdentifier { - if m != nil { - return m.AdditionalItems +func (x *ExecuteResponse) GetAdditionalItems() []*generated.ResourceIdentifier { + if x != nil { + return x.AdditionalItems } return nil } type BackupItemActionAppliesToRequest struct { - Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` +} + +func (x *BackupItemActionAppliesToRequest) Reset() { + *x = BackupItemActionAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupItemActionAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *BackupItemActionAppliesToRequest) Reset() { *m = BackupItemActionAppliesToRequest{} } -func (m *BackupItemActionAppliesToRequest) String() string { return proto.CompactTextString(m) } -func (*BackupItemActionAppliesToRequest) ProtoMessage() {} +func (*BackupItemActionAppliesToRequest) ProtoMessage() {} + +func (x *BackupItemActionAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupItemActionAppliesToRequest.ProtoReflect.Descriptor instead. func (*BackupItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{2} + return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{2} } -func (m *BackupItemActionAppliesToRequest) GetPlugin() string { - if m != nil { - return m.Plugin +func (x *BackupItemActionAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin } return "" } type BackupItemActionAppliesToResponse struct { - ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector" json:"ResourceSelector,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *BackupItemActionAppliesToResponse) Reset() { + *x = BackupItemActionAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupItemActionAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *BackupItemActionAppliesToResponse) Reset() { *m = BackupItemActionAppliesToResponse{} } -func (m *BackupItemActionAppliesToResponse) String() string { return proto.CompactTextString(m) } -func (*BackupItemActionAppliesToResponse) ProtoMessage() {} +func (*BackupItemActionAppliesToResponse) ProtoMessage() {} + +func (x *BackupItemActionAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupItemActionAppliesToResponse.ProtoReflect.Descriptor instead. func (*BackupItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{3} + return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{3} } -func (m *BackupItemActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { - if m != nil { - return m.ResourceSelector +func (x *BackupItemActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { + if x != nil { + return x.ResourceSelector } return nil } -func init() { - proto.RegisterType((*ExecuteRequest)(nil), "v1.ExecuteRequest") - proto.RegisterType((*ExecuteResponse)(nil), "v1.ExecuteResponse") - proto.RegisterType((*BackupItemActionAppliesToRequest)(nil), "v1.BackupItemActionAppliesToRequest") - proto.RegisterType((*BackupItemActionAppliesToResponse)(nil), "v1.BackupItemActionAppliesToResponse") +var File_backupitemaction_v1_BackupItemAction_proto protoreflect.FileDescriptor + +var file_backupitemaction_v1_BackupItemAction_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x69, 0x74, 0x65, 0x6d, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, + 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, + 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, + 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x22, 0x6e, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x47, 0x0a, 0x0f, 0x61, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, + 0x74, 0x65, 0x6d, 0x73, 0x22, 0x3a, 0x0a, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, + 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x22, 0x6c, 0x0a, 0x21, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x32, 0xa0, + 0x01, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, + 0x12, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, + 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, + 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x49, 0x5a, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, + 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x69, + 0x74, 0x65, 0x6d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_backupitemaction_v1_BackupItemAction_proto_rawDescOnce sync.Once + file_backupitemaction_v1_BackupItemAction_proto_rawDescData = file_backupitemaction_v1_BackupItemAction_proto_rawDesc +) + +func file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP() []byte { + file_backupitemaction_v1_BackupItemAction_proto_rawDescOnce.Do(func() { + file_backupitemaction_v1_BackupItemAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_backupitemaction_v1_BackupItemAction_proto_rawDescData) + }) + return file_backupitemaction_v1_BackupItemAction_proto_rawDescData +} + +var file_backupitemaction_v1_BackupItemAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_backupitemaction_v1_BackupItemAction_proto_goTypes = []interface{}{ + (*ExecuteRequest)(nil), // 0: v1.ExecuteRequest + (*ExecuteResponse)(nil), // 1: v1.ExecuteResponse + (*BackupItemActionAppliesToRequest)(nil), // 2: v1.BackupItemActionAppliesToRequest + (*BackupItemActionAppliesToResponse)(nil), // 3: v1.BackupItemActionAppliesToResponse + (*generated.ResourceIdentifier)(nil), // 4: generated.ResourceIdentifier + (*generated.ResourceSelector)(nil), // 5: generated.ResourceSelector +} +var file_backupitemaction_v1_BackupItemAction_proto_depIdxs = []int32{ + 4, // 0: v1.ExecuteResponse.additionalItems:type_name -> generated.ResourceIdentifier + 5, // 1: v1.BackupItemActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 2, // 2: v1.BackupItemAction.AppliesTo:input_type -> v1.BackupItemActionAppliesToRequest + 0, // 3: v1.BackupItemAction.Execute:input_type -> v1.ExecuteRequest + 3, // 4: v1.BackupItemAction.AppliesTo:output_type -> v1.BackupItemActionAppliesToResponse + 1, // 5: v1.BackupItemAction.Execute:output_type -> v1.ExecuteResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_backupitemaction_v1_BackupItemAction_proto_init() } +func file_backupitemaction_v1_BackupItemAction_proto_init() { + if File_backupitemaction_v1_BackupItemAction_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecuteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecuteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BackupItemActionAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BackupItemActionAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_backupitemaction_v1_BackupItemAction_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_backupitemaction_v1_BackupItemAction_proto_goTypes, + DependencyIndexes: file_backupitemaction_v1_BackupItemAction_proto_depIdxs, + MessageInfos: file_backupitemaction_v1_BackupItemAction_proto_msgTypes, + }.Build() + File_backupitemaction_v1_BackupItemAction_proto = out.File + file_backupitemaction_v1_BackupItemAction_proto_rawDesc = nil + file_backupitemaction_v1_BackupItemAction_proto_goTypes = nil + file_backupitemaction_v1_BackupItemAction_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for BackupItemAction service +const _ = grpc.SupportPackageIsVersion6 +// BackupItemActionClient is the client API for BackupItemAction service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type BackupItemActionClient interface { AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) } type backupItemActionClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewBackupItemActionClient(cc *grpc.ClientConn) BackupItemActionClient { +func NewBackupItemActionClient(cc grpc.ClientConnInterface) BackupItemActionClient { return &backupItemActionClient{cc} } func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) { out := new(BackupItemActionAppliesToResponse) - err := grpc.Invoke(ctx, "/v1.BackupItemAction/AppliesTo", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/v1.BackupItemAction/AppliesTo", in, out, opts...) if err != nil { return nil, err } @@ -169,20 +435,30 @@ func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemAc func (c *backupItemActionClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) { out := new(ExecuteResponse) - err := grpc.Invoke(ctx, "/v1.BackupItemAction/Execute", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/v1.BackupItemAction/Execute", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for BackupItemAction service - +// BackupItemActionServer is the server API for BackupItemAction service. type BackupItemActionServer interface { AppliesTo(context.Context, *BackupItemActionAppliesToRequest) (*BackupItemActionAppliesToResponse, error) Execute(context.Context, *ExecuteRequest) (*ExecuteResponse, error) } +// UnimplementedBackupItemActionServer can be embedded to have forward compatible implementations. +type UnimplementedBackupItemActionServer struct { +} + +func (*UnimplementedBackupItemActionServer) AppliesTo(context.Context, *BackupItemActionAppliesToRequest) (*BackupItemActionAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (*UnimplementedBackupItemActionServer) Execute(context.Context, *ExecuteRequest) (*ExecuteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Execute not implemented") +} + func RegisterBackupItemActionServer(s *grpc.Server, srv BackupItemActionServer) { s.RegisterService(&_BackupItemAction_serviceDesc, srv) } @@ -239,31 +515,3 @@ var _BackupItemAction_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "backupitemaction/v1/BackupItemAction.proto", } - -func init() { proto.RegisterFile("backupitemaction/v1/BackupItemAction.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 343 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0x5f, 0x4b, 0xfb, 0x30, - 0x14, 0xa5, 0xdb, 0x8f, 0xfd, 0x58, 0x36, 0xdc, 0x88, 0x20, 0x65, 0x22, 0xd4, 0xa2, 0x30, 0x04, - 0x1b, 0x56, 0xdf, 0x7c, 0xdb, 0x40, 0xc6, 0x5e, 0xbb, 0x3d, 0x88, 0x6f, 0x59, 0x7b, 0xed, 0xc2, - 0xda, 0x24, 0xa6, 0x49, 0x15, 0x3f, 0x8d, 0x1f, 0x55, 0xfa, 0x67, 0xc5, 0xcd, 0xe1, 0xde, 0x72, - 0x0f, 0xf7, 0xdc, 0x73, 0x4e, 0xee, 0x45, 0x77, 0x6b, 0x1a, 0x6e, 0x8d, 0x64, 0x1a, 0x52, 0x1a, - 0x6a, 0x26, 0x38, 0xc9, 0x27, 0x64, 0x56, 0x62, 0x0b, 0x0d, 0xe9, 0xb4, 0xc4, 0x3c, 0xa9, 0x84, - 0x16, 0xb8, 0x95, 0x4f, 0x46, 0xfd, 0xe5, 0x86, 0x2a, 0x88, 0x2a, 0xc4, 0x5d, 0xa1, 0xb3, 0xa7, - 0x0f, 0x08, 0x8d, 0x86, 0x00, 0xde, 0x0c, 0x64, 0x1a, 0x5f, 0xa0, 0x8e, 0x4c, 0x4c, 0xcc, 0xb8, - 0x6d, 0x39, 0xd6, 0xb8, 0x1b, 0xd4, 0x15, 0xc6, 0xe8, 0x5f, 0xa1, 0x61, 0xb7, 0x1c, 0x6b, 0xdc, - 0x0f, 0xca, 0x77, 0xd1, 0x5b, 0xa9, 0xdb, 0xed, 0x12, 0xad, 0x2b, 0x97, 0xa3, 0x41, 0x33, 0x35, - 0x93, 0x82, 0x67, 0xd0, 0xd0, 0xad, 0x1f, 0xf4, 0x39, 0x1a, 0xd0, 0x28, 0x62, 0x85, 0x41, 0x9a, - 0x14, 0x66, 0x33, 0xbb, 0xe5, 0xb4, 0xc7, 0x3d, 0xff, 0xca, 0x8b, 0x81, 0x83, 0xa2, 0x1a, 0x22, - 0x2f, 0x80, 0x4c, 0x18, 0x15, 0xc2, 0x22, 0x02, 0xae, 0xd9, 0x2b, 0x03, 0x15, 0x1c, 0xb2, 0xdc, - 0x47, 0xe4, 0x1c, 0x26, 0x9e, 0x4a, 0x99, 0x30, 0xc8, 0x56, 0xe2, 0x44, 0x2e, 0x37, 0x41, 0xd7, - 0x7f, 0x70, 0x6b, 0xf7, 0x73, 0x34, 0xdc, 0xf9, 0x58, 0x42, 0x02, 0xa1, 0x16, 0xaa, 0x1c, 0xd3, - 0xf3, 0x2f, 0x8f, 0x58, 0xdd, 0xb5, 0x04, 0xbf, 0x48, 0xfe, 0x97, 0x85, 0x86, 0x87, 0x72, 0xf8, - 0x19, 0x75, 0x1b, 0x49, 0x7c, 0xe3, 0xe5, 0x13, 0xef, 0x54, 0x9a, 0xd1, 0xed, 0x89, 0xae, 0xda, - 0xb7, 0x8f, 0xfe, 0xd7, 0x8b, 0xc0, 0xb8, 0x60, 0xec, 0xef, 0x7a, 0x74, 0xbe, 0x87, 0x55, 0x9c, - 0xd9, 0xe2, 0x65, 0x1e, 0x33, 0xbd, 0x31, 0x6b, 0x2f, 0x14, 0x29, 0xc9, 0xd3, 0x77, 0xaa, 0xe0, - 0x5e, 0x53, 0xfe, 0x69, 0x48, 0x0e, 0x09, 0x28, 0x41, 0xe4, 0x36, 0x26, 0xd5, 0xef, 0x91, 0x26, - 0x3c, 0x39, 0x72, 0x86, 0xeb, 0x4e, 0x79, 0x64, 0x0f, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd7, - 0x9d, 0xff, 0x70, 0xa4, 0x02, 0x00, 0x00, -} diff --git a/pkg/plugin/proto/DeleteItemAction.proto b/pkg/plugin/proto/DeleteItemAction.proto index 48c38817ea..f2838fe0c5 100644 --- a/pkg/plugin/proto/DeleteItemAction.proto +++ b/pkg/plugin/proto/DeleteItemAction.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; diff --git a/pkg/plugin/proto/ItemSnapshotter.proto b/pkg/plugin/proto/ItemSnapshotter.proto index 8aa62a8999..a5f6455860 100644 --- a/pkg/plugin/proto/ItemSnapshotter.proto +++ b/pkg/plugin/proto/ItemSnapshotter.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; diff --git a/pkg/plugin/proto/ObjectStore.proto b/pkg/plugin/proto/ObjectStore.proto index f418568fe4..4487a0cef3 100644 --- a/pkg/plugin/proto/ObjectStore.proto +++ b/pkg/plugin/proto/ObjectStore.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; diff --git a/pkg/plugin/proto/PluginLister.proto b/pkg/plugin/proto/PluginLister.proto index caa8b02aad..55cfe62de8 100644 --- a/pkg/plugin/proto/PluginLister.proto +++ b/pkg/plugin/proto/PluginLister.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; diff --git a/pkg/plugin/proto/RestoreItemAction.proto b/pkg/plugin/proto/RestoreItemAction.proto index 49b2568255..ae9c3ddd99 100644 --- a/pkg/plugin/proto/RestoreItemAction.proto +++ b/pkg/plugin/proto/RestoreItemAction.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; diff --git a/pkg/plugin/proto/VolumeSnapshotter.proto b/pkg/plugin/proto/VolumeSnapshotter.proto index 970fc1909c..affb762bcb 100644 --- a/pkg/plugin/proto/VolumeSnapshotter.proto +++ b/pkg/plugin/proto/VolumeSnapshotter.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; From 5a5a4c184e925bfee4f2d163e62e2200c8097a50 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 25 Aug 2022 21:24:53 -0400 Subject: [PATCH 269/366] Updated plugin/framework server files to cope with protoc changes Signed-off-by: Scott Seago --- pkg/plugin/framework/backup_item_action_server.go | 2 +- pkg/plugin/framework/delete_item_action_server.go | 2 +- pkg/plugin/framework/item_snapshotter_server.go | 2 +- pkg/plugin/framework/restore_item_action_server.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/plugin/framework/backup_item_action_server.go b/pkg/plugin/framework/backup_item_action_server.go index dfea095954..630d2c7185 100644 --- a/pkg/plugin/framework/backup_item_action_server.go +++ b/pkg/plugin/framework/backup_item_action_server.go @@ -70,7 +70,7 @@ func (s *BackupItemActionGRPCServer) AppliesTo( } return &protobiav1.BackupItemActionAppliesToResponse{ - &proto.ResourceSelector{ + ResourceSelector: &proto.ResourceSelector{ IncludedNamespaces: resourceSelector.IncludedNamespaces, ExcludedNamespaces: resourceSelector.ExcludedNamespaces, IncludedResources: resourceSelector.IncludedResources, diff --git a/pkg/plugin/framework/delete_item_action_server.go b/pkg/plugin/framework/delete_item_action_server.go index 3c6be3b547..d9d3a7dc70 100644 --- a/pkg/plugin/framework/delete_item_action_server.go +++ b/pkg/plugin/framework/delete_item_action_server.go @@ -66,7 +66,7 @@ func (s *DeleteItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.D } return &proto.DeleteItemActionAppliesToResponse{ - &proto.ResourceSelector{ + ResourceSelector: &proto.ResourceSelector{ IncludedNamespaces: resourceSelector.IncludedNamespaces, ExcludedNamespaces: resourceSelector.ExcludedNamespaces, IncludedResources: resourceSelector.IncludedResources, diff --git a/pkg/plugin/framework/item_snapshotter_server.go b/pkg/plugin/framework/item_snapshotter_server.go index 166e6d07f5..d746947577 100644 --- a/pkg/plugin/framework/item_snapshotter_server.go +++ b/pkg/plugin/framework/item_snapshotter_server.go @@ -88,7 +88,7 @@ func (recv *ItemSnapshotterGRPCServer) AppliesTo(ctx context.Context, req *proto } return &proto.ItemSnapshotterAppliesToResponse{ - &proto.ResourceSelector{ + ResourceSelector: &proto.ResourceSelector{ IncludedNamespaces: resourceSelector.IncludedNamespaces, ExcludedNamespaces: resourceSelector.ExcludedNamespaces, IncludedResources: resourceSelector.IncludedResources, diff --git a/pkg/plugin/framework/restore_item_action_server.go b/pkg/plugin/framework/restore_item_action_server.go index 340559a777..ae5affb151 100644 --- a/pkg/plugin/framework/restore_item_action_server.go +++ b/pkg/plugin/framework/restore_item_action_server.go @@ -66,7 +66,7 @@ func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto. } return &proto.RestoreItemActionAppliesToResponse{ - &proto.ResourceSelector{ + ResourceSelector: &proto.ResourceSelector{ IncludedNamespaces: resourceSelector.IncludedNamespaces, ExcludedNamespaces: resourceSelector.ExcludedNamespaces, IncludedResources: resourceSelector.IncludedResources, From 3769cd218a1a4a625a258572895da0861e4ae7e5 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 19 Aug 2022 13:47:00 +0800 Subject: [PATCH 270/366] kopia lib Signed-off-by: Lyndon-Li --- changelogs/unreleased/5233-lyndon | 2 + .../unified-repo-and-kopia-integration.md | 204 ++--- go.mod | 1 + go.sum | 2 + pkg/repository/provider/provider.go | 25 +- pkg/repository/provider/restic.go | 10 +- pkg/repository/provider/unified_repo.go | 89 +-- pkg/repository/provider/unified_repo_test.go | 1 + pkg/repository/restic/repository.go | 5 + .../backend/mocks/DirectRepository.go | 542 +++++++++++++ .../backend/mocks/DirectRepositoryWriter.go | 718 ++++++++++++++++++ pkg/repository/udmrepo/kopialib/lib_repo.go | 587 ++++++++++++++ .../udmrepo/kopialib/lib_repo_test.go | 406 ++++++++++ .../udmrepo/mocks/BackupRepoService.go | 16 + pkg/repository/udmrepo/repo.go | 4 + pkg/repository/udmrepo/service/service.go | 4 +- pkg/util/logging/kopia_log.go | 90 +++ pkg/util/logging/kopia_log_test.go | 86 +++ 18 files changed, 2618 insertions(+), 174 deletions(-) create mode 100644 changelogs/unreleased/5233-lyndon create mode 100644 pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go create mode 100644 pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go create mode 100644 pkg/repository/udmrepo/kopialib/lib_repo.go create mode 100644 pkg/repository/udmrepo/kopialib/lib_repo_test.go create mode 100644 pkg/util/logging/kopia_log.go create mode 100644 pkg/util/logging/kopia_log_test.go diff --git a/changelogs/unreleased/5233-lyndon b/changelogs/unreleased/5233-lyndon new file mode 100644 index 0000000000..498111d471 --- /dev/null +++ b/changelogs/unreleased/5233-lyndon @@ -0,0 +1,2 @@ +Add changes for Kopia Integration: Kopia Lib - method implementation +Add changes to write Kopia Repository logs to Velero log \ No newline at end of file diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 5b4a897e26..7bef16df89 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -93,126 +93,140 @@ Velero by default uses the Unified Repository for all kinds of data movement, it ## The Unified Repository Interface Below are the definitions of the Unified Repository Interface. All the functions are synchronization functions. ``` -///BackupRepoService is used to initialize, open or maintain a backup repository +// BackupRepoService is used to initialize, open or maintain a backup repository type BackupRepoService interface { - ///Create a backup repository or connect to an existing backup repository - ///repoOption: option to the backup repository and the underlying backup storage - ///createNew: indicates whether to create a new or connect to an existing backup repository - ///result: the backup repository specific output that could be used to open the backup repository later - Init(ctx context.Context, repoOption RepoOptions, createNew bool) error - - ///Open an backup repository that has been created/connected - ///repoOption: options to open the backup repository and the underlying storage - Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) - - ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance - ///repoOption: options to maintain the backup repository - Maintain(ctx context.Context, repoOption RepoOptions) error + // Init creates a backup repository or connect to an existing backup repository. + // repoOption: option to the backup repository and the underlying backup storage. + // createNew: indicates whether to create a new or connect to an existing backup repository. + Init(ctx context.Context, repoOption RepoOptions, createNew bool) error + + // Open opens an backup repository that has been created/connected. + // repoOption: options to open the backup repository and the underlying storage. + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) + + // Maintain is periodically called to maintain the backup repository to eliminate redundant data. + // repoOption: options to maintain the backup repository. + Maintain(ctx context.Context, repoOption RepoOptions) error + + // DefaultMaintenanceFrequency returns the defgault frequency of maintenance, callers refer this + // frequency to maintain the backup repository to get the best maintenance performance + DefaultMaintenanceFrequency() time.Duration } -///BackupRepo provides the access to the backup repository +// BackupRepo provides the access to the backup repository type BackupRepo interface { - ///Open an existing object for read - ///id: the object's unified identifier - OpenObject(ctx context.Context, id ID) (ObjectReader, error) - - ///Get a manifest data - GetManifest(ctx context.Context, id ID, mani *RepoManifest) error - - ///Get one or more manifest data that match the given labels - FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) - - ///Create a new object and return the object's writer interface - ///return: A unified identifier of the object on success - NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter - - ///Save a manifest object - PutManifest(ctx context.Context, mani RepoManifest) (ID, error) - - ///Delete a manifest object - DeleteManifest(ctx context.Context, id ID) error - - ///Flush all the backup repository data - Flush(ctx context.Context) error - - ///Get the local time of the backup repository. It may be different from the time of the caller - Time() time.Time - - ///Close the backup repository - Close(ctx context.Context) error -} + // OpenObject opens an existing object for read. + // id: the object's unified identifier. + OpenObject(ctx context.Context, id ID) (ObjectReader, error) + + // GetManifest gets a manifest data from the backup repository. + GetManifest(ctx context.Context, id ID, mani *RepoManifest) error + + // FindManifests gets one or more manifest data that match the given labels + FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) + + // NewObjectWriter creates a new object and return the object's writer interface. + // return: A unified identifier of the object on success. + NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter + + // PutManifest saves a manifest object into the backup repository. + PutManifest(ctx context.Context, mani RepoManifest) (ID, error) + + // DeleteManifest deletes a manifest object from the backup repository. + DeleteManifest(ctx context.Context, id ID) error + + // Flush flushes all the backup repository data + Flush(ctx context.Context) error + + // Time returns the local time of the backup repository. It may be different from the time of the caller + Time() time.Time + + // Close closes the backup repository + Close(ctx context.Context) error type ObjectReader interface { - io.ReadCloser - io.Seeker - - ///Length returns the logical size of the object - Length() int64 + io.ReadCloser + io.Seeker + + // Length returns the logical size of the object + Length() int64 } type ObjectWriter interface { - io.WriteCloser - - ///For some cases, i.e. block incremental, the object is not written sequentially - io.Seeker - - // Periodically called to preserve the state of data written to the repo so far - // Return a unified identifier that represent the current state - // An empty ID could be returned on success if the backup repository doesn't support this - Checkpoint() (ID, error) - - ///Wait for the completion of the object write - ///Result returns the object's unified identifier after the write completes - Result() (ID, error) -} + io.WriteCloser + + // Seeker is used in the cases that the object is not written sequentially + io.Seeker + + // Checkpoint is periodically called to preserve the state of data written to the repo so far. + // Checkpoint returns a unified identifier that represent the current state. + // An empty ID could be returned on success if the backup repository doesn't support this. + Checkpoint() (ID, error) + + // Result waits for the completion of the object write. + // Result returns the object's unified identifier after the write completes. + Result() (ID, error) +} ``` Some data structure & constants used by the interfaces: -``` +``` type RepoOptions struct { - ///A repository specific string to identify a backup storage, i.e., "s3", "filesystem" - StorageType string - ///Backup repository password, if any - RepoPassword string - ///A custom path to save the repository's configuration, if any - ConfigFilePath string - ///Other repository specific options - GeneralOptions map[string]string - ///Storage specific options - StorageOptions map[string]string + // StorageType is a repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + // RepoPassword is the backup repository's password, if any + RepoPassword string + // ConfigFilePath is a custom path to save the repository's configuration, if any + ConfigFilePath string + // GeneralOptions takes other repository specific options + GeneralOptions map[string]string + // StorageOptions takes storage specific options + StorageOptions map[string]string + // Description is a description of the backup repository/backup repository operation. + // It is for logging/debugging purpose only and doesn't control any behavior of the backup repository. + Description string } -///ObjectWriteOptions defines the options when creating an object for write +// ObjectWriteOptions defines the options when creating an object for write type ObjectWriteOptions struct { - FullPath string ///Full logical path of the object - Description string ///A description of the object, could be empty - Prefix ID ///A prefix of the name used to save the object - AccessMode int ///OBJECT_DATA_ACCESS_* - BackupMode int ///OBJECT_DATA_BACKUP_* + FullPath string // Full logical path of the object + DataType int // OBJECT_DATA_TYPE_* + Description string // A description of the object, could be empty + Prefix ID // A prefix of the name used to save the object + AccessMode int // OBJECT_DATA_ACCESS_* + BackupMode int // OBJECT_DATA_BACKUP_* } const ( - ///Below consts defines the access mode when creating an object for write - OBJECT_DATA_ACCESS_MODE_UNKNOWN int = 0 - OBJECT_DATA_ACCESS_MODE_FILE int = 1 - OBJECT_DATA_ACCESS_MODE_BLOCK int = 2 - - OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 - OBJECT_DATA_BACKUP_MODE_FULL int = 1 - OBJECT_DATA_BACKUP_MODE_INC int = 2 + // Below consts descrbe the data type of one object. + // Metadata: This type describes how the data is organized. + // For a file system backup, the Metadata describes a Dir or File. + // For a block backup, the Metadata describes a Disk and its incremental link. + ObjectDataTypeUnknown int = 0 + ObjectDataTypeMetadata int = 1 + ObjectDataTypeData int = 2 + + // Below consts defines the access mode when creating an object for write + ObjectDataAccessModeUnknown int = 0 + ObjectDataAccessModeFile int = 1 + ObjectDataAccessModeBlock int = 2 + + ObjectDataBackupModeUnknown int = 0 + ObjectDataBackupModeFull int = 1 + ObjectDataBackupModeInc int = 2 ) -///ManifestEntryMetadata is the metadata describing one manifest data +// ManifestEntryMetadata is the metadata describing one manifest data type ManifestEntryMetadata struct { - ID ID ///The ID of the manifest data - Length int32 ///The data size of the manifest data - Labels map[string]string ///Labels saved together with the manifest data - ModTime time.Time ///Modified time of the manifest data + ID ID // The ID of the manifest data + Length int32 // The data size of the manifest data + Labels map[string]string // Labels saved together with the manifest data + ModTime time.Time // Modified time of the manifest data } type RepoManifest struct { - Payload interface{} ///The user data of manifest - Metadata *ManifestEntryMetadata ///The metadata data of manifest + Payload interface{} // The user data of manifest + Metadata *ManifestEntryMetadata // The metadata data of manifest } type ManifestFilter struct { diff --git a/go.mod b/go.mod index d01c71d58f..3d061de81c 100644 --- a/go.mod +++ b/go.mod @@ -76,6 +76,7 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-logr/zapr v0.4.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 4a1ebe18c9..75a473f1c3 100644 --- a/go.sum +++ b/go.sum @@ -323,6 +323,7 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -552,6 +553,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= diff --git a/pkg/repository/provider/provider.go b/pkg/repository/provider/provider.go index 2d78d64263..6579386d6e 100644 --- a/pkg/repository/provider/provider.go +++ b/pkg/repository/provider/provider.go @@ -18,6 +18,7 @@ package provider import ( "context" + "time" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -30,27 +31,27 @@ type RepoParam struct { // Provider defines the methods to manipulate a backup repository type Provider interface { - //InitRepo is to initialize a repository from a new storage place + // InitRepo is to initialize a repository from a new storage place InitRepo(ctx context.Context, param RepoParam) error - //ConnectToRepo is to establish the connection to a - //storage place that a repository is already initialized + // ConnectToRepo is to establish the connection to a + // storage place that a repository is already initialized ConnectToRepo(ctx context.Context, param RepoParam) error - //PrepareRepo is a combination of InitRepo and ConnectToRepo, - //it may do initializing + connecting, connecting only if the repository - //is already initialized, or do nothing if the repository is already connected + // PrepareRepo is a combination of InitRepo and ConnectToRepo, + // it may do initializing + connecting, connecting only if the repository + // is already initialized, or do nothing if the repository is already connected PrepareRepo(ctx context.Context, param RepoParam) error - //PruneRepo does a full prune/maintenance of the repository + // PruneRepo does a full prune/maintenance of the repository PruneRepo(ctx context.Context, param RepoParam) error - //PruneRepoQuick does a quick prune/maintenance of the repository if available - PruneRepoQuick(ctx context.Context, param RepoParam) error - - //EnsureUnlockRepo esures to remove any stale file locks in the storage + // EnsureUnlockRepo esures to remove any stale file locks in the storage EnsureUnlockRepo(ctx context.Context, param RepoParam) error - //Forget is to delete a snapshot from the repository + // Forget is to delete a snapshot from the repository Forget(ctx context.Context, snapshotID string, param RepoParam) error + + // DefaultMaintenanceFrequency returns the default frequency to run maintenance + DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration } diff --git a/pkg/repository/provider/restic.go b/pkg/repository/provider/restic.go index 3659f1be76..4d9f63b4a6 100644 --- a/pkg/repository/provider/restic.go +++ b/pkg/repository/provider/restic.go @@ -18,6 +18,7 @@ package provider import ( "context" + "time" "github.com/sirupsen/logrus" @@ -55,11 +56,6 @@ func (r *resticRepositoryProvider) PruneRepo(ctx context.Context, param RepoPara return r.svc.PruneRepo(param.BackupLocation, param.BackupRepo) } -func (r *resticRepositoryProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { - // restic doesn't support this operation - return nil -} - func (r *resticRepositoryProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error { return r.svc.UnlockRepo(param.BackupLocation, param.BackupRepo) } @@ -67,3 +63,7 @@ func (r *resticRepositoryProvider) EnsureUnlockRepo(ctx context.Context, param R func (r *resticRepositoryProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { return r.svc.Forget(param.BackupLocation, param.BackupRepo, snapshotID) } + +func (r *resticRepositoryProvider) DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration { + return r.svc.DefaultMaintenanceFrequency() +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 4cf2897cfc..18018bf155 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -21,6 +21,7 @@ import ( "fmt" "path" "strings" + "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -59,9 +60,8 @@ var funcTable = localFuncTable{ } const ( - repoOpDescFullMaintain = "full maintenance" - repoOpDescQuickMaintain = "quick maintenance" - repoOpDescForget = "forget" + repoOpDescMaintain = "repo maintenance" + repoOpDescForget = "forget" repoConnectDesc = "unfied repo" ) @@ -70,7 +70,7 @@ const ( func NewUnifiedRepoProvider( credentialGetter credentials.CredentialGetter, log logrus.FieldLogger, -) (Provider, error) { +) Provider { repo := unifiedRepoProvider{ credentialGetter: credentialGetter, log: log, @@ -78,22 +78,21 @@ func NewUnifiedRepoProvider( repo.repoService = createRepoService(log) - log.Debug("Finished create unified repo service") - - return &repo, nil + return &repo } func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) error { log := urp.log.WithFields(logrus.Fields{ - "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, + "BSL name": param.BackupLocation.Name, + "repo name": param.BackupRepo.Name, + "repo UID": param.BackupRepo.UID, }) log.Debug("Start to init repo") repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupRepo.UID)), udmrepo.WithGenOptions( map[string]string{ udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), @@ -120,15 +119,16 @@ func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) e func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoParam) error { log := urp.log.WithFields(logrus.Fields{ - "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, + "BSL name": param.BackupLocation.Name, + "repo name": param.BackupRepo.Name, + "repo UID": param.BackupRepo.UID, }) log.Debug("Start to connect repo") repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupRepo.UID)), udmrepo.WithGenOptions( map[string]string{ udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), @@ -155,15 +155,16 @@ func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoPar func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam) error { log := urp.log.WithFields(logrus.Fields{ - "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, + "BSL name": param.BackupLocation.Name, + "repo name": param.BackupRepo.Name, + "repo UID": param.BackupRepo.UID, }) log.Debug("Start to prepare repo") repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupRepo.UID)), udmrepo.WithGenOptions( map[string]string{ udmrepo.GenOptionOwnerName: udmrepo.GetRepoUser(), @@ -196,21 +197,17 @@ func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) error { log := urp.log.WithFields(logrus.Fields{ - "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, + "BSL name": param.BackupLocation.Name, + "repo name": param.BackupRepo.Name, + "repo UID": param.BackupRepo.UID, }) log.Debug("Start to prune repo") repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), - udmrepo.WithGenOptions( - map[string]string{ - udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainFull, - }, - ), - udmrepo.WithDescription(repoOpDescFullMaintain), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupRepo.UID)), + udmrepo.WithDescription(repoOpDescMaintain), ) if err != nil { @@ -227,39 +224,6 @@ func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) return nil } -func (urp *unifiedRepoProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { - log := urp.log.WithFields(logrus.Fields{ - "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, - }) - - log.Debug("Start to prune repo quick") - - repoOption, err := udmrepo.NewRepoOptions( - udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), - udmrepo.WithGenOptions( - map[string]string{ - udmrepo.GenOptionMaintainMode: udmrepo.GenOptionMaintainQuick, - }, - ), - udmrepo.WithDescription(repoOpDescQuickMaintain), - ) - - if err != nil { - return errors.Wrap(err, "error to get repo options") - } - - err = urp.repoService.Maintain(ctx, *repoOption) - if err != nil { - return errors.Wrap(err, "error to prune backup repo quick") - } - - log.Debug("Prune repo quick complete") - - return nil -} - func (urp *unifiedRepoProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error { return nil } @@ -267,7 +231,8 @@ func (urp *unifiedRepoProvider) EnsureUnlockRepo(ctx context.Context, param Repo func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { log := urp.log.WithFields(logrus.Fields{ "BSL name": param.BackupLocation.Name, - "BSL UID": param.BackupLocation.UID, + "repo name": param.BackupRepo.Name, + "repo UID": param.BackupRepo.UID, "snapshotID": snapshotID, }) @@ -275,7 +240,7 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p repoOption, err := udmrepo.NewRepoOptions( udmrepo.WithPassword(urp, param), - udmrepo.WithConfigFile(urp.workPath, string(param.BackupLocation.UID)), + udmrepo.WithConfigFile(urp.workPath, string(param.BackupRepo.UID)), udmrepo.WithDescription(repoOpDescForget), ) @@ -305,6 +270,10 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p return nil } +func (urp *unifiedRepoProvider) DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration { + return urp.repoService.DefaultMaintenanceFrequency() +} + func (urp *unifiedRepoProvider) GetPassword(param interface{}) (string, error) { repoParam, ok := param.(RepoParam) if !ok { diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index fe8d03abfd..abcac14310 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -775,6 +775,7 @@ func TestForget(t *testing.T) { err := urp.Forget(context.Background(), "", RepoParam{ BackupLocation: &velerov1api.BackupStorageLocation{}, + BackupRepo: &velerov1api.BackupRepository{}, }) if tc.expectedErr == "" { diff --git a/pkg/repository/restic/repository.go b/pkg/repository/restic/repository.go index fa88a9cc44..392caf2842 100644 --- a/pkg/repository/restic/repository.go +++ b/pkg/repository/restic/repository.go @@ -18,6 +18,7 @@ package restic import ( "os" + "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -71,6 +72,10 @@ func (r *RepositoryService) Forget(bsl *velerov1api.BackupStorageLocation, repo return r.exec(restic.ForgetCommand(repo.Spec.ResticIdentifier, snapshotID), bsl) } +func (r *RepositoryService) DefaultMaintenanceFrequency() time.Duration { + return restic.DefaultMaintenanceFrequency +} + func (r *RepositoryService) exec(cmd *restic.Command, bsl *velerov1api.BackupStorageLocation) error { file, err := r.credentialsFileStore.Path(repokey.RepoKeySelector()) if err != nil { diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go new file mode 100644 index 0000000000..c327878827 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go @@ -0,0 +1,542 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + blob "github.com/kopia/kopia/repo/blob" + content "github.com/kopia/kopia/repo/content" + + context "context" + + index "github.com/kopia/kopia/repo/content/index" + + manifest "github.com/kopia/kopia/repo/manifest" + + mock "github.com/stretchr/testify/mock" + + object "github.com/kopia/kopia/repo/object" + + repo "github.com/kopia/kopia/repo" + + throttling "github.com/kopia/kopia/repo/blob/throttling" + + time "time" +) + +// DirectRepository is an autogenerated mock type for the DirectRepository type +type DirectRepository struct { + mock.Mock +} + +// AlsoLogToContentLog provides a mock function with given fields: ctx +func (_m *DirectRepository) AlsoLogToContentLog(ctx context.Context) context.Context { + ret := _m.Called(ctx) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// BlobCfg provides a mock function with given fields: +func (_m *DirectRepository) BlobCfg() content.BlobCfgBlob { + ret := _m.Called() + + var r0 content.BlobCfgBlob + if rf, ok := ret.Get(0).(func() content.BlobCfgBlob); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(content.BlobCfgBlob) + } + + return r0 +} + +// BlobReader provides a mock function with given fields: +func (_m *DirectRepository) BlobReader() blob.Reader { + ret := _m.Called() + + var r0 blob.Reader + if rf, ok := ret.Get(0).(func() blob.Reader); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Reader) + } + } + + return r0 +} + +// BlobVolume provides a mock function with given fields: +func (_m *DirectRepository) BlobVolume() blob.Volume { + ret := _m.Called() + + var r0 blob.Volume + if rf, ok := ret.Get(0).(func() blob.Volume); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Volume) + } + } + + return r0 +} + +// ClientOptions provides a mock function with given fields: +func (_m *DirectRepository) ClientOptions() repo.ClientOptions { + ret := _m.Called() + + var r0 repo.ClientOptions + if rf, ok := ret.Get(0).(func() repo.ClientOptions); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(repo.ClientOptions) + } + + return r0 +} + +// Close provides a mock function with given fields: ctx +func (_m *DirectRepository) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConfigFilename provides a mock function with given fields: +func (_m *DirectRepository) ConfigFilename() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// ContentInfo provides a mock function with given fields: ctx, contentID +func (_m *DirectRepository) ContentInfo(ctx context.Context, contentID index.ID) (index.Info, error) { + ret := _m.Called(ctx, contentID) + + var r0 index.Info + if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { + r0 = rf(ctx, contentID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(index.Info) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { + r1 = rf(ctx, contentID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContentReader provides a mock function with given fields: +func (_m *DirectRepository) ContentReader() content.Reader { + ret := _m.Called() + + var r0 content.Reader + if rf, ok := ret.Get(0).(func() content.Reader); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(content.Reader) + } + } + + return r0 +} + +// Crypter provides a mock function with given fields: +func (_m *DirectRepository) Crypter() *content.Crypter { + ret := _m.Called() + + var r0 *content.Crypter + if rf, ok := ret.Get(0).(func() *content.Crypter); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*content.Crypter) + } + } + + return r0 +} + +// DeriveKey provides a mock function with given fields: purpose, keyLength +func (_m *DirectRepository) DeriveKey(purpose []byte, keyLength int) []byte { + ret := _m.Called(purpose, keyLength) + + var r0 []byte + if rf, ok := ret.Get(0).(func([]byte, int) []byte); ok { + r0 = rf(purpose, keyLength) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// DisableIndexRefresh provides a mock function with given fields: +func (_m *DirectRepository) DisableIndexRefresh() { + _m.Called() +} + +// FindManifests provides a mock function with given fields: ctx, labels +func (_m *DirectRepository) FindManifests(ctx context.Context, labels map[string]string) ([]*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, labels) + + var r0 []*manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { + r0 = rf(ctx, labels) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { + r1 = rf(ctx, labels) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetManifest provides a mock function with given fields: ctx, id, data +func (_m *DirectRepository) GetManifest(ctx context.Context, id manifest.ID, data interface{}) (*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, id, data) + + var r0 *manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { + r0 = rf(ctx, id, data) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { + r1 = rf(ctx, id, data) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// IndexBlobs provides a mock function with given fields: ctx, includeInactive +func (_m *DirectRepository) IndexBlobs(ctx context.Context, includeInactive bool) ([]content.IndexBlobInfo, error) { + ret := _m.Called(ctx, includeInactive) + + var r0 []content.IndexBlobInfo + if rf, ok := ret.Get(0).(func(context.Context, bool) []content.IndexBlobInfo); ok { + r0 = rf(ctx, includeInactive) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]content.IndexBlobInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { + r1 = rf(ctx, includeInactive) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewDirectWriter provides a mock function with given fields: ctx, opt +func (_m *DirectRepository) NewDirectWriter(ctx context.Context, opt repo.WriteSessionOptions) (context.Context, repo.DirectRepositoryWriter, error) { + ret := _m.Called(ctx, opt) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + var r1 repo.DirectRepositoryWriter + if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.DirectRepositoryWriter); ok { + r1 = rf(ctx, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(repo.DirectRepositoryWriter) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { + r2 = rf(ctx, opt) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewWriter provides a mock function with given fields: ctx, opt +func (_m *DirectRepository) NewWriter(ctx context.Context, opt repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error) { + ret := _m.Called(ctx, opt) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + var r1 repo.RepositoryWriter + if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { + r1 = rf(ctx, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(repo.RepositoryWriter) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { + r2 = rf(ctx, opt) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ObjectFormat provides a mock function with given fields: +func (_m *DirectRepository) ObjectFormat() object.Format { + ret := _m.Called() + + var r0 object.Format + if rf, ok := ret.Get(0).(func() object.Format); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(object.Format) + } + + return r0 +} + +// OpenObject provides a mock function with given fields: ctx, id +func (_m *DirectRepository) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { + ret := _m.Called(ctx, id) + + var r0 object.Reader + if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(object.Reader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PrefetchContents provides a mock function with given fields: ctx, contentIDs, hint +func (_m *DirectRepository) PrefetchContents(ctx context.Context, contentIDs []index.ID, hint string) []index.ID { + ret := _m.Called(ctx, contentIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []index.ID, string) []index.ID); ok { + r0 = rf(ctx, contentIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + return r0 +} + +// PrefetchObjects provides a mock function with given fields: ctx, objectIDs, hint +func (_m *DirectRepository) PrefetchObjects(ctx context.Context, objectIDs []object.ID, hint string) ([]index.ID, error) { + ret := _m.Called(ctx, objectIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { + r0 = rf(ctx, objectIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { + r1 = rf(ctx, objectIDs, hint) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Refresh provides a mock function with given fields: ctx +func (_m *DirectRepository) Refresh(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Throttler provides a mock function with given fields: +func (_m *DirectRepository) Throttler() throttling.SettableThrottler { + ret := _m.Called() + + var r0 throttling.SettableThrottler + if rf, ok := ret.Get(0).(func() throttling.SettableThrottler); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(throttling.SettableThrottler) + } + } + + return r0 +} + +// Time provides a mock function with given fields: +func (_m *DirectRepository) Time() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// Token provides a mock function with given fields: password +func (_m *DirectRepository) Token(password string) (string, error) { + ret := _m.Called(password) + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(password) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(password) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UniqueID provides a mock function with given fields: +func (_m *DirectRepository) UniqueID() []byte { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// UpdateDescription provides a mock function with given fields: d +func (_m *DirectRepository) UpdateDescription(d string) { + _m.Called(d) +} + +// VerifyObject provides a mock function with given fields: ctx, id +func (_m *DirectRepository) VerifyObject(ctx context.Context, id object.ID) ([]index.ID, error) { + ret := _m.Called(ctx, id) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewDirectRepository interface { + mock.TestingT + Cleanup(func()) +} + +// NewDirectRepository creates a new instance of DirectRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDirectRepository(t mockConstructorTestingTNewDirectRepository) *DirectRepository { + mock := &DirectRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go new file mode 100644 index 0000000000..2116a025ca --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go @@ -0,0 +1,718 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + blob "github.com/kopia/kopia/repo/blob" + content "github.com/kopia/kopia/repo/content" + + context "context" + + index "github.com/kopia/kopia/repo/content/index" + + manifest "github.com/kopia/kopia/repo/manifest" + + mock "github.com/stretchr/testify/mock" + + object "github.com/kopia/kopia/repo/object" + + repo "github.com/kopia/kopia/repo" + + throttling "github.com/kopia/kopia/repo/blob/throttling" + + time "time" +) + +// DirectRepositoryWriter is an autogenerated mock type for the DirectRepositoryWriter type +type DirectRepositoryWriter struct { + mock.Mock +} + +// AlsoLogToContentLog provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) AlsoLogToContentLog(ctx context.Context) context.Context { + ret := _m.Called(ctx) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// BlobCfg provides a mock function with given fields: +func (_m *DirectRepositoryWriter) BlobCfg() content.BlobCfgBlob { + ret := _m.Called() + + var r0 content.BlobCfgBlob + if rf, ok := ret.Get(0).(func() content.BlobCfgBlob); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(content.BlobCfgBlob) + } + + return r0 +} + +// BlobReader provides a mock function with given fields: +func (_m *DirectRepositoryWriter) BlobReader() blob.Reader { + ret := _m.Called() + + var r0 blob.Reader + if rf, ok := ret.Get(0).(func() blob.Reader); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Reader) + } + } + + return r0 +} + +// BlobStorage provides a mock function with given fields: +func (_m *DirectRepositoryWriter) BlobStorage() blob.Storage { + ret := _m.Called() + + var r0 blob.Storage + if rf, ok := ret.Get(0).(func() blob.Storage); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Storage) + } + } + + return r0 +} + +// BlobVolume provides a mock function with given fields: +func (_m *DirectRepositoryWriter) BlobVolume() blob.Volume { + ret := _m.Called() + + var r0 blob.Volume + if rf, ok := ret.Get(0).(func() blob.Volume); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(blob.Volume) + } + } + + return r0 +} + +// ChangePassword provides a mock function with given fields: ctx, newPassword +func (_m *DirectRepositoryWriter) ChangePassword(ctx context.Context, newPassword string) error { + ret := _m.Called(ctx, newPassword) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, newPassword) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ClientOptions provides a mock function with given fields: +func (_m *DirectRepositoryWriter) ClientOptions() repo.ClientOptions { + ret := _m.Called() + + var r0 repo.ClientOptions + if rf, ok := ret.Get(0).(func() repo.ClientOptions); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(repo.ClientOptions) + } + + return r0 +} + +// Close provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CommitUpgrade provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) CommitUpgrade(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConfigFilename provides a mock function with given fields: +func (_m *DirectRepositoryWriter) ConfigFilename() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// ContentInfo provides a mock function with given fields: ctx, contentID +func (_m *DirectRepositoryWriter) ContentInfo(ctx context.Context, contentID index.ID) (index.Info, error) { + ret := _m.Called(ctx, contentID) + + var r0 index.Info + if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { + r0 = rf(ctx, contentID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(index.Info) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { + r1 = rf(ctx, contentID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContentManager provides a mock function with given fields: +func (_m *DirectRepositoryWriter) ContentManager() *content.WriteManager { + ret := _m.Called() + + var r0 *content.WriteManager + if rf, ok := ret.Get(0).(func() *content.WriteManager); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*content.WriteManager) + } + } + + return r0 +} + +// ContentReader provides a mock function with given fields: +func (_m *DirectRepositoryWriter) ContentReader() content.Reader { + ret := _m.Called() + + var r0 content.Reader + if rf, ok := ret.Get(0).(func() content.Reader); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(content.Reader) + } + } + + return r0 +} + +// Crypter provides a mock function with given fields: +func (_m *DirectRepositoryWriter) Crypter() *content.Crypter { + ret := _m.Called() + + var r0 *content.Crypter + if rf, ok := ret.Get(0).(func() *content.Crypter); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*content.Crypter) + } + } + + return r0 +} + +// DeleteManifest provides a mock function with given fields: ctx, id +func (_m *DirectRepositoryWriter) DeleteManifest(ctx context.Context, id manifest.ID) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeriveKey provides a mock function with given fields: purpose, keyLength +func (_m *DirectRepositoryWriter) DeriveKey(purpose []byte, keyLength int) []byte { + ret := _m.Called(purpose, keyLength) + + var r0 []byte + if rf, ok := ret.Get(0).(func([]byte, int) []byte); ok { + r0 = rf(purpose, keyLength) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// DisableIndexRefresh provides a mock function with given fields: +func (_m *DirectRepositoryWriter) DisableIndexRefresh() { + _m.Called() +} + +// FindManifests provides a mock function with given fields: ctx, labels +func (_m *DirectRepositoryWriter) FindManifests(ctx context.Context, labels map[string]string) ([]*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, labels) + + var r0 []*manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { + r0 = rf(ctx, labels) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { + r1 = rf(ctx, labels) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Flush provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) Flush(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetManifest provides a mock function with given fields: ctx, id, data +func (_m *DirectRepositoryWriter) GetManifest(ctx context.Context, id manifest.ID, data interface{}) (*manifest.EntryMetadata, error) { + ret := _m.Called(ctx, id, data) + + var r0 *manifest.EntryMetadata + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { + r0 = rf(ctx, id, data) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*manifest.EntryMetadata) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { + r1 = rf(ctx, id, data) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// IndexBlobs provides a mock function with given fields: ctx, includeInactive +func (_m *DirectRepositoryWriter) IndexBlobs(ctx context.Context, includeInactive bool) ([]content.IndexBlobInfo, error) { + ret := _m.Called(ctx, includeInactive) + + var r0 []content.IndexBlobInfo + if rf, ok := ret.Get(0).(func(context.Context, bool) []content.IndexBlobInfo); ok { + r0 = rf(ctx, includeInactive) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]content.IndexBlobInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { + r1 = rf(ctx, includeInactive) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewDirectWriter provides a mock function with given fields: ctx, opt +func (_m *DirectRepositoryWriter) NewDirectWriter(ctx context.Context, opt repo.WriteSessionOptions) (context.Context, repo.DirectRepositoryWriter, error) { + ret := _m.Called(ctx, opt) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + var r1 repo.DirectRepositoryWriter + if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.DirectRepositoryWriter); ok { + r1 = rf(ctx, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(repo.DirectRepositoryWriter) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { + r2 = rf(ctx, opt) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewObjectWriter provides a mock function with given fields: ctx, opt +func (_m *DirectRepositoryWriter) NewObjectWriter(ctx context.Context, opt object.WriterOptions) object.Writer { + ret := _m.Called(ctx, opt) + + var r0 object.Writer + if rf, ok := ret.Get(0).(func(context.Context, object.WriterOptions) object.Writer); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(object.Writer) + } + } + + return r0 +} + +// NewWriter provides a mock function with given fields: ctx, opt +func (_m *DirectRepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error) { + ret := _m.Called(ctx, opt) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { + r0 = rf(ctx, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + var r1 repo.RepositoryWriter + if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { + r1 = rf(ctx, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(repo.RepositoryWriter) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { + r2 = rf(ctx, opt) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ObjectFormat provides a mock function with given fields: +func (_m *DirectRepositoryWriter) ObjectFormat() object.Format { + ret := _m.Called() + + var r0 object.Format + if rf, ok := ret.Get(0).(func() object.Format); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(object.Format) + } + + return r0 +} + +// OpenObject provides a mock function with given fields: ctx, id +func (_m *DirectRepositoryWriter) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { + ret := _m.Called(ctx, id) + + var r0 object.Reader + if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(object.Reader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PrefetchContents provides a mock function with given fields: ctx, contentIDs, hint +func (_m *DirectRepositoryWriter) PrefetchContents(ctx context.Context, contentIDs []index.ID, hint string) []index.ID { + ret := _m.Called(ctx, contentIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []index.ID, string) []index.ID); ok { + r0 = rf(ctx, contentIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + return r0 +} + +// PrefetchObjects provides a mock function with given fields: ctx, objectIDs, hint +func (_m *DirectRepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs []object.ID, hint string) ([]index.ID, error) { + ret := _m.Called(ctx, objectIDs, hint) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { + r0 = rf(ctx, objectIDs, hint) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { + r1 = rf(ctx, objectIDs, hint) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PutManifest provides a mock function with given fields: ctx, labels, payload +func (_m *DirectRepositoryWriter) PutManifest(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + ret := _m.Called(ctx, labels, payload) + + var r0 manifest.ID + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { + r0 = rf(ctx, labels, payload) + } else { + r0 = ret.Get(0).(manifest.ID) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { + r1 = rf(ctx, labels, payload) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Refresh provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) Refresh(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RollbackUpgrade provides a mock function with given fields: ctx +func (_m *DirectRepositoryWriter) RollbackUpgrade(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetParameters provides a mock function with given fields: ctx, m, blobcfg +func (_m *DirectRepositoryWriter) SetParameters(ctx context.Context, m content.MutableParameters, blobcfg content.BlobCfgBlob) error { + ret := _m.Called(ctx, m, blobcfg) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, content.MutableParameters, content.BlobCfgBlob) error); ok { + r0 = rf(ctx, m, blobcfg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetUpgradeLockIntent provides a mock function with given fields: ctx, l +func (_m *DirectRepositoryWriter) SetUpgradeLockIntent(ctx context.Context, l content.UpgradeLock) (*content.UpgradeLock, error) { + ret := _m.Called(ctx, l) + + var r0 *content.UpgradeLock + if rf, ok := ret.Get(0).(func(context.Context, content.UpgradeLock) *content.UpgradeLock); ok { + r0 = rf(ctx, l) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*content.UpgradeLock) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, content.UpgradeLock) error); ok { + r1 = rf(ctx, l) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Throttler provides a mock function with given fields: +func (_m *DirectRepositoryWriter) Throttler() throttling.SettableThrottler { + ret := _m.Called() + + var r0 throttling.SettableThrottler + if rf, ok := ret.Get(0).(func() throttling.SettableThrottler); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(throttling.SettableThrottler) + } + } + + return r0 +} + +// Time provides a mock function with given fields: +func (_m *DirectRepositoryWriter) Time() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// Token provides a mock function with given fields: password +func (_m *DirectRepositoryWriter) Token(password string) (string, error) { + ret := _m.Called(password) + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(password) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(password) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UniqueID provides a mock function with given fields: +func (_m *DirectRepositoryWriter) UniqueID() []byte { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// UpdateDescription provides a mock function with given fields: d +func (_m *DirectRepositoryWriter) UpdateDescription(d string) { + _m.Called(d) +} + +// VerifyObject provides a mock function with given fields: ctx, id +func (_m *DirectRepositoryWriter) VerifyObject(ctx context.Context, id object.ID) ([]index.ID, error) { + ret := _m.Called(ctx, id) + + var r0 []index.ID + if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]index.ID) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewDirectRepositoryWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewDirectRepositoryWriter creates a new instance of DirectRepositoryWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDirectRepositoryWriter(t mockConstructorTestingTNewDirectRepositoryWriter) *DirectRepositoryWriter { + mock := &DirectRepositoryWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/udmrepo/kopialib/lib_repo.go b/pkg/repository/udmrepo/kopialib/lib_repo.go new file mode 100644 index 0000000000..45dc849276 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/lib_repo.go @@ -0,0 +1,587 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopialib + +import ( + "context" + "os" + "strings" + "sync/atomic" + "time" + + "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/compression" + "github.com/kopia/kopia/repo/content/index" + "github.com/kopia/kopia/repo/maintenance" + "github.com/kopia/kopia/repo/manifest" + "github.com/kopia/kopia/repo/object" + "github.com/kopia/kopia/snapshot/snapshotmaintenance" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/util/logging" +) + +type kopiaRepoService struct { + logger logrus.FieldLogger +} + +type kopiaRepository struct { + rawRepo repo.Repository + rawWriter repo.RepositoryWriter + description string + uploaded int64 + openTime time.Time + throttle logThrottle + logger logrus.FieldLogger +} + +type kopiaMaintenance struct { + mode maintenance.Mode + startTime time.Time + uploaded int64 + throttle logThrottle + logger logrus.FieldLogger +} + +type logThrottle struct { + lastTime int64 + interval time.Duration +} + +type kopiaObjectReader struct { + rawReader object.Reader +} + +type kopiaObjectWriter struct { + rawWriter object.Writer +} + +const ( + defaultLogInterval = time.Duration(time.Second * 10) + defaultMaintainCheckPeriod = time.Hour + overwriteFullMaintainInterval = time.Duration(0) + overwriteQuickMaintainInterval = time.Duration(0) +) + +var kopiaRepoOpen = repo.Open + +// NewKopiaRepoService creates an instance of BackupRepoService implemented by Kopia +func NewKopiaRepoService(logger logrus.FieldLogger) udmrepo.BackupRepoService { + ks := &kopiaRepoService{ + logger: logger, + } + + return ks +} + +func (ks *kopiaRepoService) Init(ctx context.Context, repoOption udmrepo.RepoOptions, createNew bool) error { + repoCtx := logging.SetupKopiaLog(ctx, ks.logger) + + if createNew { + if err := CreateBackupRepo(repoCtx, repoOption); err != nil { + return err + } + + return writeInitParameters(repoCtx, repoOption, ks.logger) + } else { + return ConnectBackupRepo(repoCtx, repoOption) + } +} + +func (ks *kopiaRepoService) Open(ctx context.Context, repoOption udmrepo.RepoOptions) (udmrepo.BackupRepo, error) { + repoConfig := repoOption.ConfigFilePath + if repoConfig == "" { + return nil, errors.New("invalid config file path") + } + + if _, err := os.Stat(repoConfig); os.IsNotExist(err) { + return nil, errors.Wrapf(err, "repo config %s doesn't exist", repoConfig) + } + + repoCtx := logging.SetupKopiaLog(ctx, ks.logger) + + r, err := openKopiaRepo(repoCtx, repoConfig, repoOption.RepoPassword) + if err != nil { + return nil, err + } + + kr := kopiaRepository{ + rawRepo: r, + openTime: time.Now(), + description: repoOption.Description, + throttle: logThrottle{ + interval: defaultLogInterval, + }, + logger: ks.logger, + } + + _, kr.rawWriter, err = r.NewWriter(repoCtx, repo.WriteSessionOptions{ + Purpose: repoOption.Description, + OnUpload: kr.updateProgress, + }) + + if err != nil { + if e := r.Close(repoCtx); e != nil { + ks.logger.WithError(e).Error("Failed to close raw repository on error") + } + + return nil, errors.Wrap(err, "error to create repo writer") + } + + return &kr, nil +} + +func (ks *kopiaRepoService) Maintain(ctx context.Context, repoOption udmrepo.RepoOptions) error { + repoConfig := repoOption.ConfigFilePath + if repoConfig == "" { + return errors.New("invalid config file path") + } + + if _, err := os.Stat(repoConfig); os.IsNotExist(err) { + return errors.Wrapf(err, "repo config %s doesn't exist", repoConfig) + } + + repoCtx := logging.SetupKopiaLog(ctx, ks.logger) + + r, err := openKopiaRepo(repoCtx, repoConfig, repoOption.RepoPassword) + if err != nil { + return err + } + + defer func() { + c := r.Close(repoCtx) + if c != nil { + ks.logger.WithError(c).Error("Failed to close repo") + } + }() + + km := kopiaMaintenance{ + mode: maintenance.ModeAuto, + startTime: time.Now(), + throttle: logThrottle{ + interval: defaultLogInterval, + }, + logger: ks.logger, + } + + if mode, exist := repoOption.GeneralOptions[udmrepo.GenOptionMaintainMode]; exist { + if strings.EqualFold(mode, udmrepo.GenOptionMaintainFull) { + km.mode = maintenance.ModeFull + } else if strings.EqualFold(mode, udmrepo.GenOptionMaintainQuick) { + km.mode = maintenance.ModeQuick + } + } + + err = repo.DirectWriteSession(repoCtx, r.(repo.DirectRepository), repo.WriteSessionOptions{ + Purpose: "UdmRepoMaintenance", + OnUpload: km.maintainProgress, + }, func(ctx context.Context, dw repo.DirectRepositoryWriter) error { + return km.runMaintenance(ctx, dw) + }) + + if err != nil { + return errors.Wrap(err, "error to maintain repo") + } + + return nil +} + +func (ks *kopiaRepoService) DefaultMaintenanceFrequency() time.Duration { + return defaultMaintainCheckPeriod +} + +func (km *kopiaMaintenance) runMaintenance(ctx context.Context, rep repo.DirectRepositoryWriter) error { + err := snapshotmaintenance.Run(logging.SetupKopiaLog(ctx, km.logger), rep, km.mode, false, maintenance.SafetyFull) + if err != nil { + return errors.Wrapf(err, "error to run maintenance under mode %s", km.mode) + } + + return nil +} + +// maintainProgress is called when the repository writes a piece of blob data to the storage during the maintenance +func (km *kopiaMaintenance) maintainProgress(uploaded int64) { + total := atomic.AddInt64(&km.uploaded, uploaded) + + if km.throttle.shouldLog() { + km.logger.WithFields( + logrus.Fields{ + "Start Time": km.startTime.Format(time.RFC3339Nano), + "Current": time.Now().Format(time.RFC3339Nano), + }, + ).Debugf("Repo maintenance uploaded %d bytes.", total) + } +} + +func (kr *kopiaRepository) OpenObject(ctx context.Context, id udmrepo.ID) (udmrepo.ObjectReader, error) { + if kr.rawRepo == nil { + return nil, errors.New("repo is closed or not open") + } + + reader, err := kr.rawRepo.OpenObject(logging.SetupKopiaLog(ctx, kr.logger), object.ID(id)) + if err != nil { + return nil, errors.Wrap(err, "error to open object") + } + + return &kopiaObjectReader{ + rawReader: reader, + }, nil +} + +func (kr *kopiaRepository) GetManifest(ctx context.Context, id udmrepo.ID, mani *udmrepo.RepoManifest) error { + if kr.rawRepo == nil { + return errors.New("repo is closed or not open") + } + + metadata, err := kr.rawRepo.GetManifest(logging.SetupKopiaLog(ctx, kr.logger), manifest.ID(id), mani.Payload) + if err != nil { + return errors.Wrap(err, "error to get manifest") + } + + mani.Metadata = getManifestEntryFromKopia(metadata) + + return nil +} + +func (kr *kopiaRepository) FindManifests(ctx context.Context, filter udmrepo.ManifestFilter) ([]*udmrepo.ManifestEntryMetadata, error) { + if kr.rawRepo == nil { + return nil, errors.New("repo is closed or not open") + } + + metadata, err := kr.rawRepo.FindManifests(logging.SetupKopiaLog(ctx, kr.logger), filter.Labels) + if err != nil { + return nil, errors.Wrap(err, "error to find manifests") + } + + return getManifestEntriesFromKopia(metadata), nil +} + +func (kr *kopiaRepository) Time() time.Time { + if kr.rawRepo == nil { + return time.Time{} + } + + return kr.rawRepo.Time() +} + +func (kr *kopiaRepository) Close(ctx context.Context) error { + if kr.rawWriter != nil { + err := kr.rawWriter.Close(logging.SetupKopiaLog(ctx, kr.logger)) + if err != nil { + return errors.Wrap(err, "error to close repo writer") + } + + kr.rawWriter = nil + } + + if kr.rawRepo != nil { + err := kr.rawRepo.Close(logging.SetupKopiaLog(ctx, kr.logger)) + if err != nil { + return errors.Wrap(err, "error to close repo") + } + + kr.rawRepo = nil + } + + return nil +} + +func (kr *kopiaRepository) NewObjectWriter(ctx context.Context, opt udmrepo.ObjectWriteOptions) udmrepo.ObjectWriter { + if kr.rawWriter == nil { + return nil + } + + writer := kr.rawWriter.NewObjectWriter(logging.SetupKopiaLog(ctx, kr.logger), object.WriterOptions{ + Description: opt.Description, + Prefix: index.ID(opt.Prefix), + AsyncWrites: getAsyncWrites(), + Compressor: getCompressorForObject(opt), + }) + + if writer == nil { + return nil + } + + return &kopiaObjectWriter{ + rawWriter: writer, + } +} + +func (kr *kopiaRepository) PutManifest(ctx context.Context, manifest udmrepo.RepoManifest) (udmrepo.ID, error) { + if kr.rawWriter == nil { + return "", errors.New("repo writer is closed or not open") + } + + id, err := kr.rawWriter.PutManifest(logging.SetupKopiaLog(ctx, kr.logger), manifest.Metadata.Labels, manifest.Payload) + if err != nil { + return "", errors.Wrap(err, "error to put manifest") + } + + return udmrepo.ID(id), nil +} + +func (kr *kopiaRepository) DeleteManifest(ctx context.Context, id udmrepo.ID) error { + if kr.rawWriter == nil { + return errors.New("repo writer is closed or not open") + } + + err := kr.rawWriter.DeleteManifest(logging.SetupKopiaLog(ctx, kr.logger), manifest.ID(id)) + if err != nil { + return errors.Wrap(err, "error to delete manifest") + } + + return nil +} + +func (kr *kopiaRepository) Flush(ctx context.Context) error { + if kr.rawWriter == nil { + return errors.New("repo writer is closed or not open") + } + + err := kr.rawWriter.Flush(logging.SetupKopiaLog(ctx, kr.logger)) + if err != nil { + return errors.Wrap(err, "error to flush repo") + } + + return nil +} + +// updateProgress is called when the repository writes a piece of blob data to the storage during data write +func (kr *kopiaRepository) updateProgress(uploaded int64) { + total := atomic.AddInt64(&kr.uploaded, uploaded) + + if kr.throttle.shouldLog() { + kr.logger.WithFields( + logrus.Fields{ + "Description": kr.description, + "Open Time": kr.openTime.Format(time.RFC3339Nano), + "Current": time.Now().Format(time.RFC3339Nano), + }, + ).Debugf("Repo uploaded %d bytes.", total) + } +} + +func (kor *kopiaObjectReader) Read(p []byte) (int, error) { + if kor.rawReader == nil { + return 0, errors.New("object reader is closed or not open") + } + + n, err := kor.rawReader.Read(p) + if err != nil { + return 0, errors.Wrap(err, "error to read object") + } + + return n, nil +} + +func (kor *kopiaObjectReader) Seek(offset int64, whence int) (int64, error) { + if kor.rawReader == nil { + return -1, errors.New("object reader is closed or not open") + } + + p, err := kor.rawReader.Seek(offset, whence) + if err != nil { + return -1, errors.Wrap(err, "error to seek object") + } + + return p, nil +} + +func (kor *kopiaObjectReader) Close() error { + if kor.rawReader == nil { + return nil + } + + err := kor.rawReader.Close() + if err != nil { + return errors.Wrap(err, "error to close object reader") + } + + kor.rawReader = nil + + return nil +} + +func (kor *kopiaObjectReader) Length() int64 { + if kor.rawReader == nil { + return -1 + } + + return kor.rawReader.Length() +} + +func (kow *kopiaObjectWriter) Write(p []byte) (int, error) { + if kow.rawWriter == nil { + return 0, errors.New("object writer is closed or not open") + } + + n, err := kow.rawWriter.Write(p) + if err != nil { + return 0, errors.Wrap(err, "error to write object") + } + + return n, nil +} + +func (kow *kopiaObjectWriter) Seek(offset int64, whence int) (int64, error) { + return -1, errors.New("not supported") +} + +func (kow *kopiaObjectWriter) Checkpoint() (udmrepo.ID, error) { + if kow.rawWriter == nil { + return udmrepo.ID(""), errors.New("object writer is closed or not open") + } + + id, err := kow.rawWriter.Checkpoint() + if err != nil { + return udmrepo.ID(""), errors.Wrap(err, "error to checkpoint object") + } + + return udmrepo.ID(id), nil +} + +func (kow *kopiaObjectWriter) Result() (udmrepo.ID, error) { + if kow.rawWriter == nil { + return udmrepo.ID(""), errors.New("object writer is closed or not open") + } + + id, err := kow.rawWriter.Result() + if err != nil { + return udmrepo.ID(""), errors.Wrap(err, "error to wait object") + } + + return udmrepo.ID(id), nil +} + +func (kow *kopiaObjectWriter) Close() error { + if kow.rawWriter == nil { + return nil + } + + err := kow.rawWriter.Close() + if err != nil { + return errors.Wrap(err, "error to close object writer") + } + + kow.rawWriter = nil + + return nil +} + +// getAsyncWrites returns the number of async writes, at present, we don't support async writes +func getAsyncWrites() int { + return 0 +} + +// getCompressorForObject returns the compressor for an object, at present, we don't support compression +func getCompressorForObject(opt udmrepo.ObjectWriteOptions) compression.Name { + return "" +} + +func getManifestEntryFromKopia(kMani *manifest.EntryMetadata) *udmrepo.ManifestEntryMetadata { + return &udmrepo.ManifestEntryMetadata{ + ID: udmrepo.ID(kMani.ID), + Labels: kMani.Labels, + Length: int32(kMani.Length), + ModTime: kMani.ModTime, + } +} + +func getManifestEntriesFromKopia(kMani []*manifest.EntryMetadata) []*udmrepo.ManifestEntryMetadata { + var ret []*udmrepo.ManifestEntryMetadata + + for _, entry := range kMani { + ret = append(ret, &udmrepo.ManifestEntryMetadata{ + ID: udmrepo.ID(entry.ID), + Labels: entry.Labels, + Length: int32(entry.Length), + ModTime: entry.ModTime, + }) + } + + return ret +} + +func (lt *logThrottle) shouldLog() bool { + nextOutputTime := atomic.LoadInt64((*int64)(<.lastTime)) + if nowNano := time.Now().UnixNano(); nowNano > nextOutputTime { + if atomic.CompareAndSwapInt64((*int64)(<.lastTime), nextOutputTime, nowNano+lt.interval.Nanoseconds()) { + return true + } + } + + return false +} + +func openKopiaRepo(ctx context.Context, configFile string, password string) (repo.Repository, error) { + r, err := kopiaRepoOpen(ctx, configFile, password, &repo.Options{}) + if os.IsNotExist(err) { + return nil, errors.Wrap(err, "error to open repo, repo doesn't exist") + } + + if err != nil { + return nil, errors.Wrap(err, "error to open repo") + } + + return r, nil +} + +func writeInitParameters(ctx context.Context, repoOption udmrepo.RepoOptions, logger logrus.FieldLogger) error { + r, err := openKopiaRepo(ctx, repoOption.ConfigFilePath, repoOption.RepoPassword) + if err != nil { + return err + } + + defer func() { + c := r.Close(ctx) + if c != nil { + logger.WithError(c).Error("Failed to close repo") + } + }() + + err = repo.WriteSession(ctx, r, repo.WriteSessionOptions{ + Purpose: "set init parameters", + }, func(ctx context.Context, w repo.RepositoryWriter) error { + p := maintenance.DefaultParams() + + if overwriteFullMaintainInterval != time.Duration(0) { + logger.Infof("Full maintenance interval change from %v to %v", p.FullCycle.Interval, overwriteFullMaintainInterval) + p.FullCycle.Interval = overwriteFullMaintainInterval + } + + if overwriteQuickMaintainInterval != time.Duration(0) { + logger.Infof("Quick maintenance interval change from %v to %v", p.QuickCycle.Interval, overwriteQuickMaintainInterval) + p.QuickCycle.Interval = overwriteQuickMaintainInterval + } + + p.Owner = r.ClientOptions().UsernameAtHost() + + if err := maintenance.SetParams(ctx, w, &p); err != nil { + return errors.Wrap(err, "error to set maintenance params") + } + + return nil + }) + + if err != nil { + return errors.Wrap(err, "error to init write repo parameters") + } + + return nil +} diff --git a/pkg/repository/udmrepo/kopialib/lib_repo_test.go b/pkg/repository/udmrepo/kopialib/lib_repo_test.go new file mode 100644 index 0000000000..e92bd5ebd8 --- /dev/null +++ b/pkg/repository/udmrepo/kopialib/lib_repo_test.go @@ -0,0 +1,406 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kopialib + +import ( + "context" + "os" + "testing" + "time" + + "github.com/kopia/kopia/repo" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + repomocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib/backend/mocks" + + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestOpen(t *testing.T) { + var directRpo *repomocks.DirectRepository + testCases := []struct { + name string + repoOptions udmrepo.RepoOptions + returnRepo *repomocks.DirectRepository + repoOpen func(context.Context, string, string, *repo.Options) (repo.Repository, error) + newWriterError error + expectedErr string + expected *kopiaRepository + }{ + { + name: "invalid config file", + expectedErr: "invalid config file path", + }, + { + name: "config file doesn't exist", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + }, + expectedErr: "repo config fake-file doesn't exist: stat fake-file: no such file or directory", + }, + { + name: "repo open fail, repo not exist", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, os.ErrNotExist + }, + expectedErr: "error to open repo, repo doesn't exist: file does not exist", + }, + { + name: "repo open fail, other error", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, errors.New("fake-repo-open-error") + }, + expectedErr: "error to open repo: fake-repo-open-error", + }, + { + name: "create repository writer fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + newWriterError: errors.New("fake-new-writer-error"), + expectedErr: "error to create repo writer: fake-new-writer-error", + }, + { + name: "create repository success", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + Description: "fake-description", + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + expected: &kopiaRepository{ + description: "fake-description", + throttle: logThrottle{ + interval: defaultLogInterval, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + logger := velerotest.NewLogger() + + service := kopiaRepoService{ + logger: logger, + } + + if tc.repoOpen != nil { + kopiaRepoOpen = tc.repoOpen + } + + if tc.returnRepo != nil { + directRpo = tc.returnRepo + } + + if tc.returnRepo != nil { + tc.returnRepo.On("NewWriter", mock.Anything, mock.Anything).Return(nil, nil, tc.newWriterError) + tc.returnRepo.On("Close", mock.Anything).Return(nil) + } + + repo, err := service.Open(context.Background(), tc.repoOptions) + + if repo != nil { + require.Equal(t, tc.expected.description, repo.(*kopiaRepository).description) + require.Equal(t, tc.expected.throttle.interval, repo.(*kopiaRepository).throttle.interval) + require.Equal(t, repo.(*kopiaRepository).logger, logger) + } + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestMaintain(t *testing.T) { + var directRpo *repomocks.DirectRepository + testCases := []struct { + name string + repoOptions udmrepo.RepoOptions + returnRepo *repomocks.DirectRepository + returnRepoWriter *repomocks.DirectRepositoryWriter + repoOpen func(context.Context, string, string, *repo.Options) (repo.Repository, error) + newRepoWriterError error + findManifestError error + expectedErr string + }{ + { + name: "invalid config file", + expectedErr: "invalid config file path", + }, + { + name: "config file doesn't exist", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "fake-file", + }, + expectedErr: "repo config fake-file doesn't exist: stat fake-file: no such file or directory", + }, + { + name: "repo open fail, repo not exist", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, os.ErrNotExist + }, + expectedErr: "error to open repo, repo doesn't exist: file does not exist", + }, + { + name: "repo open fail, other error", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, errors.New("fake-repo-open-error") + }, + expectedErr: "error to open repo: fake-repo-open-error", + }, + { + name: "write session fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + newRepoWriterError: errors.New("fake-new-direct-writer-error"), + expectedErr: "error to maintain repo: unable to create direct writer: fake-new-direct-writer-error", + }, + { + name: "maintain fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + returnRepoWriter: new(repomocks.DirectRepositoryWriter), + findManifestError: errors.New("fake-find-manifest-error"), + expectedErr: "error to maintain repo: error to run maintenance under mode auto: unable to get maintenance params: error looking for maintenance manifest: fake-find-manifest-error", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + logger := velerotest.NewLogger() + ctx := context.Background() + + service := kopiaRepoService{ + logger: logger, + } + + if tc.repoOpen != nil { + kopiaRepoOpen = tc.repoOpen + } + + if tc.returnRepo != nil { + directRpo = tc.returnRepo + } + + if tc.returnRepo != nil { + tc.returnRepo.On("NewDirectWriter", mock.Anything, mock.Anything).Return(ctx, tc.returnRepoWriter, tc.newRepoWriterError) + tc.returnRepo.On("Close", mock.Anything).Return(nil) + } + + if tc.returnRepoWriter != nil { + tc.returnRepoWriter.On("DisableIndexRefresh").Return() + tc.returnRepoWriter.On("AlsoLogToContentLog", mock.Anything).Return(nil) + tc.returnRepoWriter.On("Close", mock.Anything).Return(nil) + tc.returnRepoWriter.On("FindManifests", mock.Anything, mock.Anything).Return(nil, tc.findManifestError) + } + + err := service.Maintain(ctx, tc.repoOptions) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestWriteInitParameters(t *testing.T) { + var directRpo *repomocks.DirectRepository + testCases := []struct { + name string + repoOptions udmrepo.RepoOptions + returnRepo *repomocks.DirectRepository + returnRepoWriter *repomocks.DirectRepositoryWriter + repoOpen func(context.Context, string, string, *repo.Options) (repo.Repository, error) + newRepoWriterError error + findManifestError error + expectedErr string + }{ + { + name: "repo open fail, repo not exist", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, os.ErrNotExist + }, + expectedErr: "error to open repo, repo doesn't exist: file does not exist", + }, + { + name: "repo open fail, other error", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return nil, errors.New("fake-repo-open-error") + }, + expectedErr: "error to open repo: fake-repo-open-error", + }, + { + name: "write session fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + newRepoWriterError: errors.New("fake-new-writer-error"), + expectedErr: "error to init write repo parameters: unable to create writer: fake-new-writer-error", + }, + { + name: "set repo param fail", + repoOptions: udmrepo.RepoOptions{ + ConfigFilePath: "/tmp", + GeneralOptions: map[string]string{}, + }, + repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { + return directRpo, nil + }, + returnRepo: new(repomocks.DirectRepository), + returnRepoWriter: new(repomocks.DirectRepositoryWriter), + findManifestError: errors.New("fake-find-manifest-error"), + expectedErr: "error to init write repo parameters: error to set maintenance params: error looking for maintenance manifest: fake-find-manifest-error", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + logger := velerotest.NewLogger() + ctx := context.Background() + + if tc.repoOpen != nil { + kopiaRepoOpen = tc.repoOpen + } + + if tc.returnRepo != nil { + directRpo = tc.returnRepo + } + + if tc.returnRepo != nil { + tc.returnRepo.On("NewWriter", mock.Anything, mock.Anything).Return(ctx, tc.returnRepoWriter, tc.newRepoWriterError) + tc.returnRepo.On("ClientOptions").Return(repo.ClientOptions{}) + tc.returnRepo.On("Close", mock.Anything).Return(nil) + } + + if tc.returnRepoWriter != nil { + tc.returnRepoWriter.On("Close", mock.Anything).Return(nil) + tc.returnRepoWriter.On("FindManifests", mock.Anything, mock.Anything).Return(nil, tc.findManifestError) + } + + err := writeInitParameters(ctx, tc.repoOptions, logger) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestShouldLog(t *testing.T) { + testCases := []struct { + name string + lastTime int64 + interval time.Duration + retValue bool + }{ + { + name: "first time", + retValue: true, + }, + { + name: "not run", + lastTime: time.Now().Add(time.Hour).UnixNano(), + interval: time.Second * 10, + }, + { + name: "not first time, run", + lastTime: time.Now().Add(-time.Hour).UnixNano(), + interval: time.Second * 10, + retValue: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + lt := logThrottle{ + lastTime: tc.lastTime, + interval: tc.interval, + } + + before := lt.lastTime + + nw := time.Now() + + s := lt.shouldLog() + + require.Equal(t, s, tc.retValue) + + if s { + require.GreaterOrEqual(t, lt.lastTime-nw.UnixNano(), lt.interval) + } else { + require.Equal(t, lt.lastTime, before) + } + }) + } +} diff --git a/pkg/repository/udmrepo/mocks/BackupRepoService.go b/pkg/repository/udmrepo/mocks/BackupRepoService.go index 135c0058c4..b61bf5ccc3 100644 --- a/pkg/repository/udmrepo/mocks/BackupRepoService.go +++ b/pkg/repository/udmrepo/mocks/BackupRepoService.go @@ -4,8 +4,10 @@ package mocks import ( context "context" + time "time" mock "github.com/stretchr/testify/mock" + udmrepo "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" ) @@ -14,6 +16,20 @@ type BackupRepoService struct { mock.Mock } +// DefaultMaintenanceFrequency provides a mock function with given fields: +func (_m *BackupRepoService) DefaultMaintenanceFrequency() time.Duration { + ret := _m.Called() + + var r0 time.Duration + if rf, ok := ret.Get(0).(func() time.Duration); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Duration) + } + + return r0 +} + // Init provides a mock function with given fields: ctx, repoOption, createNew func (_m *BackupRepoService) Init(ctx context.Context, repoOption udmrepo.RepoOptions, createNew bool) error { ret := _m.Called(ctx, repoOption, createNew) diff --git a/pkg/repository/udmrepo/repo.go b/pkg/repository/udmrepo/repo.go index 01d434fdad..6bec441898 100644 --- a/pkg/repository/udmrepo/repo.go +++ b/pkg/repository/udmrepo/repo.go @@ -84,6 +84,10 @@ type BackupRepoService interface { // Maintain is periodically called to maintain the backup repository to eliminate redundant data. // repoOption: options to maintain the backup repository. Maintain(ctx context.Context, repoOption RepoOptions) error + + // DefaultMaintenanceFrequency returns the defgault frequency of maintenance, callers refer this + // frequency to maintain the backup repository to get the best maintenance performance + DefaultMaintenanceFrequency() time.Duration } // BackupRepo provides the access to the backup repository diff --git a/pkg/repository/udmrepo/service/service.go b/pkg/repository/udmrepo/service/service.go index 445063ff04..c2f0a9b0e6 100644 --- a/pkg/repository/udmrepo/service/service.go +++ b/pkg/repository/udmrepo/service/service.go @@ -20,10 +20,10 @@ import ( "github.com/sirupsen/logrus" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib" ) // Create creates an instance of BackupRepoService func Create(logger logrus.FieldLogger) udmrepo.BackupRepoService { - ///TODO: create from kopiaLib - return nil + return kopialib.NewKopiaRepoService(logger) } diff --git a/pkg/util/logging/kopia_log.go b/pkg/util/logging/kopia_log.go new file mode 100644 index 0000000000..59fac6dc38 --- /dev/null +++ b/pkg/util/logging/kopia_log.go @@ -0,0 +1,90 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logging + +import ( + "context" + + "github.com/kopia/kopia/repo/logging" + "github.com/sirupsen/logrus" +) + +type kopiaLog struct { + module string + logger logrus.FieldLogger +} + +// SetupKopiaLog sets the Kopia log handler to the specific context, Kopia modules +// call the logger in the context to write logs +func SetupKopiaLog(ctx context.Context, logger logrus.FieldLogger) context.Context { + return logging.WithLogger(ctx, func(module string) logging.Logger { + return &kopiaLog{ + module: module, + logger: logger, + } + }) +} + +func (kl *kopiaLog) Debugf(msg string, args ...interface{}) { + logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger.Debugf(msg, args...) +} + +func (kl *kopiaLog) Debugw(msg string, keyValuePairs ...interface{}) { + logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger.WithFields(getLogFields(keyValuePairs...)).Debug(msg) +} + +func (kl *kopiaLog) Infof(msg string, args ...interface{}) { + logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger.Infof(msg, args...) +} + +func (kl *kopiaLog) Warnf(msg string, args ...interface{}) { + logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger.Warnf(msg, args...) +} + +// We see Kopia generates error logs for some normal cases or non-critical +// cases. So Kopia's error logs are regarded as warning logs so that they don't +// affect Velero's workflow. +func (kl *kopiaLog) Errorf(msg string, args ...interface{}) { + logger := kl.logger.WithFields(logrus.Fields{ + "logSource": kl.getLogSource(), + "sublevel": "error", + }) + + logger.Warnf(msg, args...) +} + +func (kl *kopiaLog) getLogSource() string { + return "kopia/" + kl.module +} + +func getLogFields(keyValuePairs ...interface{}) map[string]interface{} { + m := map[string]interface{}{} + for i := 0; i+1 < len(keyValuePairs); i += 2 { + s, ok := keyValuePairs[i].(string) + if !ok { + s = "non-string-key" + } + + m[s] = keyValuePairs[i+1] + } + + return m +} diff --git a/pkg/util/logging/kopia_log_test.go b/pkg/util/logging/kopia_log_test.go new file mode 100644 index 0000000000..2498cb418b --- /dev/null +++ b/pkg/util/logging/kopia_log_test.go @@ -0,0 +1,86 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logging + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetLogFields(t *testing.T) { + testCases := []struct { + name string + pairs []interface{} + expected map[string]interface{} + }{ + { + name: "normal", + pairs: []interface{}{ + "fake-key1", + "fake-value1", + "fake-key2", + 10, + "fake-key3", + struct{ v int }{v: 10}, + }, + expected: map[string]interface{}{ + "fake-key1": "fake-value1", + "fake-key2": 10, + "fake-key3": struct{ v int }{v: 10}, + }, + }, + { + name: "non string key", + pairs: []interface{}{ + "fake-key1", + "fake-value1", + 10, + 10, + "fake-key3", + struct{ v int }{v: 10}, + }, + expected: map[string]interface{}{ + "fake-key1": "fake-value1", + "non-string-key": 10, + "fake-key3": struct{ v int }{v: 10}, + }, + }, + { + name: "missing value", + pairs: []interface{}{ + "fake-key1", + "fake-value1", + "fake-key2", + 10, + "fake-key3", + }, + expected: map[string]interface{}{ + "fake-key1": "fake-value1", + "fake-key2": 10, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + m := getLogFields(tc.pairs...) + + require.Equal(t, tc.expected, m) + }) + } +} From e77aaa32cade1d909c2a5b8e34834d3eff4a3c85 Mon Sep 17 00:00:00 2001 From: cleverhu Date: Sun, 28 Aug 2022 15:09:01 +0800 Subject: [PATCH 271/366] add CSISnapshotTimeout for describe backup Signed-off-by: cleverhu --- changelogs/unreleased/5252-cleverhu | 1 + pkg/cmd/util/output/backup_describer.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5252-cleverhu diff --git a/changelogs/unreleased/5252-cleverhu b/changelogs/unreleased/5252-cleverhu new file mode 100644 index 0000000000..d478a57505 --- /dev/null +++ b/changelogs/unreleased/5252-cleverhu @@ -0,0 +1 @@ +Add csiSnapshotTimeout for describe backup \ No newline at end of file diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 2bc742c9f5..f8c05c1567 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -110,7 +110,6 @@ func DescribeBackup( d.Println() DescribePodVolumeBackups(d, podVolumeBackups, details) } - }) } @@ -165,6 +164,9 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { d.Println() d.Printf("TTL:\t%s\n", spec.TTL.Duration) + d.Println() + d.Printf("CSISnapshotTimeout:\t%s\n", &spec.CSISnapshotTimeout.Duration) + d.Println() if len(spec.Hooks.Resources) == 0 { d.Printf("Hooks:\t\n") @@ -241,7 +243,6 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { d.Printf("\t%s: %s\n", key, value) } } - } // DescribeBackupStatus describes a backup status in human-readable format. From 4a5647a891105968aa86a17e3fe9915de13cafd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 29 Aug 2022 09:43:34 +0800 Subject: [PATCH 272/366] Move the code in pkg/restic/common.go to the proper package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the code in pkg/restic/common.go to the proper package Fixes #5243 Signed-off-by: Wenkai Yin(尹文开) --- internal/hook/item_hook_handler.go | 4 +- pkg/cmd/cli/restore/describe.go | 12 +- pkg/cmd/server/server.go | 5 +- pkg/controller/backup_deletion_controller.go | 33 +++- .../backup_deletion_controller_test.go | 172 +++++++++++++++++ .../pod_volume_restore_controller.go | 5 +- .../pod_volume_restore_controller_test.go | 20 +- pkg/podvolume/util.go | 4 + pkg/repository/manager.go | 20 +- pkg/repository/mocks/repository_manager.go | 6 +- pkg/restic/common.go | 64 ------- pkg/restic/common_test.go | 173 ------------------ pkg/restore/restic_restore_action.go | 5 +- 13 files changed, 258 insertions(+), 265 deletions(-) diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 83c756bd53..062922a056 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -36,7 +36,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -126,7 +126,7 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( // restored data to be consumed by the application container(s). // So if there is a "restic-wait" init container already on the pod at index 0, we'll preserve that and run // it before running any other init container. - if len(pod.Spec.InitContainers) > 0 && pod.Spec.InitContainers[0].Name == restic.InitContainer { + if len(pod.Spec.InitContainers) > 0 && pod.Spec.InitContainers[0].Name == podvolume.InitContainer { initContainers = append(initContainers, pod.Spec.InitContainers[0]) pod.Spec.InitContainers = pod.Spec.InitContainers[1:] } diff --git a/pkg/cmd/cli/restore/describe.go b/pkg/cmd/cli/restore/describe.go index 3a8c797a61..76ef3a43b0 100644 --- a/pkg/cmd/cli/restore/describe.go +++ b/pkg/cmd/cli/restore/describe.go @@ -28,7 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/label" ) func NewDescribeCommand(f client.Factory, use string) *cobra.Command { @@ -69,7 +69,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { first := true for _, restore := range restores.Items { - opts := restic.NewPodVolumeRestoreListOptions(restore.Name) + opts := newPodVolumeRestoreListOptions(restore.Name) podvolumeRestoreList, err := veleroClient.VeleroV1().PodVolumeRestores(f.Namespace()).List(context.TODO(), opts) if err != nil { fmt.Fprintf(os.Stderr, "error getting PodVolumeRestores for restore %s: %v\n", restore.Name, err) @@ -94,3 +94,11 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { return c } + +// newPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to +// find PodVolumeRestores for the restore identified by name. +func newPodVolumeRestoreListOptions(name string) metav1.ListOptions { + return metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", velerov1api.RestoreNameLabel, label.GetValidName(name)), + } +} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index c05dc75ef5..03111bcdf7 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -112,6 +112,9 @@ const ( // defaultCredentialsDirectory is the path on disk where credential // files will be written to defaultCredentialsDirectory = "/tmp/credentials" + + // daemonSet is the name of the Velero restic daemonset. + daemonSet = "restic" ) type serverConfig struct { @@ -529,7 +532,7 @@ var defaultRestorePriorities = []string{ func (s *server) initRestic() error { // warn if restic daemonset does not exist - if _, err := s.kubeClient.AppsV1().DaemonSets(s.namespace).Get(s.ctx, restic.DaemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) { + if _, err := s.kubeClient.AppsV1().DaemonSets(s.namespace).Get(s.ctx, daemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) { s.logger.Warn("Velero restic daemonset not found; restic backups/restores will not work until it's created") } else if err != nil { s.logger.WithError(errors.WithStack(err)).Warn("Error checking for existence of velero restic daemonset") diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index a616dcb711..a2193cd927 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -41,7 +41,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/repository" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -440,7 +439,7 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba return nil } - snapshots, err := restic.GetSnapshotsInBackup(ctx, backup, r.Client) + snapshots, err := getSnapshotsInBackup(ctx, backup, r.Client) if err != nil { return []error{err} } @@ -491,3 +490,33 @@ func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *vele } return backup, nil } + +// getSnapshotsInBackup returns a list of all restic snapshot ids associated with +// a given Velero backup. +func getSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]repository.SnapshotIdentifier, error) { + podVolumeBackups := &velerov1api.PodVolumeBackupList{} + options := &client.ListOptions{ + LabelSelector: labels.Set(map[string]string{ + velerov1api.BackupNameLabel: label.GetValidName(backup.Name), + }).AsSelector(), + } + + err := kbClient.List(ctx, podVolumeBackups, options) + if err != nil { + return nil, errors.WithStack(err) + } + + var res []repository.SnapshotIdentifier + for _, item := range podVolumeBackups.Items { + if item.Status.SnapshotID == "" { + continue + } + res = append(res, repository.SnapshotIdentifier{ + VolumeNamespace: item.Spec.Pod.Namespace, + BackupStorageLocation: backup.Spec.StorageLocation, + SnapshotID: item.Status.SnapshotID, + }) + } + + return res, nil +} diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index d2de589b24..79833958f5 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -19,6 +19,7 @@ package controller import ( "bytes" "fmt" + "sort" "time" "context" @@ -32,6 +33,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -52,6 +54,7 @@ import ( persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" + "github.com/vmware-tanzu/velero/pkg/repository" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -692,3 +695,172 @@ func TestBackupDeletionControllerReconcile(t *testing.T) { }) } + +func TestGetSnapshotsInBackup(t *testing.T) { + tests := []struct { + name string + podVolumeBackups []velerov1api.PodVolumeBackup + expected []repository.SnapshotIdentifier + longBackupNameEnabled bool + }{ + { + name: "no pod volume backups", + podVolumeBackups: nil, + expected: nil, + }, + { + name: "no pod volume backups with matching label", + podVolumeBackups: []velerov1api.PodVolumeBackup{ + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, + }, + }, + expected: nil, + }, + { + name: "some pod volume backups with matching label", + podVolumeBackups: []velerov1api.PodVolumeBackup{ + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""}, + }, + }, + expected: []repository.SnapshotIdentifier{ + { + VolumeNamespace: "ns-1", + SnapshotID: "snap-3", + }, + { + VolumeNamespace: "ns-1", + SnapshotID: "snap-4", + }, + }, + }, + { + name: "some pod volume backups with matching label and backup name greater than 63 chars", + longBackupNameEnabled: true, + podVolumeBackups: []velerov1api.PodVolumeBackup{ + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, + Spec: velerov1api.PodVolumeBackupSpec{ + Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"}, + }, + Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""}, + }, + }, + expected: []repository.SnapshotIdentifier{ + { + VolumeNamespace: "ns-1", + SnapshotID: "snap-3", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var ( + clientBuilder = velerotest.NewFakeControllerRuntimeClientBuilder(t) + veleroBackup = &velerov1api.Backup{} + ) + + veleroBackup.Name = "backup-1" + + if test.longBackupNameEnabled { + veleroBackup.Name = "the-really-long-backup-name-that-is-much-more-than-63-characters" + } + clientBuilder.WithLists(&velerov1api.PodVolumeBackupList{ + Items: test.podVolumeBackups, + }) + + res, err := getSnapshotsInBackup(context.TODO(), veleroBackup, clientBuilder.Build()) + assert.NoError(t, err) + + // sort to ensure good compare of slices + less := func(snapshots []repository.SnapshotIdentifier) func(i, j int) bool { + return func(i, j int) bool { + if snapshots[i].VolumeNamespace == snapshots[j].VolumeNamespace { + return snapshots[i].SnapshotID < snapshots[j].SnapshotID + } + return snapshots[i].VolumeNamespace < snapshots[j].VolumeNamespace + } + + } + + sort.Slice(test.expected, less(test.expected)) + sort.Slice(res, less(res)) + + assert.Equal(t, test.expected, res) + }) + } +} diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 00853710fc..c8f0913db7 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -39,6 +39,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/uploader" @@ -106,7 +107,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req resticInitContainerIndex := getResticInitContainerIndex(pod) if resticInitContainerIndex > 0 { log.Warnf(`Init containers before the %s container may cause issues - if they interfere with volumes being restored: %s index %d`, restic.InitContainer, restic.InitContainer, resticInitContainerIndex) + if they interfere with volumes being restored: %s index %d`, podvolume.InitContainer, podvolume.InitContainer, resticInitContainerIndex) } log.Info("Restore starting") @@ -216,7 +217,7 @@ func isResticInitContainerRunning(pod *corev1api.Pod) bool { func getResticInitContainerIndex(pod *corev1api.Pod) int { // Restic wait container can be anywhere in the list of init containers so locate it. for i, initContainer := range pod.Spec.InitContainers { - if initContainer.Name == restic.InitContainer { + if initContainer.Name == podvolume.InitContainer { return i } } diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 69bc313a34..d9cdc5d264 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -120,7 +120,7 @@ func TestShouldProcess(t *testing.T) { NodeName: controllerNode, InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, }, }, @@ -160,7 +160,7 @@ func TestShouldProcess(t *testing.T) { NodeName: controllerNode, InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, }, }, @@ -260,7 +260,7 @@ func TestIsResticContainerRunning(t *testing.T) { Name: "non-restic-init", }, { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, }, }, @@ -291,7 +291,7 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, { Name: "non-restic-init", @@ -323,7 +323,7 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, { Name: "non-restic-init", @@ -357,7 +357,7 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, }, }, @@ -422,7 +422,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { Name: "non-restic-init", }, { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, }, }, @@ -439,7 +439,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, { Name: "non-restic-init", @@ -459,7 +459,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: restic.InitContainer, + Name: podvolume.InitContainer, }, { Name: "non-restic-init", diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index 57baacc106..959f05b7ef 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -42,6 +42,10 @@ const ( // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes // should be excluded from restic backup. VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" + + // InitContainer is the name of the init container added + // to workload pods to help with restores. + InitContainer = "restic-wait" ) // GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go index eb700d1062..ff97931827 100644 --- a/pkg/repository/manager.go +++ b/pkg/repository/manager.go @@ -27,10 +27,24 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository/provider" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) +// SnapshotIdentifier uniquely identifies a restic snapshot +// taken by Velero. +type SnapshotIdentifier struct { + // VolumeNamespace is the namespace of the pod/volume that + // the restic snapshot is for. + VolumeNamespace string + + // BackupStorageLocation is the backup's storage location + // name. + BackupStorageLocation string + + // SnapshotID is the short ID of the restic snapshot. + SnapshotID string +} + // Manager manages backup repositories. type Manager interface { // InitRepo initializes a repo with the specified name and identifier. @@ -50,7 +64,7 @@ type Manager interface { // Forget removes a snapshot from the list of // available snapshots in a repo. - Forget(context.Context, restic.SnapshotIdentifier) error + Forget(context.Context, SnapshotIdentifier) error } type manager struct { @@ -147,7 +161,7 @@ func (m *manager) UnlockRepo(repo *velerov1api.BackupRepository) error { return prd.EnsureUnlockRepo(context.Background(), param) } -func (m *manager) Forget(ctx context.Context, snapshot restic.SnapshotIdentifier) error { +func (m *manager) Forget(ctx context.Context, snapshot SnapshotIdentifier) error { repo, err := m.repoEnsurer.EnsureRepo(ctx, m.namespace, snapshot.VolumeNamespace, snapshot.BackupStorageLocation) if err != nil { return err diff --git a/pkg/repository/mocks/repository_manager.go b/pkg/repository/mocks/repository_manager.go index 5533706741..a0ec81db75 100644 --- a/pkg/repository/mocks/repository_manager.go +++ b/pkg/repository/mocks/repository_manager.go @@ -20,10 +20,10 @@ import ( context "context" mock "github.com/stretchr/testify/mock" - restic "github.com/vmware-tanzu/velero/pkg/restic" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" ) // RepositoryManager is an autogenerated mock type for the RepositoryManager type @@ -46,11 +46,11 @@ func (_m *RepositoryManager) ConnectToRepo(repo *v1.BackupRepository) error { } // Forget provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 restic.SnapshotIdentifier) error { +func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 repository.SnapshotIdentifier) error { ret := _m.Called(_a0, _a1) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, restic.SnapshotIdentifier) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, repository.SnapshotIdentifier) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 860f983f72..90a38f58be 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -17,7 +17,6 @@ limitations under the License. package restic import ( - "context" "fmt" "os" "strconv" @@ -25,24 +24,14 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/label" repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) const ( - // DaemonSet is the name of the Velero restic daemonset. - DaemonSet = "restic" - - // InitContainer is the name of the init container added - // to workload pods to help with restores. - InitContainer = "restic-wait" // DefaultMaintenanceFrequency is the default time interval // at which restic prune is run. @@ -61,51 +50,6 @@ const ( resticInsecureTLSFlag = "--insecure-tls" ) -// SnapshotIdentifier uniquely identifies a restic snapshot -// taken by Velero. -type SnapshotIdentifier struct { - // VolumeNamespace is the namespace of the pod/volume that - // the restic snapshot is for. - VolumeNamespace string - - // BackupStorageLocation is the backup's storage location - // name. - BackupStorageLocation string - - // SnapshotID is the short ID of the restic snapshot. - SnapshotID string -} - -// GetSnapshotsInBackup returns a list of all restic snapshot ids associated with -// a given Velero backup. -func GetSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]SnapshotIdentifier, error) { - podVolumeBackups := &velerov1api.PodVolumeBackupList{} - options := &client.ListOptions{ - LabelSelector: labels.Set(map[string]string{ - velerov1api.BackupNameLabel: label.GetValidName(backup.Name), - }).AsSelector(), - } - - err := kbClient.List(ctx, podVolumeBackups, options) - if err != nil { - return nil, errors.WithStack(err) - } - - var res []SnapshotIdentifier - for _, item := range podVolumeBackups.Items { - if item.Status.SnapshotID == "" { - continue - } - res = append(res, SnapshotIdentifier{ - VolumeNamespace: item.Spec.Pod.Namespace, - BackupStorageLocation: backup.Spec.StorageLocation, - SnapshotID: item.Status.SnapshotID, - }) - } - - return res, nil -} - // TempCACertFile creates a temp file containing a CA bundle // and returns its path. The caller should generally call os.Remove() // to remove the file when done with it. @@ -131,14 +75,6 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -// NewPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to -// find PodVolumeRestores for the restore identified by name. -func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { - return metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", velerov1api.RestoreNameLabel, label.GetValidName(name)), - } -} - // CmdEnv returns a list of environment variables (in the format var=val) that // should be used when running a restic command for a particular backend provider. // This list is the current environment, plus any provider-specific variables restic needs. diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index b2acee773f..97363340c0 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -17,190 +17,17 @@ limitations under the License. package restic import ( - "context" "os" - "sort" "testing" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - corev1api "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestGetSnapshotsInBackup(t *testing.T) { - tests := []struct { - name string - podVolumeBackups []velerov1api.PodVolumeBackup - expected []SnapshotIdentifier - longBackupNameEnabled bool - }{ - { - name: "no pod volume backups", - podVolumeBackups: nil, - expected: nil, - }, - { - name: "no pod volume backups with matching label", - podVolumeBackups: []velerov1api.PodVolumeBackup{ - { - ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, - }, - }, - expected: nil, - }, - { - name: "some pod volume backups with matching label", - podVolumeBackups: []velerov1api.PodVolumeBackup{ - { - ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""}, - }, - }, - expected: []SnapshotIdentifier{ - { - VolumeNamespace: "ns-1", - SnapshotID: "snap-3", - }, - { - VolumeNamespace: "ns-1", - SnapshotID: "snap-4", - }, - }, - }, - { - name: "some pod volume backups with matching label and backup name greater than 63 chars", - longBackupNameEnabled: true, - podVolumeBackups: []velerov1api.PodVolumeBackup{ - { - ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}}, - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"}, - }, - Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""}, - }, - }, - expected: []SnapshotIdentifier{ - { - VolumeNamespace: "ns-1", - SnapshotID: "snap-3", - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var ( - clientBuilder = velerotest.NewFakeControllerRuntimeClientBuilder(t) - veleroBackup = &velerov1api.Backup{} - ) - - veleroBackup.Name = "backup-1" - - if test.longBackupNameEnabled { - veleroBackup.Name = "the-really-long-backup-name-that-is-much-more-than-63-characters" - } - clientBuilder.WithLists(&velerov1api.PodVolumeBackupList{ - Items: test.podVolumeBackups, - }) - - res, err := GetSnapshotsInBackup(context.TODO(), veleroBackup, clientBuilder.Build()) - assert.NoError(t, err) - - // sort to ensure good compare of slices - less := func(snapshots []SnapshotIdentifier) func(i, j int) bool { - return func(i, j int) bool { - if snapshots[i].VolumeNamespace == snapshots[j].VolumeNamespace { - return snapshots[i].SnapshotID < snapshots[j].SnapshotID - } - return snapshots[i].VolumeNamespace < snapshots[j].VolumeNamespace - } - - } - - sort.Slice(test.expected, less(test.expected)) - sort.Slice(res, less(res)) - - assert.Equal(t, test.expected, res) - }) - } -} - func TestTempCACertFile(t *testing.T) { var ( fs = velerotest.NewFakeFileSystem() diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index ba9f9cb0a3..586dbf7852 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -37,7 +37,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podvolume" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -161,7 +160,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu initContainerBuilder.Command(getCommand(log, config)) initContainer := *initContainerBuilder.Result() - if len(pod.Spec.InitContainers) == 0 || pod.Spec.InitContainers[0].Name != restic.InitContainer { + if len(pod.Spec.InitContainers) == 0 || pod.Spec.InitContainers[0].Name != podvolume.InitContainer { pod.Spec.InitContainers = append([]corev1.Container{initContainer}, pod.Spec.InitContainers...) } else { pod.Spec.InitContainers[0] = initContainer @@ -290,7 +289,7 @@ func getPluginConfig(kind framework.PluginKind, name string, client corev1client } func newResticInitContainerBuilder(image, restoreUID string) *builder.ContainerBuilder { - return builder.ForContainer(restic.InitContainer, image). + return builder.ForContainer(podvolume.InitContainer, image). Args(restoreUID). Env([]*corev1.EnvVar{ { From f15757a3d820a49050069901d146edc8b744a2d4 Mon Sep 17 00:00:00 2001 From: Ming Date: Mon, 15 Aug 2022 10:34:08 +0000 Subject: [PATCH 273/366] Uploader Implementation: Restic backup and restore Signed-off-by: Ming --- changelogs/unreleased/5214-qiuming-best | 1 + pkg/builder/backup_repository_builer.go | 59 ++++ pkg/cmd/cli/restic/server.go | 26 +- .../pod_volume_backup_controller.go | 216 +++++---------- .../pod_volume_backup_controller_test.go | 82 ++++-- .../pod_volume_restore_controller.go | 104 +++---- pkg/restic/exec_commands.go | 151 +--------- pkg/restic/exec_commands_test.go | 6 +- pkg/restic/executer.go | 37 --- pkg/uploader/provider/kopia.go | 4 +- pkg/uploader/provider/kopia_test.go | 24 +- pkg/uploader/provider/provider.go | 24 +- pkg/uploader/provider/restic.go | 260 +++++++++++++++++- pkg/uploader/provider/restic_test.go | 136 +++++++++ 14 files changed, 707 insertions(+), 423 deletions(-) create mode 100644 changelogs/unreleased/5214-qiuming-best create mode 100644 pkg/builder/backup_repository_builer.go delete mode 100644 pkg/restic/executer.go create mode 100644 pkg/uploader/provider/restic_test.go diff --git a/changelogs/unreleased/5214-qiuming-best b/changelogs/unreleased/5214-qiuming-best new file mode 100644 index 0000000000..9c2ddd1de7 --- /dev/null +++ b/changelogs/unreleased/5214-qiuming-best @@ -0,0 +1 @@ +Uploader Implementation: Restic backup and restore diff --git a/pkg/builder/backup_repository_builer.go b/pkg/builder/backup_repository_builer.go new file mode 100644 index 0000000000..a78f3238af --- /dev/null +++ b/pkg/builder/backup_repository_builer.go @@ -0,0 +1,59 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package builder + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// BackupRepositoryBuilder builds BackupRepository objects. +type BackupRepositoryBuilder struct { + object *velerov1api.BackupRepository +} + +// ForBackupRepository is the constructor for a BackupRepositoryBuilder. +func ForBackupRepository(ns, name string) *BackupRepositoryBuilder { + return &BackupRepositoryBuilder{ + object: &velerov1api.BackupRepository{ + Spec: velerov1api.BackupRepositorySpec{ResticIdentifier: ""}, + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "BackupRepository", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built BackupRepository. +func (b *BackupRepositoryBuilder) Result() *velerov1api.BackupRepository { + return b.object +} + +// ObjectMeta applies functional options to the BackupRepository's ObjectMeta. +func (b *BackupRepositoryBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupRepositoryBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index ae593306b0..73d7cdec56 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -52,7 +52,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd/util/signals" "github.com/vmware-tanzu/velero/pkg/controller" "github.com/vmware-tanzu/velero/pkg/metrics" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -197,22 +196,27 @@ func (s *resticServer) run() { s.logger.Fatalf("Failed to create credentials file store: %v", err) } + credSecretStore, err := credentials.NewNamespacedSecretStore(s.mgr.GetClient(), s.namespace) + if err != nil { + s.logger.Fatalf("Failed to create secret file store: %v", err) + } + + credentialGetter := &credentials.CredentialGetter{FromFile: credentialFileStore, FromSecret: credSecretStore} pvbReconciler := controller.PodVolumeBackupReconciler{ - Scheme: s.mgr.GetScheme(), - Client: s.mgr.GetClient(), - Clock: clock.RealClock{}, - Metrics: s.metrics, - CredsFileStore: credentialFileStore, - NodeName: s.nodeName, - FileSystem: filesystem.NewFileSystem(), - ResticExec: restic.BackupExec{}, - Log: s.logger, + Scheme: s.mgr.GetScheme(), + Client: s.mgr.GetClient(), + Clock: clock.RealClock{}, + Metrics: s.metrics, + CredentialGetter: credentialGetter, + NodeName: s.nodeName, + FileSystem: filesystem.NewFileSystem(), + Log: s.logger, } if err := pvbReconciler.SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.PodVolumeBackup) } - if err = controller.NewPodVolumeRestoreReconciler(s.logger, s.mgr.GetClient(), credentialFileStore).SetupWithManager(s.mgr); err != nil { + if err = controller.NewPodVolumeRestoreReconciler(s.logger, s.mgr.GetClient(), credentialGetter).SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the pod volume restore controller") } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 635ef00a76..97b0993413 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -19,7 +19,6 @@ package controller import ( "context" "fmt" - "os" "strings" "time" @@ -37,29 +36,25 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/metrics" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" ) -// BackupExecuter runs backups. -type BackupExecuter interface { - RunBackup(*restic.Command, logrus.FieldLogger, func(velerov1api.PodVolumeOperationProgress)) (string, string, error) - GetSnapshotID(*restic.Command) (string, error) -} +// For unit test to mock function +var NewUploaderProviderFunc = provider.NewUploaderProvider // PodVolumeBackupReconciler reconciles a PodVolumeBackup object type PodVolumeBackupReconciler struct { - Scheme *runtime.Scheme - Client client.Client - Clock clock.Clock - Metrics *metrics.ServerMetrics - CredsFileStore credentials.FileStore - NodeName string - FileSystem filesystem.Interface - ResticExec BackupExecuter - Log logrus.FieldLogger + Scheme *runtime.Scheme + Client client.Client + Clock clock.Clock + Metrics *metrics.ServerMetrics + CredentialGetter *credentials.CredentialGetter + NodeName string + FileSystem filesystem.Interface + Log logrus.FieldLogger } type BackupProgressUpdater struct { @@ -85,7 +80,6 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ } return ctrl.Result{}, errors.Wrap(err, "getting PodVolumeBackup") } - if len(pvb.OwnerReferences) == 1 { log = log.WithField( "backup", @@ -128,16 +122,19 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("getting pod %s/%s", pvb.Spec.Pod.Namespace, pvb.Spec.Pod.Name), log) } - var resticDetails resticDetails - resticCmd, err := r.buildResticCommand(ctx, log, &pvb, &pod, &resticDetails) + volDir, err := kube.GetVolumeDirectory(ctx, log, &pod, pvb.Spec.Volume, r.Client) if err != nil { - return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command", log) + return r.updateStatusToFailed(ctx, &pvb, err, "getting volume directory name", log) } - defer func() { - os.Remove(resticDetails.credsFile) - os.Remove(resticDetails.caCertFile) - }() + pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(pvb.Spec.Pod.UID), volDir) + log.WithField("pathGlob", pathGlob).Debug("Looking for path matching glob") + + path, err := kube.SinglePathMatch(pathGlob, r.FileSystem, log) + if err != nil { + return r.updateStatusToFailed(ctx, &pvb, err, "identifying unique volume path on host", log) + } + log.WithField("path", path).Debugf("Found path matching glob") backupLocation := &velerov1api.BackupStorageLocation{} if err := r.Client.Get(context.Background(), client.ObjectKey{ @@ -147,47 +144,72 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } - // #4820: restrieve insecureSkipTLSVerify from BSL configuration for - // AWS plugin. If nothing is return, that means insecureSkipTLSVerify - // is not enable for Restic command. - skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(backupLocation, log) - if len(skipTLSRet) > 0 { - resticCmd.ExtraFlags = append(resticCmd.ExtraFlags, skipTLSRet) + // name of ResticRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters + // it could not retrieve the ResticRepository CR with namespace + name. so first list all CRs with in the volumeNamespace + // then filtering the matched CR with prefix volumeNamespace-backupLocation- + backupRepos := &velerov1api.BackupRepositoryList{} + var backupRepo velerov1api.BackupRepository + isFoundRepo := false + if r.Client.List(ctx, backupRepos, &client.ListOptions{ + Namespace: pvb.Namespace, + }); err != nil { + return ctrl.Result{}, errors.Wrap(err, "error getting backup repository") + } else if len(backupRepos.Items) == 0 { + return ctrl.Result{}, errors.Errorf("find empty BackupRepository found for workload namespace %s, backup storage location %s", pvb.Namespace, pvb.Spec.BackupStorageLocation) + } else { + for _, repo := range backupRepos.Items { + if strings.HasPrefix(repo.Name, fmt.Sprintf("%s-%s-", pvb.Spec.Pod.Namespace, pvb.Spec.BackupStorageLocation)) { + backupRepo = repo + isFoundRepo = true + break + } + } + if !isFoundRepo { + return ctrl.Result{}, errors.Errorf("could not found match BackupRepository for workload namespace %s, backup storage location %s", pvb.Namespace, pvb.Spec.BackupStorageLocation) + } } - var stdout, stderr string - - var emptySnapshot bool - stdout, stderr, err = r.ResticExec.RunBackup(resticCmd, log, r.updateBackupProgressFunc(&pvb, log)) + var uploaderProv provider.Provider + uploaderProv, err = NewUploaderProviderFunc(ctx, r.Client, pvb.Spec.UploaderType, pvb.Spec.RepoIdentifier, + backupLocation, &backupRepo, r.CredentialGetter, repokey.RepoKeySelector(), log) if err != nil { - if strings.Contains(stderr, "snapshot is empty") { - emptySnapshot = true + return r.updateStatusToFailed(ctx, &pvb, err, "error creating uploader", log) + } + + // If this is a PVC, look for the most recent completed pod volume backup for it and get + // its restic snapshot ID to use as the value of the `--parent` flag. Without this, + // if the pod using the PVC (and therefore the directory path under /host_pods/) has + // changed since the PVC's last backup, restic will not be able to identify a suitable + // parent snapshot to use, and will have to do a full rescan of the contents of the PVC. + var parentSnapshotID string + if pvcUID, ok := pvb.Labels[velerov1api.PVCUIDLabel]; ok { + parentSnapshotID = r.getParentSnapshot(ctx, log, pvb.Namespace, pvcUID, pvb.Spec.BackupStorageLocation) + if parentSnapshotID == "" { + log.Info("No parent snapshot found for PVC, not using --parent flag for this backup") } else { - return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running Restic backup, stderr=%s", stderr), log) + log.WithField("parentSnapshotID", parentSnapshotID).Info("Setting --parent flag for this backup") } } - log.Debugf("Ran command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr) - - var snapshotID string - if !emptySnapshot { - cmd := restic.GetSnapshotCommand(pvb.Spec.RepoIdentifier, resticDetails.credsFile, pvb.Spec.Tags) - cmd.Env = resticDetails.envs - cmd.CACertFile = resticDetails.caCertFile - // #4820: also apply the insecureTLS flag to Restic snapshots command - if len(skipTLSRet) > 0 { - cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet) + defer func() { + if err := uploaderProv.Close(ctx); err != nil { + log.Errorf("failed to close uploader provider with error %v", err) } + }() - snapshotID, err = r.ResticExec.GetSnapshotID(cmd) - if err != nil { - return r.updateStatusToFailed(ctx, &pvb, err, "getting snapshot id", log) + var emptySnapshot bool + snapshotID, err := uploaderProv.RunBackup(ctx, path, pvb.Spec.Tags, parentSnapshotID, r.NewBackupProgressUpdater(&pvb, log, ctx)) + if err != nil { + if strings.Contains(err.Error(), "snapshot is empty") { + emptySnapshot = true + } else { + return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running Restic backup, stderr=%v", err), log) } } // Update status to Completed with path & snapshot ID. original = pvb.DeepCopy() - pvb.Status.Path = resticDetails.path + pvb.Status.Path = path pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseCompleted pvb.Status.SnapshotID = snapshotID pvb.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} @@ -202,8 +224,9 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ latencyDuration := pvb.Status.CompletionTimestamp.Time.Sub(pvb.Status.StartTimestamp.Time) latencySeconds := float64(latencyDuration / time.Second) backupName := fmt.Sprintf("%s/%s", req.Namespace, pvb.OwnerReferences[0].Name) - r.Metrics.ObserveResticOpLatency(r.NodeName, req.Name, resticCmd.Command, backupName, latencySeconds) - r.Metrics.RegisterResticOpLatencyGauge(r.NodeName, req.Name, resticCmd.Command, backupName, latencySeconds) + generateOpName := fmt.Sprintf("%s-%s-%s-%s-backup", pvb.Name, backupRepo.Name, pvb.Spec.BackupStorageLocation, pvb.Namespace) + r.Metrics.ObserveResticOpLatency(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) + r.Metrics.RegisterResticOpLatencyGauge(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) r.Metrics.RegisterPodVolumeBackupDequeue(r.NodeName) log.Info("PodVolumeBackup completed") @@ -272,18 +295,6 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l return mostRecentPVB.Status.SnapshotID } -// updateBackupProgressFunc returns a func that takes progress info and patches -// the PVB with the new progress. -func (r *PodVolumeBackupReconciler) updateBackupProgressFunc(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { - return func(progress velerov1api.PodVolumeOperationProgress) { - original := pvb.DeepCopy() - pvb.Status.Progress = progress - if err := r.Client.Patch(context.Background(), pvb, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("error update progress") - } - } -} - func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pvb *velerov1api.PodVolumeBackup, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { original := pvb.DeepCopy() pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed @@ -298,81 +309,6 @@ func (r *PodVolumeBackupReconciler) updateStatusToFailed(ctx context.Context, pv return ctrl.Result{}, nil } -type resticDetails struct { - credsFile, caCertFile string - envs []string - path string -} - -func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log *logrus.Entry, pvb *velerov1api.PodVolumeBackup, pod *corev1.Pod, details *resticDetails) (*restic.Command, error) { - volDir, err := kube.GetVolumeDirectory(ctx, log, pod, pvb.Spec.Volume, r.Client) - if err != nil { - return nil, errors.Wrap(err, "getting volume directory name") - } - - pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(pvb.Spec.Pod.UID), volDir) - log.WithField("pathGlob", pathGlob).Debug("Looking for path matching glob") - - path, err := kube.SinglePathMatch(pathGlob, r.FileSystem, log) - if err != nil { - return nil, errors.Wrap(err, "identifying unique volume path on host") - } - log.WithField("path", path).Debugf("Found path matching glob") - - // Temporary credentials. - details.credsFile, err = r.CredsFileStore.Path(repokey.RepoKeySelector()) - if err != nil { - return nil, errors.Wrap(err, "creating temporary Restic credentials file") - } - - cmd := restic.BackupCommand(pvb.Spec.RepoIdentifier, details.credsFile, path, pvb.Spec.Tags) - - backupLocation := &velerov1api.BackupStorageLocation{} - if err := r.Client.Get(context.Background(), client.ObjectKey{ - Namespace: pvb.Namespace, - Name: pvb.Spec.BackupStorageLocation, - }, backupLocation); err != nil { - return nil, errors.Wrap(err, "getting backup storage location") - } - - // If there's a caCert on the ObjectStorage, write it to disk so that it can - // be passed to Restic. - if backupLocation.Spec.ObjectStorage != nil && - backupLocation.Spec.ObjectStorage.CACert != nil { - - details.caCertFile, err = restic.TempCACertFile(backupLocation.Spec.ObjectStorage.CACert, pvb.Spec.BackupStorageLocation, r.FileSystem) - if err != nil { - log.WithError(err).Error("creating temporary caCert file") - } - } - cmd.CACertFile = details.caCertFile - - details.envs, err = restic.CmdEnv(backupLocation, r.CredsFileStore) - if err != nil { - return nil, errors.Wrap(err, "setting Restic command environment") - } - cmd.Env = details.envs - - // If this is a PVC, look for the most recent completed PodVolumeBackup for - // it and get its Restic snapshot ID to use as the value of the `--parent` - // flag. Without this, if the pod using the PVC (and therefore the directory - // path under /host_pods/) has changed since the PVC's last backup, Restic - // will not be able to identify a suitable parent snapshot to use, and will - // have to do a full rescan of the contents of the PVC. - if pvcUID, ok := pvb.Labels[velerov1api.PVCUIDLabel]; ok { - parentSnapshotID := r.getParentSnapshot(ctx, log, pvb.Namespace, pvcUID, pvb.Spec.BackupStorageLocation) - if parentSnapshotID == "" { - log.Info("No parent snapshot found for PVC, not using --parent flag for this backup") - } else { - log.WithField("parentSnapshotID", parentSnapshotID). - Info("Setting --parent flag for this backup") - cmd.ExtraFlags = append(cmd.ExtraFlags, fmt.Sprintf("--parent=%s", parentSnapshotID)) - } - } - - return cmd, nil -} - func (r *PodVolumeBackupReconciler) NewBackupProgressUpdater(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger, ctx context.Context) *BackupProgressUpdater { return &BackupProgressUpdater{pvb, log, ctx, r.Client} } diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index ffc5f662cb..794606cb0a 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -24,6 +24,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,11 +36,13 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/metrics" - "github.com/vmware-tanzu/velero/pkg/restic/mocks" velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/uploader/provider" ) const name = "pvb-1" @@ -68,12 +71,16 @@ func bslBuilder() *builder.BackupStorageLocationBuilder { ForBackupStorageLocation(velerov1api.DefaultNamespace, "bsl-loc") } +func backupRepoBuilder() *builder.BackupRepositoryBuilder { + return builder.ForBackupRepository(velerov1api.DefaultNamespace, fmt.Sprintf("%s-bsl-loc-dn24h", velerov1api.DefaultNamespace)) +} + var _ = Describe("PodVolumeBackup Reconciler", func() { type request struct { - pvb *velerov1api.PodVolumeBackup - pod *corev1.Pod - bsl *velerov1api.BackupStorageLocation - + pvb *velerov1api.PodVolumeBackup + pod *corev1.Pod + bsl *velerov1api.BackupStorageLocation + backupRepo *velerov1api.BackupRepository expectedProcessed bool expected *velerov1api.PodVolumeBackup expectedRequeue ctrl.Result @@ -100,31 +107,41 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { err = fakeClient.Create(ctx, test.bsl) Expect(err).To(BeNil()) + err = fakeClient.Create(ctx, test.backupRepo) + Expect(err).To(BeNil()) + fakeFS := velerotest.NewFakeFileSystem() pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", "", "pvb-1-volume") _, err = fakeFS.Create(pathGlob) Expect(err).To(BeNil()) + credentialFileStore, err := credentials.NewNamespacedFileStore( + fakeClient, + velerov1api.DefaultNamespace, + "/tmp/credentials", + fakeFS, + ) + // Setup reconciler Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) r := PodVolumeBackupReconciler{ - Client: fakeClient, - Clock: clock.NewFakeClock(now), - Metrics: metrics.NewResticServerMetrics(), - CredsFileStore: fakeCredsFileStore{}, - NodeName: "test_node", - FileSystem: fakeFS, - ResticExec: mocks.FakeResticBackupExec{}, - Log: velerotest.NewLogger(), + Client: fakeClient, + Clock: clock.NewFakeClock(now), + Metrics: metrics.NewResticServerMetrics(), + CredentialGetter: &credentials.CredentialGetter{FromFile: credentialFileStore}, + NodeName: "test_node", + FileSystem: fakeFS, + Log: velerotest.NewLogger(), + } + NewUploaderProviderFunc = func(ctx context.Context, client kbclient.Client, uploaderType, repoIdentifier string, bsl *velerov1api.BackupStorageLocation, backupRepo *velerov1api.BackupRepository, credGetter *credentials.CredentialGetter, repoKeySelector *corev1.SecretKeySelector, log logrus.FieldLogger) (provider.Provider, error) { + return &fakeProvider{}, nil } - actualResult, err := r.Reconcile(ctx, ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: velerov1api.DefaultNamespace, Name: test.pvb.Name, }, }) - Expect(actualResult).To(BeEquivalentTo(test.expectedRequeue)) if test.expectedErrMsg == "" { Expect(err).To(BeNil()) @@ -137,7 +154,6 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Name: test.pvb.Name, Namespace: test.pvb.Namespace, }, &pvb) - // Assertions if test.expected == nil { Expect(apierrors.IsNotFound(err)).To(BeTrue()) @@ -160,6 +176,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { pvb: pvbBuilder().Phase("").Node("test_node").Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: true, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -173,6 +190,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: true, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -186,6 +204,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseInProgress). @@ -199,6 +218,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -212,6 +232,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -225,6 +246,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -238,6 +260,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseNew). @@ -251,6 +274,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseInProgress). @@ -264,6 +288,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -277,6 +302,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), + backupRepo: backupRepoBuilder().Result(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -286,8 +312,26 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { ) }) -type fakeCredsFileStore struct{} +type fakeProvider struct { +} + +func (f *fakeProvider) RunBackup( + ctx context.Context, + path string, + tags map[string]string, + parentSnapshot string, + updater uploader.ProgressUpdater) (string, error) { + return "", nil +} + +func (f *fakeProvider) RunRestore( + ctx context.Context, + snapshotID string, + volumePath string, + updater uploader.ProgressUpdater) error { + return nil +} -func (f fakeCredsFileStore) Path(selector *corev1.SecretKeySelector) (string, error) { - return "/fake/path", nil +func (f *fakeProvider) Close(ctx context.Context) error { + return nil } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 00853710fc..7e09bd5a47 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -42,27 +43,28 @@ import ( repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" ) -func NewPodVolumeRestoreReconciler(logger logrus.FieldLogger, client client.Client, credentialsFileStore credentials.FileStore) *PodVolumeRestoreReconciler { +func NewPodVolumeRestoreReconciler(logger logrus.FieldLogger, client client.Client, credentialGetter *credentials.CredentialGetter) *PodVolumeRestoreReconciler { return &PodVolumeRestoreReconciler{ - Client: client, - logger: logger.WithField("controller", "PodVolumeRestore"), - credentialsFileStore: credentialsFileStore, - fileSystem: filesystem.NewFileSystem(), - clock: &clock.RealClock{}, + Client: client, + logger: logger.WithField("controller", "PodVolumeRestore"), + credentialGetter: credentialGetter, + fileSystem: filesystem.NewFileSystem(), + clock: &clock.RealClock{}, } } type PodVolumeRestoreReconciler struct { client.Client - logger logrus.FieldLogger - credentialsFileStore credentials.FileStore - fileSystem filesystem.Interface - clock clock.Clock + logger logrus.FieldLogger + credentialGetter *credentials.CredentialGetter + fileSystem filesystem.Interface + clock clock.Clock } type RestoreProgressUpdater struct { @@ -239,20 +241,6 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error identifying path of volume") } - credsFile, err := c.credentialsFileStore.Path(repokey.RepoKeySelector()) - if err != nil { - return errors.Wrap(err, "error creating temp restic credentials file") - } - // ignore error since there's nothing we can do and it's a temp file. - defer os.Remove(credsFile) - - resticCmd := restic.RestoreCommand( - req.Spec.RepoIdentifier, - credsFile, - req.Spec.SnapshotID, - volumePath, - ) - backupLocation := &velerov1api.BackupStorageLocation{} if err := c.Get(ctx, client.ObjectKey{ Namespace: req.Namespace, @@ -261,38 +249,46 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error getting backup storage location") } - // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - var caCertFile string - if backupLocation.Spec.ObjectStorage != nil && backupLocation.Spec.ObjectStorage.CACert != nil { - caCertFile, err = restic.TempCACertFile(backupLocation.Spec.ObjectStorage.CACert, req.Spec.BackupStorageLocation, c.fileSystem) - if err != nil { - log.WithError(err).Error("Error creating temp cacert file") + // name of ResticRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters + // it could not retrieve the ResticRepository CR with namespace + name. so first list all CRs with in the volumeNamespace + // then filtering the matched CR with prefix volumeNamespace-backupLocation- + backupRepos := &velerov1api.BackupRepositoryList{} + var backupRepo velerov1api.BackupRepository + isFoundRepo := false + if c.List(ctx, backupRepos, &client.ListOptions{ + Namespace: req.Namespace, + }); err != nil { + return errors.Wrap(err, "error getting backup repository") + } else if len(backupRepos.Items) == 0 { + return errors.Errorf("find empty BackupRepository found for workload namespace %s, backup storage location %s", req.Namespace, req.Spec.BackupStorageLocation) + } else { + for _, repo := range backupRepos.Items { + if strings.HasPrefix(repo.Name, fmt.Sprintf("%s-%s-", req.Spec.Pod.Namespace, req.Spec.BackupStorageLocation)) { + backupRepo = repo + isFoundRepo = true + break + } + } + if !isFoundRepo { + return errors.Errorf("could not found match BackupRepository for workload namespace %s, backup storage location %s", req.Namespace, req.Spec.BackupStorageLocation) } - // ignore error since there's nothing we can do and it's a temp file. - defer os.Remove(caCertFile) } - resticCmd.CACertFile = caCertFile - env, err := restic.CmdEnv(backupLocation, c.credentialsFileStore) + uploaderProv, err := provider.NewUploaderProvider(ctx, c.Client, req.Spec.UploaderType, + req.Spec.RepoIdentifier, backupLocation, &backupRepo, c.credentialGetter, repokey.RepoKeySelector(), log) if err != nil { - return errors.Wrap(err, "error setting restic cmd env") - } - resticCmd.Env = env - - // #4820: restrieve insecureSkipTLSVerify from BSL configuration for - // AWS plugin. If nothing is return, that means insecureSkipTLSVerify - // is not enable for Restic command. - skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(backupLocation, log) - if len(skipTLSRet) > 0 { - resticCmd.ExtraFlags = append(resticCmd.ExtraFlags, skipTLSRet) + return errors.Wrap(err, "error creating uploader") } - var stdout, stderr string + defer func() { + if err := uploaderProv.Close(ctx); err != nil { + log.Errorf("failed to close uploader provider with error %v", err) + } + }() - if stdout, stderr, err = restic.RunRestore(resticCmd, log, c.updateRestoreProgressFunc(req, log)); err != nil { - return errors.Wrapf(err, "error running restic restore, cmd=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr) + if err = uploaderProv.RunRestore(ctx, req.Spec.SnapshotID, volumePath, c.NewRestoreProgressUpdater(req, log, ctx)); err != nil { + return errors.Wrapf(err, "error running restic restore err=%v", err) } - log.Debugf("Ran command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr) // Remove the .velero directory from the restored volume (it may contain done files from previous restores // of this volume, which we don't want to carry over). If this fails for any reason, log and continue, since @@ -326,18 +322,6 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return nil } -// updateRestoreProgressFunc returns a func that takes progress info and patches -// the PVR with the new progress -func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.PodVolumeRestore, log logrus.FieldLogger) func(velerov1api.PodVolumeOperationProgress) { - return func(progress velerov1api.PodVolumeOperationProgress) { - original := req.DeepCopy() - req.Status.Progress = progress - if err := c.Patch(context.Background(), req, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("Unable to update PodVolumeRestore progress") - } - } -} - func (r *PodVolumeRestoreReconciler) NewRestoreProgressUpdater(pvr *velerov1api.PodVolumeRestore, log logrus.FieldLogger, ctx context.Context) *RestoreProgressUpdater { return &RestoreProgressUpdater{pvr, log, ctx, r.Client} } diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index 7dd0057c0f..1a4b055bf2 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -20,13 +20,10 @@ import ( "bytes" "encoding/json" "fmt" - "strings" "time" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -69,82 +66,7 @@ func GetSnapshotID(snapshotIdCmd *Command) (string, error) { return snapshots[0].ShortID, nil } -// RunBackup runs a `restic backup` command and watches the output to provide -// progress updates to the caller. -func RunBackup(backupCmd *Command, log logrus.FieldLogger, updateFunc func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { - // buffers for copying command stdout/err output into - stdoutBuf := new(bytes.Buffer) - stderrBuf := new(bytes.Buffer) - - // create a channel to signal when to end the goroutine scanning for progress - // updates - quit := make(chan struct{}) - - cmd := backupCmd.Cmd() - cmd.Stdout = stdoutBuf - cmd.Stderr = stderrBuf - - err := cmd.Start() - if err != nil { - return stdoutBuf.String(), stderrBuf.String(), err - } - - go func() { - ticker := time.NewTicker(backupProgressCheckInterval) - for { - select { - case <-ticker.C: - lastLine := getLastLine(stdoutBuf.Bytes()) - if len(lastLine) > 0 { - stat, err := decodeBackupStatusLine(lastLine) - if err != nil { - log.WithError(err).Errorf("error getting restic backup progress") - } - - // if the line contains a non-empty bytes_done field, we can update the - // caller with the progress - if stat.BytesDone != 0 { - updateFunc(velerov1api.PodVolumeOperationProgress{ - TotalBytes: stat.TotalBytes, - BytesDone: stat.BytesDone, - }) - } - } - case <-quit: - ticker.Stop() - return - } - } - }() - - err = cmd.Wait() - if err != nil { - return stdoutBuf.String(), stderrBuf.String(), err - } - quit <- struct{}{} - - summary, err := getSummaryLine(stdoutBuf.Bytes()) - if err != nil { - return stdoutBuf.String(), stderrBuf.String(), err - } - stat, err := decodeBackupStatusLine(summary) - if err != nil { - return stdoutBuf.String(), stderrBuf.String(), err - } - if stat.MessageType != "summary" { - return stdoutBuf.String(), stderrBuf.String(), errors.WithStack(fmt.Errorf("error getting restic backup summary: %s", string(summary))) - } - - // update progress to 100% - updateFunc(velerov1api.PodVolumeOperationProgress{ - TotalBytes: stat.TotalBytesProcessed, - BytesDone: stat.TotalBytesProcessed, - }) - - return string(summary), stderrBuf.String(), nil -} - -func decodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { +func DecodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { var stat backupStatusLine if err := json.Unmarshal(lastLine, &stat); err != nil { return stat, errors.Wrapf(err, "unable to decode backup JSON line: %s", string(lastLine)) @@ -152,10 +74,10 @@ func decodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { return stat, nil } -// getLastLine returns the last line of a byte array. The string is assumed to +// GetLastLine returns the last line of a byte array. The string is assumed to // have a newline at the end of it, so this returns the substring between the // last two newlines. -func getLastLine(b []byte) []byte { +func GetLastLine(b []byte) []byte { if b == nil || len(b) == 0 { return []byte("") } @@ -164,12 +86,12 @@ func getLastLine(b []byte) []byte { return b[lastNewLineIdx+1 : len(b)-1] } -// getSummaryLine looks for the summary JSON line +// GetSummaryLine looks for the summary JSON line // (`{"message_type:"summary",...`) in the restic backup command output. Due to // an issue in Restic, this might not always be the last line // (https://github.com/restic/restic/issues/2389). It returns an error if it // can't be found. -func getSummaryLine(b []byte) ([]byte, error) { +func GetSummaryLine(b []byte) ([]byte, error) { summaryLineIdx := bytes.LastIndex(b, []byte(`{"message_type":"summary"`)) if summaryLineIdx < 0 { return nil, errors.New("unable to find summary in restic backup command output") @@ -182,64 +104,7 @@ func getSummaryLine(b []byte) ([]byte, error) { return b[summaryLineIdx : summaryLineIdx+newLineIdx], nil } -// RunRestore runs a `restic restore` command and monitors the volume size to -// provide progress updates to the caller. -func RunRestore(restoreCmd *Command, log logrus.FieldLogger, updateFunc func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { - insecureTLSFlag := "" - - for _, extraFlag := range restoreCmd.ExtraFlags { - if strings.Contains(extraFlag, resticInsecureTLSFlag) { - insecureTLSFlag = extraFlag - } - } - - snapshotSize, err := getSnapshotSize(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env, insecureTLSFlag) - if err != nil { - return "", "", errors.Wrap(err, "error getting snapshot size") - } - - updateFunc(velerov1api.PodVolumeOperationProgress{ - TotalBytes: snapshotSize, - }) - - // create a channel to signal when to end the goroutine scanning for progress - // updates - quit := make(chan struct{}) - - go func() { - ticker := time.NewTicker(restoreProgressCheckInterval) - for { - select { - case <-ticker.C: - volumeSize, err := getVolumeSize(restoreCmd.Dir) - if err != nil { - log.WithError(err).Errorf("error getting restic restore progress") - } - - updateFunc(velerov1api.PodVolumeOperationProgress{ - TotalBytes: snapshotSize, - BytesDone: volumeSize, - }) - case <-quit: - ticker.Stop() - return - } - } - }() - - stdout, stderr, err := exec.RunCommand(restoreCmd.Cmd()) - quit <- struct{}{} - - // update progress to 100% - updateFunc(velerov1api.PodVolumeOperationProgress{ - TotalBytes: snapshotSize, - BytesDone: snapshotSize, - }) - - return stdout, stderr, err -} - -func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { +func GetSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { cmd := StatsCommand(repoIdentifier, passwordFile, snapshotID) cmd.Env = env cmd.CACertFile = caCertFile @@ -264,7 +129,7 @@ func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string return snapshotStats.TotalSize, nil } -func getVolumeSize(path string) (int64, error) { +func GetVolumeSize(path string) (int64, error) { var size int64 files, err := fileSystem.ReadDir(path) @@ -274,7 +139,7 @@ func getVolumeSize(path string) (int64, error) { for _, file := range files { if file.IsDir() { - s, err := getVolumeSize(fmt.Sprintf("%s/%s", path, file.Name())) + s, err := GetVolumeSize(fmt.Sprintf("%s/%s", path, file.Name())) if err != nil { return 0, err } diff --git a/pkg/restic/exec_commands_test.go b/pkg/restic/exec_commands_test.go index 0353f3e2c3..e8ca9076fe 100644 --- a/pkg/restic/exec_commands_test.go +++ b/pkg/restic/exec_commands_test.go @@ -52,7 +52,7 @@ func Test_getSummaryLine(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - summary, err := getSummaryLine([]byte(tt.output)) + summary, err := GetSummaryLine([]byte(tt.output)) if tt.wantErr { assert.Error(t, err) } else { @@ -79,7 +79,7 @@ third line } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { - assert.Equal(t, []byte(tt.want), getLastLine([]byte(tt.output))) + assert.Equal(t, []byte(tt.want), GetLastLine([]byte(tt.output))) }) } } @@ -103,7 +103,7 @@ func Test_getVolumeSize(t *testing.T) { fileSystem = fakefs defer func() { fileSystem = filesystem.NewFileSystem() }() - actualSize, err := getVolumeSize("/") + actualSize, err := GetVolumeSize("/") assert.NoError(t, err) assert.Equal(t, expectedSize, actualSize) diff --git a/pkg/restic/executer.go b/pkg/restic/executer.go deleted file mode 100644 index e89883e76c..0000000000 --- a/pkg/restic/executer.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Velero Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "github.com/sirupsen/logrus" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" -) - -// BackupExec is able to run backups. -type BackupExec struct{} - -// RunBackup is a wrapper for the restic.RunBackup function in order to be able -// to use interfaces (and swap out objects for testing purposes). -func (exec BackupExec) RunBackup(cmd *Command, log logrus.FieldLogger, updateFn func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { - return RunBackup(cmd, log, updateFn) -} - -// GetSnapshotID gets the Restic snapshot ID. -func (exec BackupExec) GetSnapshotID(snapshotIdCmd *Command) (string, error) { - return GetSnapshotID(snapshotIdCmd) -} diff --git a/pkg/uploader/provider/kopia.go b/pkg/uploader/provider/kopia.go index 6384142eff..73798d9ac3 100644 --- a/pkg/uploader/provider/kopia.go +++ b/pkg/uploader/provider/kopia.go @@ -97,8 +97,8 @@ func (kp *kopiaProvider) CheckContext(ctx context.Context, finishChan chan struc } } -func (kp *kopiaProvider) Close(ctx context.Context) { - kp.bkRepo.Close(ctx) +func (kp *kopiaProvider) Close(ctx context.Context) error { + return kp.bkRepo.Close(ctx) } //RunBackup which will backup specific path and update backup progress diff --git a/pkg/uploader/provider/kopia_test.go b/pkg/uploader/provider/kopia_test.go index 747acbfb4c..ac061d6c74 100644 --- a/pkg/uploader/provider/kopia_test.go +++ b/pkg/uploader/provider/kopia_test.go @@ -25,10 +25,10 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/controller" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/kopia" @@ -37,7 +37,7 @@ import ( func TestRunBackup(t *testing.T) { var kp kopiaProvider kp.log = logrus.New() - updater := controller.BackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} + updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { name string hookBackupFunc func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) @@ -81,7 +81,7 @@ func TestRunBackup(t *testing.T) { func TestRunRestore(t *testing.T) { var kp kopiaProvider kp.log = logrus.New() - updater := controller.RestoreProgressUpdater{PodVolumeRestore: &velerov1api.PodVolumeRestore{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} + updater := FakeRestoreProgressUpdater{PodVolumeRestore: &velerov1api.PodVolumeRestore{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { name string @@ -116,3 +116,21 @@ func TestRunRestore(t *testing.T) { }) } } + +type FakeBackupProgressUpdater struct { + PodVolumeBackup *velerov1api.PodVolumeBackup + Log logrus.FieldLogger + Ctx context.Context + Cli client.Client +} + +func (f *FakeBackupProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) {} + +type FakeRestoreProgressUpdater struct { + PodVolumeRestore *velerov1api.PodVolumeRestore + Log logrus.FieldLogger + Ctx context.Context + Cli client.Client +} + +func (f *FakeRestoreProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) {} diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index b435708f63..cac5427986 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -20,11 +20,16 @@ import ( "context" "time" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository/provider" "github.com/vmware-tanzu/velero/pkg/uploader" ) @@ -49,23 +54,34 @@ type Provider interface { volumePath string, updater uploader.ProgressUpdater) error // Close which will close related repository - Close(ctx context.Context) + Close(ctx context.Context) error } // NewUploaderProvider initialize provider with specific uploaderType func NewUploaderProvider( ctx context.Context, + client client.Client, uploaderType string, repoIdentifier string, bsl *velerov1api.BackupStorageLocation, - backupReo *velerov1api.BackupRepository, + backupRepo *velerov1api.BackupRepository, credGetter *credentials.CredentialGetter, repoKeySelector *v1.SecretKeySelector, log logrus.FieldLogger, ) (Provider, error) { + if credGetter.FromFile == nil { + return nil, errors.New("uninitialized FileStore credentail is not supported") + } if uploaderType == uploader.KopiaType { - return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) + if err := provider.NewUnifiedRepoProvider(*credGetter, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { + return nil, errors.Wrap(err, "failed to connect repository") + } + return NewKopiaUploaderProvider(ctx, credGetter, backupRepo, log) } else { - return NewKopiaUploaderProvider(ctx, credGetter, backupReo, log) + err := provider.NewResticRepositoryProvider(credGetter.FromFile, nil, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}) + if err != nil { + return nil, errors.Wrap(err, "failed to connect repository") + } + return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) } } diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go index f908077a3c..1f9e18ebaa 100644 --- a/pkg/uploader/provider/restic.go +++ b/pkg/uploader/provider/restic.go @@ -13,16 +13,45 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + package provider import ( + "bytes" + "context" + "fmt" + "os" + "time" + + "github.com/pkg/errors" "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/exec" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) +// BackupFunc mainly used to make testing more convenient +var ResticBackupFunc = restic.BackupCommand +var ResticRunCMDFunc = exec.RunCommand +var ResticGetSnapshotSizeFunc = restic.GetSnapshotSize +var ResticGetVolumeSizeFunc = restic.GetVolumeSize +var ResticRestoreFunc = restic.RestoreCommand + +type resticProvider struct { + repoIdentifier string + credentialsFile string + caCertFile string + repoEnv []string + backupTags map[string]string + log logrus.FieldLogger + bsl *velerov1api.BackupStorageLocation +} + func NewResticUploaderProvider( repoIdentifier string, bsl *velerov1api.BackupStorageLocation, @@ -30,5 +59,234 @@ func NewResticUploaderProvider( repoKeySelector *v1.SecretKeySelector, log logrus.FieldLogger, ) (Provider, error) { - return nil, nil //TODO + provider := resticProvider{ + repoIdentifier: repoIdentifier, + log: log, + bsl: bsl, + } + + var err error + provider.credentialsFile, err = credGetter.FromFile.Path(repoKeySelector) + if err != nil { + return nil, errors.Wrap(err, "error creating temp restic credentials file") + } + + // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic + if bsl.Spec.ObjectStorage != nil && bsl.Spec.ObjectStorage.CACert != nil { + provider.caCertFile, err = restic.TempCACertFile(bsl.Spec.ObjectStorage.CACert, bsl.Name, filesystem.NewFileSystem()) + if err != nil { + return nil, errors.Wrap(err, "error create temp cert file") + } + } + + provider.repoEnv, err = restic.CmdEnv(bsl, credGetter.FromFile) + if err != nil { + return nil, errors.Wrap(err, "error generating repository cmnd env") + } + + return &provider, nil +} + +// Not implement yet +func (rp *resticProvider) Cancel() { +} + +func (rp *resticProvider) Close(ctx context.Context) error { + if err := os.Remove(rp.credentialsFile); err != nil { + return errors.Wrapf(err, "failed to remove file %s", rp.credentialsFile) + } + if err := os.Remove(rp.caCertFile); err != nil { + return errors.Wrapf(err, "failed to remove file %s", rp.caCertFile) + } + return nil +} + +// RunBackup runs a `backup` command and watches the output to provide +// progress updates to the caller. +func (rp *resticProvider) RunBackup( + ctx context.Context, + path string, + tags map[string]string, + parentSnapshot string, + updater uploader.ProgressUpdater) (string, error) { + if updater == nil { + return "", errors.New("Need to initial backup progress updater first") + } + + log := rp.log.WithFields(logrus.Fields{ + "path": path, + "parentSnapshot": parentSnapshot, + }) + + // buffers for copying command stdout/err output into + stdoutBuf := new(bytes.Buffer) + stderrBuf := new(bytes.Buffer) + + // create a channel to signal when to end the goroutine scanning for progress + // updates + quit := make(chan struct{}) + + rp.backupTags = tags + backupCmd := ResticBackupFunc(rp.repoIdentifier, rp.credentialsFile, path, tags) + backupCmd.Env = rp.repoEnv + backupCmd.CACertFile = rp.caCertFile + + if parentSnapshot != "" { + backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, fmt.Sprintf("--parent=%s", parentSnapshot)) + } + + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(rp.bsl, rp.log) + if len(skipTLSRet) > 0 { + backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, skipTLSRet) + } + + cmd := backupCmd.Cmd() + cmd.Stdout = stdoutBuf + cmd.Stderr = stderrBuf + + err := cmd.Start() + if err != nil { + log.Errorf("failed to execute %v with stderr %v error %v", cmd, stderrBuf.String(), err) + return "", err + } + + go func() { + ticker := time.NewTicker(backupProgressCheckInterval) + for { + select { + case <-ticker.C: + lastLine := restic.GetLastLine(stdoutBuf.Bytes()) + if len(lastLine) > 0 { + stat, err := restic.DecodeBackupStatusLine(lastLine) + if err != nil { + rp.log.WithError(err).Errorf("error getting backup progress") + } + + // if the line contains a non-empty bytes_done field, we can update the + // caller with the progress + if stat.BytesDone != 0 { + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: stat.TotalBytesProcessed, + BytesDone: stat.TotalBytesProcessed, + }) + } + } + case <-quit: + ticker.Stop() + return + } + } + }() + + err = cmd.Wait() + if err != nil { + return "", errors.WithStack(fmt.Errorf("failed to wait execute %v with stderr %v error %v", cmd, stderrBuf.String(), err)) + } + quit <- struct{}{} + + summary, err := restic.GetSummaryLine(stdoutBuf.Bytes()) + if err != nil { + return "", errors.WithStack(fmt.Errorf("failed to get summary %v with error %v", stderrBuf.String(), err)) + } + stat, err := restic.DecodeBackupStatusLine(summary) + if err != nil { + return "", errors.WithStack(fmt.Errorf("failed to decode summary %v with error %v", string(summary), err)) + } + if stat.MessageType != "summary" { + return "", errors.WithStack(fmt.Errorf("error getting backup summary: %s", string(summary))) + } + + // update progress to 100% + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: stat.TotalBytesProcessed, + BytesDone: stat.TotalBytesProcessed, + }) + + //GetSnapshtotID + snapshotIdCmd := restic.GetSnapshotCommand(rp.repoIdentifier, rp.credentialsFile, rp.backupTags) + snapshotIdCmd.Env = rp.repoEnv + snapshotIdCmd.CACertFile = rp.caCertFile + + snapshotID, err := restic.GetSnapshotID(snapshotIdCmd) + if err != nil { + return "", errors.WithStack(fmt.Errorf("error getting snapshot id with error: %s", err)) + } + log.Debugf("Ran command=%s, stdout=%s, stderr=%s", cmd.String(), summary, stderrBuf.String()) + return snapshotID, nil +} + +// RunRestore runs a `restore` command and monitors the volume size to +// provide progress updates to the caller. +func (rp *resticProvider) RunRestore( + ctx context.Context, + snapshotID string, + volumePath string, + updater uploader.ProgressUpdater) error { + log := rp.log.WithFields(logrus.Fields{ + "snapshotID": snapshotID, + "volumePath": volumePath, + }) + + restoreCmd := ResticRestoreFunc(rp.repoIdentifier, rp.credentialsFile, snapshotID, volumePath) + restoreCmd.Env = rp.repoEnv + restoreCmd.CACertFile = rp.caCertFile + + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(rp.bsl, log) + if len(skipTLSRet) > 0 { + restoreCmd.ExtraFlags = append(restoreCmd.ExtraFlags, skipTLSRet) + } + snapshotSize, err := ResticGetSnapshotSizeFunc(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env, skipTLSRet) + if err != nil { + return errors.Wrap(err, "error getting snapshot size") + } + + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + }) + + // create a channel to signal when to end the goroutine scanning for progress + // updates + quit := make(chan struct{}) + + go func() { + ticker := time.NewTicker(restoreProgressCheckInterval) + for { + select { + case <-ticker.C: + volumeSize, err := ResticGetVolumeSizeFunc(restoreCmd.Dir) + if err != nil { + log.WithError(err).Errorf("error getting volume size for restore dir %v", restoreCmd.Dir) + return + } + if volumeSize != 0 { + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + BytesDone: volumeSize, + }) + } + case <-quit: + ticker.Stop() + return + } + } + }() + + stdout, stderr, err := ResticRunCMDFunc(restoreCmd.Cmd()) + quit <- struct{}{} + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to execute restore command %v with stderr %v", restoreCmd.Cmd().String(), stderr)) + } + // update progress to 100% + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + BytesDone: snapshotSize, + }) + log.Debugf("Ran command=%s, stdout=%s, stderr=%s", restoreCmd.Command, stdout, stderr) + return err } diff --git a/pkg/uploader/provider/restic_test.go b/pkg/uploader/provider/restic_test.go new file mode 100644 index 0000000000..22b44c2acb --- /dev/null +++ b/pkg/uploader/provider/restic_test.go @@ -0,0 +1,136 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + "github.com/vmware-tanzu/velero/pkg/restic" +) + +func TestResticRunBackup(t *testing.T) { + var rp resticProvider + rp.log = logrus.New() + updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: rp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} + testCases := []struct { + name string + hookBackupFunc func(repoIdentifier string, passwordFile string, path string, tags map[string]string) *restic.Command + errorHandleFunc func(err error) bool + }{ + { + name: "wrong restic execute command", + hookBackupFunc: func(repoIdentifier string, passwordFile string, path string, tags map[string]string) *restic.Command { + return &restic.Command{Command: "date"} + }, + errorHandleFunc: func(err error) bool { + return strings.Contains(err.Error(), "executable file not found in") + }, + }, + { + name: "wrong parsing json summary content", + hookBackupFunc: func(repoIdentifier string, passwordFile string, path string, tags map[string]string) *restic.Command { + return &restic.Command{Command: "version"} + }, + errorHandleFunc: func(err error) bool { + return strings.Contains(err.Error(), "executable file not found in") + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ResticBackupFunc = tc.hookBackupFunc + _, err := rp.RunBackup(context.Background(), "var", nil, "", &updater) + rp.log.Infof("test name %v error %v", tc.name, err) + require.Equal(t, true, tc.errorHandleFunc(err)) + }) + } +} +func TestResticRunRestore(t *testing.T) { + var rp resticProvider + rp.log = logrus.New() + updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: rp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} + ResticRestoreFunc = func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { + return &restic.Command{Args: []string{""}} + } + testCases := []struct { + name string + hookResticGetVolumeSizeFunc func(path string) (int64, error) + hookResticGetSnapshotSizeFunc func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) + hookResticRestoreFunc func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command + errorHandleFunc func(err error) bool + }{ + { + name: "failed to get snapshot", + hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, nil }, + hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { + return 100, errors.New("failed to get snapshot") + }, + hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { + return &restic.Command{Args: []string{""}} + }, + errorHandleFunc: func(err error) bool { return strings.Contains(err.Error(), "failed to get snapshot") }, + }, + { + name: "failed to get volume size", + hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, errors.New("failed to get volume size") }, + hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { + return 100, nil + }, + hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { + return &restic.Command{Args: []string{""}} + }, + errorHandleFunc: func(err error) bool { + return strings.Contains(err.Error(), "failed to execute restore command restic") + }, + }, + { + name: "wrong restic execute command", + hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, nil }, + hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { + return 100, nil + }, + hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { + return &restic.Command{Args: []string{"date"}} + }, + errorHandleFunc: func(err error) bool { + return strings.Contains(err.Error(), "failed to execute restore command restic") + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ResticGetSnapshotSizeFunc = tc.hookResticGetSnapshotSizeFunc + ResticGetVolumeSizeFunc = tc.hookResticGetVolumeSizeFunc + ResticRestoreFunc = tc.hookResticRestoreFunc + err := rp.RunRestore(context.Background(), "", "var", &updater) + rp.log.Infof("test name %v error %v", tc.name, err) + require.Equal(t, true, tc.errorHandleFunc(err)) + }) + } + +} From e6c94af3587e466b65410be4cee099d548c0f9b1 Mon Sep 17 00:00:00 2001 From: danfengl Date: Thu, 18 Aug 2022 09:18:24 +0000 Subject: [PATCH 274/366] Add enable API group on k8s resources E2E test upon issue #5146 Signed-off-by: danfengl --- test/e2e/basic/enable_api_group_versions.go | 141 +++++++++++++++++- test/e2e/e2e_suite_test.go | 2 +- test/e2e/migration/migration.go | 2 +- .../case-a-source-v1beta1.yaml | 90 +++++++++++ test/e2e/util/k8s/common.go | 65 +++++++- 5 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 test/e2e/testdata/enable_api_group_versions/case-a-source-v1beta1.yaml diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 220f6a0298..5c4d11c348 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -40,6 +40,128 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) +func APIExtensionsVersionsTest() { + var ( + backupName, restoreName string + ) + resourceName := "apiextensions.k8s.io" + crdName := "rocknrollbands.music.example.io" + label := "for=backup" + srcCrdYaml := "testdata/enable_api_group_versions/case-a-source-v1beta1.yaml" + BeforeEach(func() { + if VeleroCfg.DefaultCluster == "" && VeleroCfg.StandbyCluster == "" { + Skip("CRD with apiextension versions migration test needs 2 clusters") + } + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + srcVersions, err := GetAPIVersions(VeleroCfg.DefaultClient, resourceName) + Expect(err).ShouldNot(HaveOccurred()) + dstVersions, err := GetAPIVersions(VeleroCfg.StandbyClient, resourceName) + Expect(err).ShouldNot(HaveOccurred()) + + Expect(srcVersions).Should(ContainElement("v1"), func() string { + Skip("CRD with apiextension versions srcVersions should have v1") + return "" + }) + Expect(srcVersions).Should(ContainElement("v1beta1"), func() string { + Skip("CRD with apiextension versions srcVersions should have v1") + return "" + }) + Expect(dstVersions).Should(ContainElement("v1"), func() string { + Skip("CRD with apiextension versions dstVersions should have v1") + return "" + }) + Expect(len(srcVersions) > 1 && len(dstVersions) == 1).Should(Equal(true), func() string { + Skip("Source cluster should support apiextension v1 and v1beta1, destination cluster should only support apiextension v1") + return "" + }) + }) + AfterEach(func() { + if !VeleroCfg.Debug { + By("Clean backups after test", func() { + DeleteBackups(context.Background(), *VeleroCfg.DefaultClient) + }) + if VeleroCfg.InstallVelero { + By("Uninstall Velero and delete CRD ", func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + Expect(deleteCRDByName(context.Background(), crdName)).To(Succeed()) + + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.StandbyCluster)).To(Succeed()) + Expect(VeleroUninstall(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace)).To(Succeed()) + Expect(deleteCRDByName(context.Background(), crdName)).To(Succeed()) + }) + } + By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultCluster), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient + }) + } + + }) + Context("When EnableAPIGroupVersions flag is set", func() { + It("Enable API Group to B/R CRD APIExtensionsVersions", func() { + backupName = "backup-" + UUIDgen.String() + restoreName = "restore-" + UUIDgen.String() + + By(fmt.Sprintf("Install Velero in cluster-A (%s) to backup workload", VeleroCfg.DefaultCluster), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) + VeleroCfg.ObjectStoreProvider = "" + VeleroCfg.Features = "EnableAPIGroupVersions" + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) + }) + + By(fmt.Sprintf("Install CRD of apiextenstions v1beta1 in cluster-A (%s)", VeleroCfg.DefaultCluster), func() { + Expect(installCRD(context.Background(), srcCrdYaml)).To(Succeed()) + Expect(CRDShouldExist(context.Background(), crdName)).To(Succeed()) + Expect(AddLabelToCRD(context.Background(), crdName, label)).To(Succeed()) + }) + + By("Backup CRD", func() { + var BackupCfg BackupConfig + BackupCfg.BackupName = backupName + BackupCfg.IncludeResources = "crd" + BackupCfg.IncludeClusterResources = true + BackupCfg.Selector = label + Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { + VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName) + return "Get backup logs" + }) + }) + + By(fmt.Sprintf("Install Velero in cluster-B (%s) to restore workload", VeleroCfg.StandbyCluster), func() { + Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.StandbyCluster)).To(Succeed()) + VeleroCfg.ObjectStoreProvider = "" + VeleroCfg.ClientToInstallVelero = VeleroCfg.StandbyClient + Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) + }) + + By(fmt.Sprintf("Waiting for backups sync to Velero in cluster-B (%s)", VeleroCfg.StandbyCluster), func() { + Expect(WaitForBackupToBeCreated(context.Background(), VeleroCfg.VeleroCLI, backupName, 5*time.Minute)).To(Succeed()) + }) + + By(fmt.Sprintf("CRD %s should not exist in cluster-B (%s)", crdName, VeleroCfg.StandbyCluster), func() { + Expect(CRDShouldNotExist(context.Background(), crdName)).To(Succeed(), "Error: CRD already exists in cluster B, clean it and re-run test") + }) + + By("Restore CRD", func() { + Expect(VeleroRestore(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, restoreName, backupName, "")).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, "", restoreName) + return "Fail to restore workload" + }) + }) + + By("Verify CRD restore ", func() { + Expect(CRDShouldExist(context.Background(), crdName)).To(Succeed()) + }) + }) + }) +} func APIGropuVersionsTest() { var ( resource, group string @@ -302,7 +424,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso } // Assertion - if containsAll(annoSpec["annotations"], tc.want["annotations"]) != true { + if !containsAll(annoSpec["annotations"], tc.want["annotations"]) { msg := fmt.Sprintf( "actual annotations: %v, expected annotations: %v", annoSpec["annotations"], @@ -312,7 +434,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso } // Assertion - if containsAll(annoSpec["specs"], tc.want["specs"]) != true { + if !containsAll(annoSpec["specs"], tc.want["specs"]) { msg := fmt.Sprintf( "actual specs: %v, expected specs: %v", annoSpec["specs"], @@ -367,6 +489,21 @@ func deleteCRD(ctx context.Context, yaml string) error { return nil } +func deleteCRDByName(ctx context.Context, name string) error { + fmt.Println("Delete CRD", name) + cmd := exec.CommandContext(ctx, "kubectl", "delete", "crd", name, "--wait") + + _, stderr, err := veleroexec.RunCommand(cmd) + if strings.Contains(stderr, "not found") { + return nil + } + if err != nil { + return errors.Wrap(err, stderr) + } + + return nil +} + func restartPods(ctx context.Context, ns string) error { fmt.Printf("Restart pods in %s namespace.\n", ns) cmd := exec.CommandContext(ctx, "kubectl", "delete", "pod", "--all", "-n", ns, "--wait=true") diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 9b3890c96a..9e509b3024 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -80,6 +80,7 @@ func init() { } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) +var _ = Describe("[APIGroup][APIExtensions] CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", APIExtensionsVersionsTest) // Test backup and restore of Kibishi using restic var _ = Describe("[Basic][Restic] Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", BackupRestoreWithRestic) @@ -117,7 +118,6 @@ var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once t var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) var _ = Describe("[Migration][Restic]", MigrationWithRestic) - var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots) var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 5c31ca9b78..90f96f578b 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -85,7 +85,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) DeleteNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace, true) }) } - By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() { + By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultCluster), func() { Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient }) diff --git a/test/e2e/testdata/enable_api_group_versions/case-a-source-v1beta1.yaml b/test/e2e/testdata/enable_api_group_versions/case-a-source-v1beta1.yaml new file mode 100644 index 0000000000..c72bb52004 --- /dev/null +++ b/test/e2e/testdata/enable_api_group_versions/case-a-source-v1beta1.yaml @@ -0,0 +1,90 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: music-system/music-serving-cert + controller-gen.kubebuilder.io/version: v0.2.5 + name: rocknrollbands.music.example.io +spec: + group: music.example.io + names: + kind: RocknrollBand + listKind: RocknrollBandList + plural: rocknrollbands + singular: rocknrollband + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: RocknrollBand is the Schema for the rocknrollbands API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: RocknrollBandSpec defines the desired state of RocknrollBand + properties: + genre: + type: string + leadSinger: + type: string + numberComponents: + format: int32 + type: integer + type: object + status: + description: RocknrollBandStatus defines the observed state of RocknrollBand + properties: + lastPlayed: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - name: v1alpha1 + schema: + openAPIV3Schema: + description: RocknrollBand is the Schema for the rocknrollbands API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: RocknrollBandSpec defines the desired state of RocknrollBand + properties: + genre: + type: string + numberComponents: + format: int32 + type: integer + type: object + status: + description: RocknrollBandStatus defines the observed state of RocknrollBand + properties: + lastPlayed: + type: string + required: + - lastPlayed + type: object + type: object + served: true + storage: false +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 6a378268cb..394a8d506f 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -49,7 +49,6 @@ func CreateSecretFromFiles(ctx context.Context, client TestClient, namespace str data[key] = contents } - secret := builder.ForSecret(namespace, name).Data(data).Result() _, err := client.ClientGo.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{}) return err @@ -124,6 +123,45 @@ func GetPvByPvc(ctx context.Context, namespace, pvc string) ([]string, error) { return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) } +func CRDShouldExist(ctx context.Context, name string) error { + return CRDCountShouldBe(ctx, name, 1) +} + +func CRDShouldNotExist(ctx context.Context, name string) error { + return CRDCountShouldBe(ctx, name, 0) +} + +func CRDCountShouldBe(ctx context.Context, name string, count int) error { + crdList, err := GetCRD(ctx, name) + if err != nil { + return errors.Wrap(err, "Fail to get CRDs") + } + len := len(crdList) + if len != count { + return errors.New(fmt.Sprintf("CRD count is expected as %d instead of %d", count, len)) + } + return nil +} + +func GetCRD(ctx context.Context, name string) ([]string, error) { + CmdLine1 := &common.OsCommandLine{ + Cmd: "kubectl", + Args: []string{"get", "crd"}, + } + + CmdLine2 := &common.OsCommandLine{ + Cmd: "grep", + Args: []string{name}, + } + + CmdLine3 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print $1}"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} + func AddLabelToPv(ctx context.Context, pv, label string) error { return exec.CommandContext(ctx, "kubectl", "label", "pv", pv, label).Run() } @@ -140,6 +178,12 @@ func AddLabelToPod(ctx context.Context, podName, namespace, label string) error return exec.CommandContext(ctx, "kubectl", args...).Run() } +func AddLabelToCRD(ctx context.Context, crd, label string) error { + args := []string{"label", "crd", crd, label} + fmt.Println(args) + return exec.CommandContext(ctx, "kubectl", args...).Run() +} + func KubectlApplyByFile(ctx context.Context, file string) error { args := []string{"apply", "-f", file, "--force=true"} return exec.CommandContext(ctx, "kubectl", args...).Run() @@ -154,3 +198,22 @@ func KubectlConfigUseContext(ctx context.Context, kubectlContext string) error { fmt.Print(stderr) return err } + +func GetAPIVersions(client *TestClient, name string) ([]string, error) { + var version []string + APIGroup, err := client.ClientGo.Discovery().ServerGroups() + if err != nil { + return nil, errors.Wrap(err, "Fail to get server API groups") + } + for _, group := range APIGroup.Groups { + fmt.Println(group.Name) + if group.Name == name { + for _, v := range group.Versions { + fmt.Println(v.Version) + version = append(version, v.Version) + } + return version, nil + } + } + return nil, errors.New("Server API groups is empty") +} From 91ac570d8117fb2a83fa9b386adcd3826c14d64e Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Mon, 29 Aug 2022 16:51:09 -0400 Subject: [PATCH 275/366] some additional debug logs Signed-off-by: Scott Seago --- pkg/backup/backup.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 0ee855fe3f..c613cba521 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -207,16 +207,19 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) if err != nil { + log.WithError(errors.WithStack(err)).Debugf("Error from getResourceHooks") return err } backupRequest.ResolvedActions, err = backupItemActionResolver.ResolveActions(kb.discoveryHelper) if err != nil { + log.WithError(errors.WithStack(err)).Debugf("Error from backupItemActionResolver.ResolveActions") return err } backupRequest.ResolvedItemSnapshotters, err = itemSnapshotterResolver.ResolveActions(kb.discoveryHelper) if err != nil { + log.WithError(errors.WithStack(err)).Debugf("Error from itemSnapshotterResolver.ResolveActions") return err } @@ -239,6 +242,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, if kb.resticBackupperFactory != nil { resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup) if err != nil { + log.WithError(errors.WithStack(err)).Debugf("Error from NewBackupper") return errors.WithStack(err) } } From 93a875873b57ab6dc4522c606b47acc713b9044e Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Mon, 22 Aug 2022 09:50:30 -0400 Subject: [PATCH 276/366] fix edge cases for already exists resources Signed-off-by: Shubham Pampattiwar add changelog file Signed-off-by: Shubham Pampattiwar update changelog filename Signed-off-by: Shubham Pampattiwar change log level and error type Signed-off-by: Shubham Pampattiwar --- .../unreleased/5239-shubham-pampattiwar | 1 + pkg/restore/restore.go | 33 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/5239-shubham-pampattiwar diff --git a/changelogs/unreleased/5239-shubham-pampattiwar b/changelogs/unreleased/5239-shubham-pampattiwar new file mode 100644 index 0000000000..693e1c4fb3 --- /dev/null +++ b/changelogs/unreleased/5239-shubham-pampattiwar @@ -0,0 +1 @@ +Fix edge cases for already exists resources \ No newline at end of file diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 11008b55e8..c3a022c566 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1249,12 +1249,31 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso errs.Add(namespace, err) return warnings, errs } - if isAlreadyExistsError { - fromCluster, err := resourceClient.Get(name, metav1.GetOptions{}) - if err != nil { - ctx.log.Infof("Error retrieving cluster version of %s: %v", kube.NamespaceAndName(obj), err) - warnings.Add(namespace, err) - return warnings, errs + + // check if we want to treat the error as a warning, in some cases the creation call might not get executed due to object API validations + // and Velero might not get the already exists error type but in reality the object already exists + objectExists := false + var fromCluster *unstructured.Unstructured + + if restoreErr != nil && !isAlreadyExistsError { + // check for the existence of the object in cluster, if no error then it implies that object exists + // and if err then we want to fallthrough and do another get call later + fromCluster, err = resourceClient.Get(name, metav1.GetOptions{}) + if err == nil { + objectExists = true + } + } + + if isAlreadyExistsError || objectExists { + // do a get call if we did not run this previously i.e. + // we've only run this for errors other than isAlreadyExistError + if fromCluster == nil { + fromCluster, err = resourceClient.Get(name, metav1.GetOptions{}) + if err != nil { + ctx.log.Errorf("Error retrieving cluster version of %s: %v", kube.NamespaceAndName(obj), err) + errs.Add(namespace, err) + return warnings, errs + } } // Remove insubstantial metadata. fromCluster, err = resetMetadataAndStatus(fromCluster) @@ -2024,7 +2043,7 @@ func (ctx *restoreContext) processUpdateResourcePolicy(fromCluster, fromClusterW // try patching the in-cluster resource (resource diff plus latest backup/restore labels) _, err = resourceClient.Patch(obj.GetName(), patchBytes) if err != nil { - ctx.log.Errorf("patch attempt failed for %s %s: %v", fromCluster.GroupVersionKind(), kube.NamespaceAndName(fromCluster), err) + ctx.log.Warnf("patch attempt failed for %s %s: %v", fromCluster.GroupVersionKind(), kube.NamespaceAndName(fromCluster), err) warnings.Add(namespace, err) // try just patching the labels warningsFromUpdate, errsFromUpdate := ctx.updateBackupRestoreLabels(fromCluster, fromClusterWithLabels, namespace, resourceClient) From a8ba4875f09ec1ccd279d42886d53790a3d90fdc Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Wed, 24 Aug 2022 19:06:17 +0800 Subject: [PATCH 277/366] equip gc controller with configurable frequency Signed-off-by: allenxu404 --- changelogs/unreleased/5248-allenxu404 | 1 + pkg/cmd/server/server.go | 2 +- pkg/controller/gc_controller.go | 25 ++++++++++++++++--------- pkg/controller/gc_controller_test.go | 5 +++-- 4 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/5248-allenxu404 diff --git a/changelogs/unreleased/5248-allenxu404 b/changelogs/unreleased/5248-allenxu404 new file mode 100644 index 0000000000..8551bc8cfa --- /dev/null +++ b/changelogs/unreleased/5248-allenxu404 @@ -0,0 +1 @@ +equip gc controller with configurable frequency \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index c05dc75ef5..c0d86f399e 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -836,7 +836,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } if _, ok := enabledRuntimeControllers[controller.GarbageCollection]; ok { - r := controller.NewGCReconciler(s.logger, s.mgr.GetClient()) + r := controller.NewGCReconciler(s.logger, s.mgr.GetClient(), s.config.garbageCollectionFrequency) if err := r.SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.GarbageCollection) } diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index c12ba45d9c..60bfb40669 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -46,27 +46,34 @@ const ( // gcReconciler creates DeleteBackupRequests for expired backups. type gcReconciler struct { client.Client - logger logrus.FieldLogger - clock clock.Clock + logger logrus.FieldLogger + clock clock.Clock + frequency time.Duration } // NewGCReconciler constructs a new gcReconciler. func NewGCReconciler( logger logrus.FieldLogger, client client.Client, + frequency time.Duration, ) *gcReconciler { - return &gcReconciler{ - Client: client, - logger: logger, - clock: clock.RealClock{}, + gcr := &gcReconciler{ + Client: client, + logger: logger, + clock: clock.RealClock{}, + frequency: frequency, } + if gcr.frequency <= 0 { + gcr.frequency = defaultGCFrequency + } + return gcr } // GCController only watches on CreateEvent for ensuring every new backup will be taken care of. // Other Events will be filtered to decrease the number of reconcile call. Especially UpdateEvent must be filtered since we removed // the backup status as the sub-resource of backup in v1.9, every change on it will be treated as UpdateEvent and trigger reconcile call. func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, defaultGCFrequency) + s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, c.frequency) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.Backup{}). WithEventFilter(predicate.Funcs{ @@ -101,7 +108,7 @@ func (c *gcReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re } return ctrl.Result{}, errors.Wrapf(err, "error getting backup %s", req.String()) } - log.Debugf("backup: %v", backup) + log.Debugf("backup: %s", backup.Name) log = c.logger.WithFields( logrus.Fields{ @@ -116,7 +123,7 @@ func (c *gcReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re return ctrl.Result{}, nil } - log.Info("Backup has expired") + log.Infof("Backup:%s has expired", backup.Name) if backup.Labels == nil { backup.Labels = make(map[string]string) diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 8985f3be18..772d123c0a 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -34,10 +34,11 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func mockGCReconciler(fakeClient kbclient.Client, fakeClock *clock.FakeClock) *gcReconciler { +func mockGCReconciler(fakeClient kbclient.Client, fakeClock *clock.FakeClock, freq time.Duration) *gcReconciler { gcr := NewGCReconciler( velerotest.NewLogger(), fakeClient, + freq, ) gcr.clock = fakeClock return gcr @@ -137,7 +138,7 @@ func TestGCReconcile(t *testing.T) { } fakeClient := velerotest.NewFakeControllerRuntimeClient(t, initObjs...) - reconciler := mockGCReconciler(fakeClient, fakeClock) + reconciler := mockGCReconciler(fakeClient, fakeClock, defaultGCFrequency) _, err := reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}}) gotErr := err != nil assert.Equal(t, test.expectError, gotErr) From 8bc464aaa686ad1cbc02a60c1a29a97cdb92c0e2 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 30 Aug 2022 15:25:41 +0800 Subject: [PATCH 278/366] Update the website of velero to refelct the change of PM (#5260) Signed-off-by: Daniel Jiang Signed-off-by: Daniel Jiang --- .../content/contributors/04-eleanor-millman.md | 7 ------- .../contributors/04-pradeep-chaturvedi.md | 7 +++++++ .../img/contributors/pradeep-chaturvedi.png | Bin 0 -> 129656 bytes 3 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 site/content/contributors/04-eleanor-millman.md create mode 100644 site/content/contributors/04-pradeep-chaturvedi.md create mode 100644 site/static/img/contributors/pradeep-chaturvedi.png diff --git a/site/content/contributors/04-eleanor-millman.md b/site/content/contributors/04-eleanor-millman.md deleted file mode 100644 index f06e96fbfb..0000000000 --- a/site/content/contributors/04-eleanor-millman.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -first_name: Eleanor -last_name: Millman -image: /img/contributors/eleanor-millman.jpg -github_handle: eleanor-millman ---- -Product Manager diff --git a/site/content/contributors/04-pradeep-chaturvedi.md b/site/content/contributors/04-pradeep-chaturvedi.md new file mode 100644 index 0000000000..73d1922062 --- /dev/null +++ b/site/content/contributors/04-pradeep-chaturvedi.md @@ -0,0 +1,7 @@ +--- +first_name: Pradeep +last_name: Chaturvedi +image: /img/contributors/pradeep-chaturvedi.png +github_handle: pradeepkchaturvedi +--- +Product Manager diff --git a/site/static/img/contributors/pradeep-chaturvedi.png b/site/static/img/contributors/pradeep-chaturvedi.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1a0103066c4f5560ed71208acb1884e24630b5 GIT binary patch literal 129656 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

    U8o95fxomx(Ts0lj-m;jlqK-f6SXl><}w_V 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≤ z`s@CkTQQ09kaEjQi;1l$C5R6mOX}slRd&A-6p6YKwX%kp1E{K^W=W#Ti@KhnAcZimxTvnpoMf=kf039 z3YE#DePrL!-5amJ)h68+b}xVR#WbSeRQ>->`a99p2>pkot$^+NAM#E^{2!L>ile)GP=Ea6>8@5F&X`UR>WQnNi!y>sjhu{8bMLz%B3II7g7upQaj@~pys%C;H~-4U`~T5)pFx{m=Y81kec$c9V;iu$ z=#2nKfngJS7cJ6i9z`)JMHFRQj_parNhX;|vY(vT&g4TB&rlwF>~ZqJmSronB}+;q zM~XyJ5{p;>g1~|V7TXK9_nQCjx}LLGdK5YQz_WY5&-2{(Dc8CBxlXx_TbE_e>#9|2 z!pJ!lBEn@N-w`BmQa(Twkl+m;91d(;;No!u17!LX0Soi8VEQupq`ksTlm%r&p!J~y zCgm~wIQOWOLGa-d4XhS2z8qdhyT(zVslSv?T83AOwWdxeqO>89!*f^`<>j5Qwb()1 zv6>e`D~b(#1(#h*p^wuX&qfffRjy6$>Ep*f_9G>v6c{TH-^&u%BHXoFDR7o0PhL$g z&e>WA4;-6*{onld^v8enr_<5Hr=aG$)4%__p1S!Ty1u)b@AcRBT~Hel4VLc+H16eR z7;)A|AGeXCBm%)3=^GoZ}#04#9h!tG%H#eYtQU4l8BqWktp! zZ+i1vXEiR!81>b%s*XK5e&Tq!y9j^ToMafZs2w6< zbP-y0VBWGI0hI75YjsW?7ZL%&==I{KC)G8y7y1EW`{TR3sxyd zAY5E-g3-8b%SKSSh6I(eYFD!@CTqhQ37oN_tvK2h#mw62)jJj`WQ9`t+*YiQ_pJT} zR+b2yEVQ=3##^YJJ?Vqr+*`CTKJW1@1r?qF?1 zl`v~oZ^*RXx*XSXbo#yD`@;0E|MkC{zWw51`U4p|-R9G-BTv!}UiF|a-dulIL~kYD z${mIK=Krt1-*?v+kt6`RFw98xXx0bt&$5Q${GjFKHU61(H9yJBi4$k1!-tP%KcR8y zqF9-=u`DaI=senWR)1;iT2xPv7S$^bcHev7bnmVQro;PQ%B1(z2X5{4=Cza)i#S=hj1`F{+!LlrvC$MWzzfDD8 zGRbfKV~M!ea6t$}RxM7ydO;Mj$E~zI9Mp*tGh9@3HuVrp&)9U^=Hif7-TEt#0zO;n z)L-j#^Jz>;p>VS;wKLY1kHW!y6`jugA<#nrDHA8^*Abr+5iESJRnFHN_Dn zW~v3_YrBLrI69Xx^5O^zVfKt7DEB*kg+CYA1at@v3+hG872t7pQp)uDU1Qrm3m9Ke zq=$3l%K8miq!4_@+bvINKGRcIZe5r@{_&p<{xh|+a&_7iIc}lkg&@SWNFF*d1kIxq zNCy{Jty~wldKK68Y#;o+|NHaP7e2pediaqCru*-|cUrs7Sy-V(1Q*WyfG26J9;^MZ zQHJ{4X~yXJ0e^Vmdr9+qlD?M$-)r6ym=tP8ZRikkTsjG8S219|uqxrM{6<9gV?9sb zm|lA6=(P8ReVOw-otxao6$z)WuK7qVkNjLejD`^*ZA5N0K%3IwfBKa#<*>5kO+<4; zR^n_n4oW+x^U8x&*=mMy%B>AE0cBOJ?(%4N2X;$>KOJNdv85%s#~IUvVO(5DZm_m6 zCjk_A;X2A&RxvmrSjkfTHLfE-BY*CuT(1+BPhtg!@RDDtUD5FB3{h6=_%1L-5VdKH zM@Wq?2`_|#1Ma~}a0WwhdqOT`!}&JZu?P=+%54js*w z#>;W}w@g3&m;U37N3(=5o|MRKs)=%KC0Te5yy#MBqrhf#3aH78rg$(zicI&dS-rOI z9nFyGg}pE27?+$9nK#^Q*sv;nML+qQjumCcLHP}@<33tREeHMmd-*%W{d+0%J>`Xr zA(ViW8d%>6Y`em=vT++kl@ZBfIgTE^JRLrCY&w?XK`@2=jpn@Ov!VpyocSZ@W4&%R zgO(6rMyr6-O;BmhdIw4v?k#V9+w{-=_kUUpZ*A?#EGyi(ZOgPJD_D+%F)i$0pL1Z1 zD@|{J!MGFmZ{2cNHg}(obv>J@f@Q_rbx7G2Zchjk7|JT8&`?n3<1j&|xV?eDw$9~7 zF<~@4`-q?=82L3H>SJd+V1leFN?0o8cPX-zDdq~G<|9?mmMYIvuB>KoPa$cCFiGsY zpjJh!(>A8R?QVmm07<55nmcSUh4Y|#>^VLSF-AE;R4#lNQ+*VXd_LD?>Pz6KXpycE zOaj`T58>{O?5kt>O2LGtfjEl!ROQ1HTx72A*$AY$RL9Lw92+-nm`>$bSg!o5p`-0< z=dWCv-t(5XMS-u$Fei#M%8qj5_JXqn9Z!U3$^*%BpF9Oo-dfzBX)F9ST@U_P#JIJq zSI@P_W_e%)`d;VdJQ*mL}tyx{;p68$YW)=x#U1?5yu#jMV#zc3;%?)7F zg+zk6WGb}CUHK)^@T`+C|LO#$E+%l{AT+Kn0X3Z>m9Rt*d?G%}qK~cBd+dpUF+c@= z(gvX-q%75#w?kNy_ypbefxn)wq|}IVQsBkdO?)uN%0`G-P_Vhw-PRj}a5sCw104EI zQD_@JMNE^c>Ib(|=DFwrp#*|}*nXxD`X_+}eCqOv*$oW{2hYOyifE@VmIG20@Dt!e z&!xeA7M=gsN)=l4$E_{Osl-cx~ZP4!3D((OvPJxvyFD4nKLu~zbQps;iP`G3Ff z78T)&Q9*x4a?ce!RY&6e>c_2Sv{=eF%xDrunobbRSME zYlRt?CP-zxbhgH*9Ral~XUkoiYqH-V*EG6yp_r0|t+A^^w9>x{BBdwA*fU(1+l3cv zRZYet9w&^2MAR#h3d}-;>{V^1#xQ+UN- z++J4mO0Gv9d2HIYZF{EOGCv)KO6};n@pD3BH6KecQcA|&g(H9TC3GyeIBnvk6s;s? zP`p}GW2|n5MmarW-P+CR&!!qfA3xF2;MYTR2cuAM57CmH9}7%2pVM0t8{ z5@Xv`O`B)6Nf>2Gmdaf?pRG+n_*_DY7G|HHKbIp}j!lm~`q=cAH@zCFRELuE-aI?HSPvSv8M!jKgWjr`livLKUj69;o764p!BxsmhGPlY? z5|o=c!2-OuGVExVTrjQKN*@XBxmZZkJd_7vvkn`Mc_QaSBLZzD75BD|RGCe`z&UHo zLfbfjzFVSDZT2?YIh*$j8H$)Iw-@;I#WT|<-~WM%{93<#DS@sI_E16sb(CLQYA$L{ zW>5E7^5)6LaSxfQqe0kw;u!GawI)8{|; zC(~EI`n75I?t7*upL#ra;z4wc+GyIhbuC8jM}O|$#2FO*v(le+JDvLvraQ~MmcLr@ zD7Rwta!B&==~&!_BZp6in5%2CfT}Sv4NkCt*5ijBUlH)qcww>;qTinJWw~s|2ADW` zu%$7oZA@pfz(GRDQ>R`{!07n2I_qZ_XW{E7e)31ByVfn6mWN<(eDcZZ-W@xp)zR`R z1mK&!p5!b z=N$o69?_wNdxt3_B~?FRN);spm<(}kFd!U$yC^N52BexcA|PNRI3=1X4A6n>xXX;Ax6&UiI za@)LlQz;oPui(<@j70^L^h+CnEhUxyp+CiI!-SyD#=P*$rUf{oaIXZ;eTiK>_wxSf z+Xs*2m6Nxo&-{ab5_dO2p1ipFRy?0|n?&tTB~Z1dtm&=v8{WK565Vb_;NSzE3de!7 z#?k4MDHm}N_jye6pl9Kx3`zii+{6iPWbT(MEB>%F%Q^|I*F2s%{c4u5ZpnJ{`(x?1 zL@5O!g&{>*=Kftb|C~Sbd;a@9du~6!T_%ke`;1RNeV1-$>YuaQ{QU54k`Y~xD==!g z9?65~XV2cuApCgdr4D7|bllkMi{jR<2?B;TW;Hr|&jlLWsk%eGE)?+zDYrb_ zBQ4AoSjW}c50l&I?2xOh6%8{9>pR8ZThBe4{h=#Dh-(%7wYa_PTbAfq5WhGt*fcIO z-Zeg5!pc(!>902JSJh`iyetdqt#B%rEzDSq}6g|{IIIQ=@sK(cXQ~sIFtKafV zgi#r(Ed#H--Esoq;(-{0mdhU|s(qc9F0c_;R&X4yUF%^fh5&<{aN?L6hrnS#(N%#r z!fvsNsE(%b;CHIaUJDR}I%IU5I@@!>u1gzzPc0qZM>#?&JroFEmj0=2a z+PVAw=_h{jr>0Xmy~BCRJg$`)%D@9f0AHop(mz56Ue5>%x4pfyG1M^Z2oS+9zNrrT zoy3@wg3E@BtJ&CK&J->wK?=|n(4vS|q_11Ub62N*`wmVoN1@!fZf*6d;7uPH72HE1 zwz8lkz#)MR3}F5L5Y$neb`BFJWByd(s`b0R=eEjlV(!b&o$w;t>&A__ed^?u>6vG~ zGrhQXUpkdt85wlnjK;G%1_RWC=2(Ikv=XQJH(lX7JUPeB%hs;A%xKm@h6 zHRY3bx3Qb%20BDbcV(Qp_gl}!%ATmHfyF7mI@2&qSYui3(g&mMZ z%M()jA_*StNx0035;_b}Q#e67WLp9V`Lr{^1A~h|NcvHMmpnr#76nrEy_e{9pa!!^ zmE(_MkA7KzVeZp2^%Ed@EgPg@ zw4En`EsD1A-xgf^1kKoUZSY1>nSZu;VR?BeS+5&hoj!dsaK0-t~{N!*u0Soac#eI@aS~x)Y&NAtJ8-*^25_TyYHJ$%gP1PNfNtBHWoAAacJVw96v^i3c!DC=$)3V|}msPDd; zOO*sYV6hh12U*~D^oz~Q`bh9FM2#zzBe2olBM1Xmuw8lbTp5XSs+}O1D)-XsveE<# z^NnaCOLf~yr8A5iHUpVw0955(jSYc5Jt4aZS zMTiCaaEm5Kj~uCWy5>f;OW{#M@V+`$&i7gs72KjFTpAjv(|FX2wKi_pkiEj|>%>H3 z;;=*lj!q3s04GZG;N0Q!gn)%p|NO{ z&ZJMR?Bpe=KJ{B)@gUrnjtpwynKc}nUleCI|4^}pSt!A=Fm_)(ui@er>AU5(qRi8SWF zPKM-Zpvy*?$HeEr>%9kp;B#T>)hTc8Fga2O>jt3G;wdR8pk4q$$XNRdNPz_egrH)U zJeS9BOl&hSA;6%XgG3tNX;1{Nabm0-0XP@UT$UnrSTDOGuI{1z`=r6FMB*6pjgb-z}4a7ps?w2YaDxw*^g^b1_Qu;xx8psmi&9jki!1AjUBex& z*%KrmKXca-xZu8Ag(iF>wjm(E$&?hFWNPKmbo$h}bb=e2V@_f@97<(%$mp~Ptf(kK zEo~G8PT8F?m&s+wDddG9(Pc#(f*?H3GO|d8Q29!z`&f;J;0~bsM*ST^eL0%Z_=uG@ z|5ePv@&!?EfA@Q*=brgS4nDg&ojP+quNVviE?t+UeK%`-WWJO_F~xuZG2+?05zNjT z2dQn${Hynym~%uX%#JZkA94SbI8(&WzmBdL=)PRae-|#@NU_6z5l7; zJW3|fenMh8ruvoo;BE*e_y-O>2NT}ccl_P>qBL4?DSEE$lJN5|67c6f*a$9Mi-quf z6cbySpZ6*c!YO!aJ6`Ca-)YBl&*tWrRK3wj;pYWeUwmYG{)KO6T)Sa<{B@6}zZqtv z3^2cY09e%q+b}s&z7ob>ciT71I?nQVg~Wl36NJobY?Cu~YQwVgivs*yBC5tr#uP4r z07Vz&Ag~)ZSj%2S-vn!B9E@a4;y!KWqD#iT*T%#UinYPn+NT%|*CatEH_@@4wIe0< zg$1i#8hus934#z0X8!G!z0XanckQS{(w4?*Y+Splf))Zwh(Y_GOBC7am~|1jHJPaQ z`oouB`p&dED{w?5jVbN!xSDoG(1mMQyWbrWwxG73X z#uy)7%RS9Kijx&KhszT&rac?Mfg|^>hB+xK(=NO((NN<$E;IMn7?c9z8HpLd!OB<} z!8qwcl%jjpkNoB`p30KK1a>2+QK%740iz3-1acNnuYbeSX(v`M-^&6~ zkBUFiu_XN`&=;@u#&h@J6#k^*q1ipZ@e%qc2hWo4;KOR@*0_Vs4o<1a6`B+!w;PXP}7DZFJg6>I}3e#dNV?TQ^o%iHE z3o9RnsNh8y1Y@zFwEysSN`UjbqRYuk5uz6y4uzvUoDnp0r0^*SRCvsNbr~jT3oIp6 z5jt=aP;T_{671kR=)WH8bvlBzKBmDU4uaUsY_3tHc;-H)JmFWLaW**M63sSc8fBFg zUnyTZn8Il2W%$8W#P9@>*K$1LyAydsp7*t0jzW0&;YX+42_h|ukysM%%@|diV9^H& zPf3j*3dj6>dOW+vee{J7#Rjh7&Qy*2_z<6)W|QQ1$xDz$i5Vn{nIPf?up~#|%Mc?7 zt2TI3xp-gavka1_w{Gpm1gOr3F2?HXQewgMr7wP?=pMDIBcQ_YBX#HeD&j=u03LW_ zL9S1S4rP8ZE+PS4xG0MRayS=expLJYJZ@I%vs<2qfn*V20t(Tr7@=bNJ;%frZHuVt zYzbQ01!NN@o2rx7Gb?l4%a6(@z;QE#mmBn5KF`u8C(A*8F?A4G0TN6W#1Nv`=pb=r z+|$f^EnhV~`0(S?gO9#$`ts*~FMAg6jvJi$yOU?KVrg>;RJl2URh>(saJcU3YT;Wt z-E;SjvaaS{S#V=gZX~N}2Z+6u+V%ZP`irQ>*qE_}DPzKnmnjAK!H|U3yrI3B82nTO zoy%3Lr_yIkUfMam2N$7*1GqE=#66V=B<+r>#Z<{(0uaqEipeq%F7y~U1FN>+m1W1A zOEQ$H@vKVogga|IDOye-c^k?Ra8qWi3e!>kBV< zY2a5&^@_^igxl#HhvN;cS-U<%l~&ki5^43?OXFDr1t^?A4%f!^$pdC&V{XqG(!7N7)49N~fC z2r2lK)ma)%Fk&zf_GR&h1=1x9 zVCB^HoI9)^1jckp9Ul_du7qIfBG?rjP2Dvmk)Z{Ps?8Hw@~9qt2n%H8)66vwBVe4> zdDWzOP|QL|SwX4{p4{teMA>_lk#g=+GNwb|S3ixV^_5@>jM3}h28%KDUG%SGxQ0fdLknr3KK{9^N6wB#MI=`A{0vj9S%0}waIpOD^ zF~sF}>kx+Zn>f%?-3a0v(^?TCAi@3O7TMuJQ|U|!M3B8N6eDJtU4+Ks=j%5ToQY6D z$e9qApvq?vei1XkVpij4&!mA|Wm2#N(>-vMR!e)B91&@6^&=W0FU<~)4`wdz-~7ve zHeJZerVgdwCh6C%>=!I;%vreB9M3Lm zbIM-VSe>i$u?Ru3zHld@u~-AV3G!S?xxz!Nsh|x4VtBiFC@#SuRyF7@0me-5H98WKbnQNFoc7za*xLm_qHmJqPM z6pLONew3S=y0&fK9-*;@Ixc?EkfmzH?S7rDRP>3$j6(n|PR13!5+Pm~_qT(WT+Qwa7`WBkeEmUtR;+k0JWqfL2$D67I$*#pBD`rQb-72o zBN5*{jYS@vO#~2&!pWzVJmJ0kW|4Z@1;KPmzUo|Rrtu&VBKYzc591VKQ$aCFG#hO{ z%BAT_5DCEgmrs-Oda4dm0g+w_u_oiI%UO5&uJ?Ul`rIG<{&eg_P9qG$_I_vI^!mpi zO4hh`8S@aRIxISUVSTu=EDJ5GP8yagr2lu_byo=1E+s~A>4Q&9Y>tgWL+I;yGnu|& z5Q0xDFG=79_te%6B@KRXn7AV@Bh_0ml3a~{1 zSzs?1wI;)btHGm%1lB8ib9x=99vWG2Z;9sV@MJ9`4BVLN?eBPZ9lyd2UJ*sRD3*w) zH-ci40kWlz`!uHnQSe+SK2TIHR~g!Zg! zzx*rh1}}~Z(qBpeZmZ4UXy6y`^fX_4h&Qqyc4?-=+L*-P{C9iy>>=rx_UO@b(>I@e zxuUo?ZWu>q(Q#g`!`*Na9&3WpgqKBKD_{`7OHfK$HPZIAm>|`P7FuQp5eDS6iSLj= zqX=U7j<^VI0fY%+fJ2UZz$0MVMs!4VG96}cR;|v0M}F<5!unn=Ux=W*yz)7jPNF8oG)a)L}}6NyatJ;cUDi^7oV!2H@{CAhBoZ(eL@;W6IE zQ2K^|2*jl#+}h#p5zrFK)FBU7$W{0a&;tkRHy%eM0s{u+Qgty-jX_gR=iO_1$R5R+ z`*03Uc*mUDX5BD>zQ8e$K)G7D5UWk$jCNW`I{-!aZ1KbR1fP;J<^-D)Aqv-NbI{BD zDa*Zc=gt~iJBW;x1SnXzxHdPic9#ry2veT%R-AkR2Vsd=&+Hw}K43wltNDFCbE%6H zTlnzDel(F_tF(gK2rBx+J6uZ@*h$~;2KvAiAkm1RHJlyrl>auLq}~4BN(f&|%G4}G z`mN6rzI1*xOY3YdIHkDZ(=)g-waMJOwwxCA=F1(C}A%v?}JAPre=mWG4y1uR7g zAr}F$Ge!&3a;Gk3vKsPeudFvvG1P43iLjFhqj)T$p-B3t7AX%k`e#-}UQ zR)P?h)Y>ps3R4APb3p*aTnojceoE-;^oHbLngG+nM0nr!uJ=yA`x~Fl7%Ni+VG7%( z9O$gy7%aB$_V>1^?Yh~u0bbK8n%55Dhjc=!eM+InL2YG+^72W0gSzffCg~$fJC}M& zDMa9IE6G_t{VTU~<{H`hRtJTr3>?%PX3plN1j}7dJ1Y~k0t?OWY~DKHJG5Z=N-^Xa zix9m4yTM%tL`-e@-D7LXdCSM3ZdM1f&8q}jD=R}qB=1~0{Yx$n6k6%Pd+39Poj zB%hcDp5w)EX>A`o6D4il^m^u88I8L$m9`;!L*r*fcm%L=v`y$26oxlvN;^=naCjA- z)NN~F3!&(5PiZka#ka|)-HNo&zAGbjJv=xx!h0QAUYp?&x~+_m5fn9UoLdXhbNtiSJOxM5wXWNf=;cVV~l?IV;|pR47zCHvaAN#6y~#Dl?$t*y(Sdk zzBBU}mD|%+FubtUGhI4-JrBaCIPvHKtQE_h&(;KEc&6CTlN>|WJ_as3{6q+NZNSS!yR zIZ}}3zIJQ;UJT3i?LMln+%)h87K*a_mS9Qnz|s>&Cn2zM`dV6?P*+=~NVt6#ipS+H zrP~%3Ov~amT+Yb|7z1AAqg)D>l!rr0AJHFewMC#u5Q$*RL)q4lC;*ri)ca(-l=)bW0;MzwuG2Z*^tN|S_uThT6fb^EpJSC* zCx}G(fCo)#S|>d#p3QIg?`UjWO1w$&HUHp~_Vid{dG$>_Xd|KL;JH*g9Xxcfl+Ft; z?7i(VnBibym8X>TEnmam;fwGP1y$qaw885m)cP*}0z#U56j{kf+E3AG{%1e=={QpR z0>~HIzt%(Mdq0cjOCAiR9t1b0!L0lC?W=n2rhov?wL99*je>!{FCo~lq=kstz0Maa z9|pyoCvAnAS;G@qp0+lw?xt*N{{7$mzovEhzAzjm3cRYOPuis?tTsC@3&SCPFW4?Q z4UR(O^cBHPrw~l=N{E6G?g=39Fj!kK=_5EX2fQc`j{~3D@!N&TZX}vWcq9TGtyE=M zCyXH?Nq~zX^9*efnc$WbM<}^j1Rfr0gLmpSv?%KqSd|sIy%Jnm$e;~%V1D$}7xd8| z{V)z?83g`3YXRiSEzY<%%P>ojIvQ`BOF~R_6689GTU@D`d*0kk&kX`t$;YQlx$w-CU z8D1OLZFwP0Kha0^)^lG9OiR-zyRk(4O|7xWJ_$BKY~|SP;}h&zMkTPIb19F|#-bDV zfnl6oGLybWd3n=Sh?`7Sbs%%+z?u7gY4@6^y;H{NT zGuEzIIjvp4CYAI%L|GEr50k22U(=Cl$GjSahw%vwMO3?l(x(bMgizc&#Anfk@8OIB z0H3j=XI#WmKDpOR4QK9W%Ihxy%%ke3=&-2_$ttPtXWTW+kxO1LRW6Gi#>0r1iGYBC zfc7w@Ek76t4&I>j)Q2DB;eqJ8m07h}JGj8G>H-&DsD8ye&<{LRo>1Uck1cosr-X>2 za2^%>ImXrNC^7Mc<#%$K~jgzAVVA zH^5ll&RhbK$xm3cPZ5Ag8p_Zo*ul%ls6!q+XsG7?O#%R?)!_**L!l zlRjDOQAA5UgcpM=TPgKWGVn6+Yh0*rED(b5IwW}1u21!0CMo=1f<-v>362JBYllxX zu_1@k8)}%5_6S(H*(psKYA-ER{qSX4gMeO28H$zy5-(s~S!2QsPula|7tFfK&X7oB zxys=&9)Jt1UX@833MBkRz{TCh`bJd)}0TArSDi zyC%N;>1a`%#6(uBI0;bl$~^7Xfn|XcllOX2I}_TH5I#uJ4;?Gv51h2LRKn}Co)^d7 z{;q#DMoIvTPlSz5Sh+xF@1EKfUmXn!`CFU?-ZGhu`*F^n4=SUwV8#!289!5%POx!&GMs;oR*G;mO6<}}9;1WaH655c4?+_Nv# zX8nyDvaZ>>-MHSTV##VJ44A@*%NO>`n3TtKu2MFkfjS5*dguc$rmSypmEl5g7bxZ&3ul`K9{@55rj8O6-NgVA%#g zTn&$66#@!B5d_yUx5^T1kag-|Lt(GcXo05a4YS;?y1*LzxmR)qv3NHmwlB{%crw@#t8(b zo@i*+k#f+E`njrwk09A^-)E?+-`cBw1U`K+XNjR%0vQp>1V2KL$T&uXFE}imrkqb+ zg1T7T9$PhTiV5;;^jF^pox$n3dxjSIQZz$d_dKIK(7hNvzqw})BU8tvtkJ%BF{jw4 zpXy&8>ri_y^9;Q1DX%Ut77R09Q;$A4XG*lzFltd&x>01bT^-^UT%60S060y_VOBE? z@NGXT5AAG}$XqP!e0Y#>vTU~u<7VyfJWL%CM3x$X=Ckaz_T=V!EA{TLh&Y;I@ToRnLQftMO63Ne^w%1hU3zBZPWvZj#=nm#3D>@zlZ)LRuN6|I-SqN!< zj0RN>P%fBzDjXdLlL~4ZTk`aGjL@5-39Z#E%Ba`;neXah{)fg<{}5P$hbfL9>sQ$m zQku;?pU9!M(?aDT2t$Z5&z*=H-_PXiUU;J@n$fK!qNp%)h!zIs zMGz)!ICM<#qo%#mUxMh^5y|Nj#mw<82roBNUwAj_8SX7Dtq<~E3Je$%9K&rMOsDVe z%O?$-u$JmAMUwky1jfM=%1~fjTr4S{xlXPwT97RH<*_?1LitAL)SbE=Icuxty%_uX}Hg!ZX>hskcw&piaZo~`J zg+8op)muCg;ffVy1U%#7>KnNImVB4M?|bzBHy)LV^ocghdm|Jgc^Vi=JvY@ z++!{cvKHxO0qxnS$9K%c0oRa*ZLn6C`o>x*qU{?`g#tfcS{~#lrkUGER zavg+F&h!Fd0GIE|^mufpg+o+<8hF=#__#vQ^1a-s{KfzS7GrmPg$7!BzzHS#4K@*5 zmRgx%Q3J- zk;*%XyL&Zjdb!B02NM|DIRof`8^0kJABW{R*?&?rPvhvb;Y(McKy zaLl@TFcyGYNZUOm#5AI3@`D57bx4Bq(q#GV-hl75=S>v%t3NX$G(vjZJt|_AFGr9N zT6;^gpgTNWQP68ylC(NUWmvIv>deV$(fZZ3XZ6^@SEh5PPfTmF^zdq!CCdXkXqNG` zzN|<%om+$%^m|x%0wHRyFW^V~?wg=RuW#$Uu6BI5NReNXzA3oKo4!CJ1Y9>T4^|Fc>hUF0jSI#1ptJ$ProCr4M!Kcb9lke*D zUB8vnE_jQfgI{%uoa>8wUZ7QThk=QQ0y;1lhn~N9x^UHsD)XkSSCsIn1RdYJ>%J^- zFpVg(-@92>tl;h8RiCYRDgB@>aK@sh?PRXLnEo;)vM^&+!ion54}noXiq~^rAKa5} z^HVzAG$arG%yVV*Kix0<@J{v9`)S5=aNrTJ$?sAxB?+H4-mAl<-8(&^oB+bIv;cY- zQkF0Qm|HWLL#3X(iW$;6E0->$d`HNXfoyps`DhrSW~nEDEkEormWl6XmKG{Yz;ps4 z)TL9HSTih*ayp??wT4)}v^9{6fh3);@A(8j=JTWoGwX+Z@`!jsfOZ|`T+=}56T+7k zk0xK62Cy$mbcMQ7;#^-Wrha6iF(MCG2r1 zB|Q27&-h2AUEk2bREovxgv)*N*7!ppC^~UkpLx@J?|+~o#f$UyJ|{hx&J#ySbL9yX zoM{uyMZ@7(GH$JQ;!*H&zzZ+h<(9uDe9gDsaGpdY! zJy6&jk51G219**SRHXn=f#061#CM$l)T%5T04SkzVxO^==Mv`gy##Qf1*5bv z@~gW9G(zeVa#iJdY2?7+#(>ACq?~JfSLc9JAN2>k;5Sv^)3zdRFC<~q)7b>QDghL8 zZM6za?X7xZ%=*OMZEKzB7y|h5)7j zRW5VNz#e!;9V4R5bqO=P94B)~O6p>k(OhuS6S0WuAVX#hZ=wO{k2>3}q>wECrDz?UPf9SU$1E~xpx=(x^EoONUIy6B*=cnoT|EH5k(a!(f)$C3D|qn0p#ntE2rw)F z0BK%z48hc}@-0wp`nB(pz!c7zA;*o^+;LV!eM1;WxjR1x;*UsnY0J9pn3;moG>t9jMt zlG$?2F!JIE8LP|<#r$3~Qi2ei6fb67v!#9b&)!#a*78w0+9%{?@dKBHm#lyG=@~d( z`rrb;-|%iMFJC%PLgl$K@GPJ2v-`V$SEPv3_GgmZK<$*W%2fb3S&SB(sGi^`v za5MxMaPYs~Ta;rZ1&mlvixDJ@c`9pQ5ePs`wnpqAfDqe(iYWt#PL0L^ExFhEba*^x z723K001WDEqy#OqPTO7Kh)7G|)T|&&1Vc|_<%NNHY+KV;^l`Y> z2ymH(qWBtDt=)?Pvp&0zwga!q!-X3ODTYF&|f*f2P2}j?~=L+lSL!> zn){|s8g`zVdKV`q5cMAGcy-zoZ)SP;Ej3o`00PxPLD)=Ty?6zu(q8kGpwh}H$-LMr zJbCcofr5GKU0d`0Ue8Oef_>#NoHsv*ByC-i2|_S< z6Vo#cPp**UP7S65AGg>`!hkP@pMm>MB^{5x~u@i`S=JS;pyIG8d9(ahBQ%B-Li|%yCn8$ld(XWf=$2M(;wZK`zIKv&-2h6_1 zS)`!uf!8H}1%!gT@q6GKkZ@Y`O`TOf9)h(7PPDj@S72(_efWX<9x z)B#qZ@`5sD?nH#LE@{kzL8E?w4}x40IF&ap>FBCO-y(m#ztrM#%3<@SjnlC##xRbx z0K%Kc?5<&@91SubO~31bN3wG1@#&QppPM$U$~boA;#lIebL(0bZFpf+v|=W4FrQa= zG@%QKd~bn>TvlK7hv34C?+EI*X&lTrp1Y?n^EYF465KIni$qu5+9S9W5b9yx(Pra0 zcnU1FKp^-mB{{<#1z12q(hSGw2(Q{hZ!RKQxaT)(%K8mFgPk`4=kg66Nr4(Cf_>Cy z{HjlsGzD9Mp46AGX}yznd1eni@^FGmW*ss3#42U^<i0^gE;nb^Lzad4PXR`7|DT^jp zc<+*7&62UAaI6yM^j5N3WFH1u6ydmFyp=o)qTzSneRsy^ujIsvxXm#dE3#PK@tI@% zgFtndR@_njXZ1u^8_u?Jh%<1cf8g{fDZ}Md4h;H6aJhQMQu+mUaH@AiZr#^csqxm# zrhEEa9!-fvZd3|w@>%__->p0ehk6JWSiq*frX}T>K1JwRSNDyrwT*uAmaykj|J3cPk9xYut( zo8=jP{I7rim!>a%{tpuX^m>aGWn60Ge|XRdi7S%t@ZrPLAN>9w#71O#B*ur!yEfj_ z2#A4q91WxY`s>oSqFtWj8_DnST>pkLmG|~^S1`+qk#ER_VF))@L|$I9^XEWg}m!s7Q>#{^4Z_T#0Rgf)p zAdc7CE0>3*uxi_mXMxdJ&oczP6Mq!hP%z5DR|#s`4w!N2JF;2jO5nKCJ@=Z*s?Xi; z7I^i+ku=(Kg1w~Ok+*5k_q31chLH9`nAz}!V$DAR6#$Lsy`3clp5kEb;aL>dQL(o3 zfz4|!326*r7L{1kKu{A6PCe8{t&YkWx0kXy=Yhwbm=@=71A^+jB=4%VWek7e1AOOI zBlyT}r2pgBer@`n|EIq)`R4}bjmFHFDrTfaHIJuwG;Wnk9D zD&zUy$`-|$G87wE6z<^>pMzCG1BOT$bTa#)F8H%*4MO19G$7o|XC;_=-B+LcEjui2 zPsa)3sdxV4pRm?9`Yh5*pjNHQ`%3eVTPU+vRS*&bZk1@5HV0TQb81&+010S7K%T}2 z12hAP9MUL+v0%^N)}R$k(2)qEkhl5=lOTY$S#=#Q>k&A#199LVoxK*%fR&N2ELW_~ z=)7GS_I%R(#-*zwU~94raK-8b6v9Xjcy61LXmUWaI_ z=q?6uv?=#z>(;xbz0W^a<08wxz{4sm$+QjPFat!y8_%4s;LQPUShU|`v(O;0vP>hv zY0)P2`?Q%HF&k$TM$C8m2~WPK$!gbcLNdxL10HGMYS2JkzRT}BoT&@`D*}KHxd*nU zNyDcN$|~}epjBM-C;`A!P%n|~uvWbn6{FYb@B#Z;l!9`%+>^&nmbmW@ZuhE_V%pSa zGrFym%T}z;w!FFN$*13vC4;Fwf}4SAtlm2AS1+;;4ET&Cy8piWr_X)vbJO4ZyMH%* z2<`|&;0C`oDGhnSNl}zwM5%1e3#PUt$n-0}@-K2`^2zB3o_;!t#X)6WQXPS$;4;l) z+=~~{i-Mql(aR@3#E-luZQwado_FE97mZ2E!?F7)Qc5m3uE165mA8bLaE4zAz53?= z+F$=~_CTHt7#I*0Ax)zi@;miZr!+)J+hy%B69#Y%fK)mzh@e$Qo@y{{VFbVqX3=Rt z6Hv-Q0?&ZMZwR3n@Y@h#f-rj-(@5Q{VK8)9j zXoEt6_kQnCesFKIB+((jSEInppE_JE&8E*emn+I^K00sR+*3IFrT^uZYCm%=V2OfT5Il2=Szkgy?MxN=r0AR3V$mXbr@28^7X+g z*^F*Qk<%mACm3<;*pca1f90P~|HI$@+tWY!$Ny;h?ce;j({KFSUys24T99-)uJZD{ z&}n>vyuNcZV4%2oYeu>a7ZxK zk7+%FuPUp(*)?b=4`xcP5@y;S4^WizF{_wMy%Z&7jsc@~N=U4&1+q8GUn8K2Hf!`niee2LYi3JwVSr4b2CO3I0`I_Vwvs< z-qGWifBBd5Mznp!uRN>KXFQ-EaO5P9eAH+DChyV+sUZ&+H7;)!{M)&+Z{I6rp~->w z;^;Rz!5L-EFj*2ia6DweFspg4H!x_kcsZZ7JT-U*mz7bkty&YvG-l66g~(w?(pxs< z)zx)SYcX zhsk{M`3NT~I-lRKy!=uvjxZnQ%>b;*Ts~Qr1h>nrtG$tdkv0Z5O%K+8KNY_6d?ddm zpDr#_s!3lXeDEBB(ecWa{!;*?WGFj=-L^y>iUtN(7?sx|U$rr-AO3kRG80Zp4}R5O zWz!+J#h^~n=juYgH;S`l0zGAMax+z>jG;=rrwlf|KV~jUth}I<^Rg+MIBrFl5}nAn zVCD4SlTS~lbGCHmWTuUAPtBVfj!_2azIFTd8u$Lr@BD6^Bx!7ke+WVGMc^*@;9=HJ z*deek%Mcxm*5lHR$zoQc-yrbJGtW#rw(lt8A?k0c%fk7ZGD;_ShNAg!uT6NX;Y;B1 zdst0|5N*j1UaY8m;0sm@HqTQ<@j4mmwF_M@S#r()r=R>U_7q`Kh-+y+)M5q=(IyBr zlEw&1;JFQ)dk`{&L8ThIGZC!nMDjwK{I>-#-`#UD_X8Hc)mcag3>b)Y)1y(JJT$tb z)AH6ohzmhkXL*c$*5(z7l+UY~7bS$h@Spv{^x0qg)%0~mSvx@$FY1k7luO{Ix0bP% zM>0k8p$~pA?yyXz$-QRll*+he{mpcq!E@K>lD-2!28LLw6 zPArgds(!<_H1MNW1i0sh^-yX7B`o?Y$wMHOgC`e+6h7c0v?RohOTy$koQ8H)U)MEo zU&u_oRlbC)+bqIYi~Wc9ZWl_ulq<@pUOiFrpv|UTDv1@%bV_0Zb^3h*M-CnkNTD2W z)SP+X2_C(bfh&A7E4@x}*?~5=u0MCtQKJlt+2prnuQJ?5C@-mWs%ZIisEPfrzd;VuX`HOo- zC8a6SC?Oh^4^2QDU?jh3GmI(EsA%LzLYl;VK)4WG8t9W9Dx0vwDD#=$ z6mSqEBCtJW)Pb#)39gA$?#lY!M;>`J??Qb!L|Ig)+-akxM*^?<3y&eBvyHy<;@%p^ zdJ`ML5AKV|PC?-Iau@X1qqw_j>4|a3{~$T3!#DRNX318^iirh~XR}^oS}*MBMzQz!i(ix(oJ2 zfuyf!do=>9PIU<`8mGfIh2`)v%K~rCFPrusJ2gG@`ZrD+cI?awuq-^ul!>|1?o;<` zd4e-sUQGYBY>F@37;0?0(Ug{7=1Y5a1Wi2R(|_bD>Cfz+4w}y zo~nVLagR)>&xMBW&wu=<|K^?{7#J@-OW)cbN7%tc8q~OZET-Q8^4+JT9`|+5J%X+L zXjoo(B>5n5wBme_KT>a>8B|tr@*tO>1uf&e0^tOp$UPd*eK5w2`}_oTlbf)SUa803Z&zC-|l-q`eW1T zEjy;;XS2vAuVjSVS`rz25k6bncHVn`mX0o-UU>fb+D6$t6NMN>VCPnyvzt$r%QVcI z2>a^Laz);sO8F96r|Pd;pLOZ1d8{abT^y^fe|1t|gqI7gZh@;ezvV5{-n}nOyLay@ zK6Jk=WcV+%kcJXN6heky;Sb-#zjLMJy;GLzS@;(H(YFP*{r}hw_P6Vgj zQuh?l7>3fIh6>5SvimJqiF(fFj1Ya?^SqcT6)W$VI!Ao~m3&%IT?6K5U&5^|_XlIC z2fzm7o?$-Ew1w#k&fL%KJPT~f$I3vWakkeK9zAw+`a6H;e=H$e9w8N~$53-wUmA8S zSSTr9mv}K&h_-Uo+6e2$9K*7`G%g0XcQ+z+wDU%y#v;8ArCtzNoGw_WBm_UZckRl& z>VbOApgcsxwIq0`D0lhpxl$-=+1fu z9o!qqJ=bWf^LHC~0PZC1qMbyKgNf@%NCyp_?tYcfSLS!73;B6Hrx(8I-5;FRZQnJ0=TKIJMR<4>rnD>`v01{#iS-ExF3-I7@zC{f zc4~>p5=K@Dyn>>x9%gvya^7Sfh3(uW`CK*q7RxEZOlb9)z(SEJN_D}HId1qgHog1q z9h22dR<2R{cKyhsO?ZLB8ghXH-SW&B-KVRAp|8&RR};T035ACi1_cLoHD*k%E;RT=4L_~#C?}E^0)Txchk79 zZf%YH^;(NCgQ%P28K(DK6s=vft`Bn=Yah$0_r?{U`Q^_{&pr3eAS-@91f+l5uDPtQ zEKtKR3$yQ0WO8|;jO*9ml`(smBA;v4tdG#H4q-C4mhw+L@w(tfbk3Y?_J}4RLgQi# zM_FQI%HpxdACKGge7#L=RlETkhbb0LrS-V!LBTCH0GHChw?4reILEyflD4M$PJ4Sc z@TdHs-@q+8j6CDBY13|3s>$=@Nj_q1{&LW+Y2B{5po#X;8l410y!SH@=3hiKZmhP) zOQgLP#d*OU;cd@>f}^ZWnJn-wob+!#Z)iAwC8s)GyET2_FaE@|dh5>V#k|S>YR0ik zvZj}!x|Mnf$NDV3w{F@Ze9H+fzDXsa@$up%Yqu#=(OA3yR(;s8VPlkrolQZC z{D`LD@m%s9PAgwvAJ5?*j=`^7$;u~w=5Os$a1asEMkO==cXMb|gix_D2}V(&%DB3I z-zj|R(6IX>4dxkb6=BmE`Fxka;`d;Rk;gOb_}u}TZr>ORGFSiz6&>~-nQd`D&9Q;~ zGoSfPkb5fdaf^GWigMgW(aGSp+%oUoS)6gw^5tBkby-%nF4{YSn(#g0vp3R|4Q;wS zfv0po43q^S!3PbF(=CC+2zFv1o^i}`-~8sZD47=pMoibirLGUWv@W8-kN(3MxZ#&z zgO3#|qUMLZl9f>+%Jnb(8F(2kI4cV`@~aD8DPQ$B4cclW5xQei?V^}G`jH1|JKuBD z?>?KkEdiLWM%i7>c1NzX_IM;-qFk>>q5M*(RbVV^F=WQ3V4+x-#I-zqA=WA{z5d8g z{M57{3mgvS++-GZE@Me`P-@^-mNm7;ciWB~p~Jbdat99VPrLH8N7dHI*s_&^x%UJ7OxTF5!o%| z1%Jv}gaL)X*+L8eAxY-JKq?Mi z3V6V{Ab|B#k9+`A*7w0A5G|j3%0R}QbplKh?HcvS1NiX_BHOiUi!M+x+Nt`IS45DI zIur~{-}=^bHLlpOaYHm^pF+VBfe~^=30YAuYF?NZWi45fx7n>;AIrHe?or;!W6TmF zv(8Qe2uS5VKmPb*5dynoFsaCGLhqv1VN~xU#RM$PQuC}~PJt|p%$2Hs1Xjw$I7vQZ zAy$|+hadfxxKQu`2g*sq(=Ze$?ci_nBQVQGsUW;yi1u&`23BR!EJ7d;CaQzfXXW9L zm1!Yvg`nPIw9t=I(S|&{hwBm44CSkB3p+$^z4D}9Pm;>@kL5LnEgGf{G<*E(-*(^#c5~W%x05!csPe^=hXpmqf8Zm<3@J5Xs6z(6JedpF_d$2 ziMD3KeLkAqRw~g!p?p@e-jkDdUU}ufw0rkGnY6z&oyei#c}eH=&uwz^g9XMFt0!w*5d!g~6 z?)&3dQ`UcJHb)Sub2-@Co8d|T0-LBX;q|)|C0+_n;F+N?HAgv!95=sOZ_cW>V14;3 ze_DdOc~cfgB)FwMaT@$RHnFG+P76=rX=h056TYorQs6B9hA=7KhrHz-2LG(Sq#oD& zCqMP+JqpM)2skphr{eLfh*@r4TUq&N4dpNuK;#+PKw0-Zcgd^c8Xj$U&a$$X0j#YNFM9CPNzIx)->BR99HC&*$EPxNk4pcX^kk|OsvyKTg zz71#K37yP!gO{?0H>rkOJcr=Hc~LyNR9g}K`|i89zVp=J6z>ZP!7R%4JHURax$7Od3ogFBd=~1aFKR{gwc$0r~Iz zjw$4oMtj95h@LiF14wzC6o(mXJb@^c-G7wUIup6-ygFb;)5kPm0|5sNDLgGk!M zOzQ9wArtW4sJ>=x-l1Y#y395{E02sV<6_dD>7U?*vItn`_r#iV$5us%5f9D;(gZ4K z<8!grDOxalCh%oEh7@YMrJ?!!X?ntRITVbdTv3eU+SzpL`*K4|>zx$Jgn ziLN#>RL!#}#N=tqoIsH&o6NAX;>N8D;;nFZkDR(N?SAyB>4`VJV|q37r|Yuf=x`R5 zT+Ayh3XiFe_hM~ujY+p}FZi^L_6H9hisi~%&!fD#vcm(R-~w@I*n45^cB**545wUM zK_WbYHVzV?(0u195zsVIwC1i-w(5khS~Q<~h1cLyy=Sx6)eEx33h)49!zfuGtdKzjTo(%y*H+6%h0$pJub?9@^%kCKu{ZP<@A(Vq5JafMaNYIgrl;nW^Yf-wXeSXJ&UB)K=Ns)_Vso^VdFUSLZjL3ST63c+9f z>Q~(=rbYlT)k1^RgP6t+=1JFY*c!rRZY|?cZZ4tXI&)Q25TBw12adqaI>VD`hxG|E zt;iHY)JMH0aQVu(XDMc9OudyXM6vL^0xS6%FF0==%dsLk#qhBwpR54aiNpJ|y2q*< zbI$@7QI^Uj)p#bb5+pc>Q}_i7ys;iMa@)DKbVInSg8TuqaWCZ)k{2E*Kz%Tkl$*K2 zEU6Fv;nk)8##D4g(QQ`YpyA*V#VzJy30cvyq@@wLdw41DK8?Tx<8jq*PRCDOobG+( zsp*aH_`r1N^!aH;BC;2v#EeN7#x1Q?Oj(d$2aBiib$Jiaid9)X7FbwD0($h=i3G3` z3(3WzTVNv6O8{5K0`n+%X{R%#0;d74aLAI1Gx#J7#e2a6#n>|dloEr*=(K`jnM?Td zNvLg3r=ZJANZaOuH)nNJ)fE^ik|8g64o-cd5uBqb91>=bWj5DH%DsMjNWNZ`qEA-2SQSo5bjg9mLLW{#+>K`AMjW% zUaDOFJFs5OqOS$&5kH(Tajc zTsSM3^T{fgFsD!2Nk_`NBh=jDT7;3h#6H$#jH+(5AgJocbKrtc>wn=ItR-Vw%Xe}| zcH~FvqDP)-4;?*o)$dRJ{C~M8&x)CRs+jf$Fs5}4qN_+l?H;#=*N9H4%mqMoNE(;8 zF&g9=`mT;jX$Qk-PaE>Nra9 z*(V7`OXWs>9>M0k@X2Wc;0=X}*+m?^J$u*vk7l3d{PfbdUy4f}yd}65Y9vqatgmnw z2E3xZ;71<`9gTiHJ4$XQ>}ttdz-#!z=#I*)08W^z0!?{HK>J;w=AK)D5xNvA+}HEu zDTZ=CmnoN0I7O!jY(6D{Df!GXvRL{^nNa}(c2;?rTfP=vFt2(kr@vjkGMC`dwdun@ z`P0++1nMpXw$o9jH!`+g6vbz4ZYjmof#&*d6F9u`a=5WaBBUq1#4BxU13tObHRQ;6 zR^%6N!M!>N3OLutYNH)`@giIVPQ<@?!D zPM^y{h%JRLF%dyN_-93Hm_Sj&PvC`DydF&TXW*jX;2Q6f0nfqXYNHmMT9mH>jr#P^ zrwf1v&%+|>Y+1gd<4n^8m|E)@K$KOFq_NV3X&g{P2#19>UUiRKYHpK2v}^I&aWE`% z2SZwrF(u>X};@+5GjZQ6b?;aCABFyT8G2k;;3LeP zFb)E1i-4F^?`=rAkv=*U&6&TpSy{BdbLY-NE@gmF+9&iIvsLQsnVi%ZCc%6GIj&&> zAlde{>l@F2;ILNcY2^`GaABRI|I*9*(vPM$ znw!(~hVsHoV4$$!$53Ic0dQoiI^e8>fxUMcoa)e5-%F|Iw|n{rf9kE^5v34WKu@3E z6Qn-v=_^6Q`y1l|Unm^(G@`n+UARtNXrNABQprkSE~S+F^1`?0&Et4J&u4~m{%3yn zKi@Md@Tr19WCQ>Z0ZREQ=$^t700zfp_YaK+&CV)D*8jzwsfGZ0)m(C)(dss*Q z!7Qw&`wm2gP~}HC_4_^UDwu+e6VrS!alrX@e%?st{D}RGq3GMk_?*6U>dc){yRxXOLJ7Jgia}6;p+M z3s(J6-sCqpx5jN7(PqC1u~P)Rox9wb;7?!piq}$Cocw6yEz@1u9R2clUaq4;mgl(* z1B<=2ER2DeFmW|FDp&%aWsUFwmxKr2FbKTerr!*O$`F3E(61q=>VO}C6W)TMhky+H zYj5-o152eUiY8&arwJFq3jq-p4X2t?B}ZK@LjiwW~vebtdv$<0JWNI!DqNJ!U8 zAVrf}Q*nn}v?D|pB7*b*UwW5+aWUmX=)h(yr+?gXS4U4Bs2!$A~9XjcIYmW%sn zW3$Zw4ojo?S0`8{)eCu}AOu$gJIW0&5OnEN_700XP>F|@d+hMsBgm)7$|md z;7@>{uYBgf2fv^-#jIZCDP!YPe8te<8~tM$;ivjq&j`z0&BYSb!dD4L&##scM!D(# z;7bv1bNLQiimX?2#cMc8RuV?Oz`)nl}F>Ff7Q`DpVtqCzm*R3Aok z*faU-3Jm%pz=9|T4cDfXps0&&EMXC z|9x@!4rLY9nFvLKWvd0!+UlEc^=b@LBE7q&mlZbCw#$p~6h|>YJ}H%E7<4m7oq$*F zl~N1i@@5<=22T_ki;U6qg|%qrwO9q}L$@%XX|tlEcEa#oM-FSxrtk9+)FEXGJoy06&EsJ>dYk8*qE-)CJtV-EgJuwm%cMS_UPl;wEaxRslDKSv_)BP zH}RxpivU8$K}!nOg}#zH(ahz$OJ3HGQbRies-Jk%C*k5HG8*#YF&2@4qn|Q9Sx!7g zX~8X;Qs~-(JN4yO>T!ZY*IyCt@E2Z!FFXw2igygi+U$-5qxWt8`?;V0#XS&@0JH~y z06RJbsG3&-hgNgl*&#s5Q{$i@j<9uI-AEBN9noMRD^>yFtDOK}!Ja%L?3hFTNKP?5 z7^?qi)qTX_#(<(y!PY*!-^VgO^`a+8;wB;(u&Xb$Xke@0LIef@^ux3E2q=Qtc%cZ| z?*2ejxab_ZqVc`kwBD17^20j zp)lFa5OVvjU0Dclq!xcHiv?Jmd)(>rKH@&Ej%C8YP?la6p}A1naLfoiQ4j>O-7_9f zEORWhF|GcF1ci6?qMf~-Xz49y+8Kh1UJ_iDMGcP8&ipTe3;1~P?)h_9rxWL{O;5h_ zgVUCW9;=PtOR|t85alFCF0uZ{Iwd$Hh!_92`X|tXUdNB0$e!PA<@$c^^Pj&hL}CLj zbfX-6y3psghZGuPa?B5pL#Xhb{=>gEny+Fwf>XT8suDWF!{VWfy5+@}cut#LkM5e* z5%FFUS9eX(oA=2ZrLZUxiz#|0g2L;lI9TCRDn1Ww49iMpW?Vhw&1cJF=P!$@U_a=J zRH9P%Xq9rMQmOmxX7qT-bC*u(yyxvc^!TP2ai*_=l zfcg9iD3s>M#hW56arM#2C zLCKiNUl4rFhY@z&cVAquGg*Lstl;40qetN_c!U=)zymm0*c#{OKAOOvJ{H{3djd|z z#d(zPQP5e4VBaXF&4b`Eo=(2;LVT(p`~qT_*mt=0$wQ!AFXxQ`mRz2V>wfjtlId+9 z{OGj#fk&qdPb;!b30$_pJi_|_)^%sUeqGfaz>jl1I|MS?aY!&z2qB<^0G&`(=tEHl zRe5M>ffhocBS=+w=tE!p=aj#o5|r>z6$uceZAinMIdMG1j_ugjbHAUpe(T;}D|KHV zpL6zJdrf<-J)iyCm%X~iiFm5h?Hyl}L*~#smG166snm3PzxBiuCl@B zL?E9JCdk3o!20&IY%ux>y#CXgMx{k!x_@91dx z*g!w`uS0+p5{=y|2k*M{DuN#S2VcqutZsgt_7Fd0Z(3qD(3MN`sWwLkpBsPmH~)Ba zh=_101aY1f1j>{^;o=ztW(iS+7-LX)t(b0!z?dvyj9?J#TB&DkhYs9jq=AF87R*DD zV$tGs6at4l7N=_sF}x_S5-joPm>J|CrK$YynL;>}?ru;O&+sq|`57=d6j(c9x3J}y zkr@wY;87vy67_^uW&=~Ww!8@npw=$)T+SL}5Y*()XSqmz$ABEl2@y727=P5}0bLtAtbtcA zAi7t9@S~x{cEpvve(_7aJcGzM6yXGpRke5C+t9SQ3cZEtIIT?AFpQf52%1~rgVQy^ zI+*~j?ol?yVF+$rxpCIwM_~lEnP+$zbA&zx$;ZJ6?>AZ1h*QhS!=T(L0tb|d_g<4! zBWD*LcpVvP7fe;C%F|Tjj(7oA-C*a>@iXzLH-eQ(4@!tBtgKJ1PoYT`jUDiX6Ze zy=Va$^i*`wVpSqq+=vrdLO*dTG~GpXr>y8#`Mcg4XF8eQf${e(X>IT(Z~Z!WeyH^H z8yA){=dUe?-}}(=&WAs?9KVnjGcC1=A^Wb4CHKW3m4$BMfo!$Bc;(`9OV*BU9TQ?uSGV@|1pRIaUuE^B3{49a1=By`#J_!J*? zrI@vL98l;UgW*9tNZWIlHFz}WhcnTOxCt65dN;9WOiE4_IuNjIiG9~S_{pSVF}n_r z2C43`0hQ|TJNP&L?yLWFl+fLBwm>p)97+)|C!>O_oi|~WgD?QhwAZ&Yfs@~T^13r@ z@&59d5D9XJQ+|el4&^E&trGlp#uPMR@<4fLw=vr}lAn3ZA=+kIb(scM;Z5yi+@)r# zP1wP$<;D+M3PorchWF+lztxQlOm!bQaMnBm84Exwfn$5f#xKhG){j!HJ=HMpF`Ic2w4tpJ8yc^a~W#{xvCWjKPE=n$h(UHhVtm5mD&YLv2a)6qbdVTHHWA%GA*W#eIEAGq0(edmpe%HI- zS<4{LJo9wY)7?vju$ft8m6GmnEpNs8>Dc58o@d4se_c8G?lG-p^jQ^8U`JL)XTK^v z7-BGLhGcxt5=@qz%J-jV^J8IGT-EymNWzH$JhP&abOq%?xk$I+j5&-2K_O zaXOP~cV#n&-ir_I{eF+1S@a=j2Lh})gqzUB#NY^EpIIG1$g#0CU9LKDu(?^!uuDTH*Tx_c|8oT~&SYylNvxO55KO97N#6+=)34*eul z0Tt#_Qg?mT%wUSojO!TV`Rs`CB}MInUA6Y`RqeK_<%&0W@wigT{x}gO7mdh?rlUNI zx5VqhFgTZPO-)qcdzHtHBjRVP;8W?5%epIglSR`DpyS6Me>?_zW%>Ho|2fA{zPmO@ z)Pz|0-w|D2O6%$XHoY7@n7!#Xp3fNXQjGkK&HT*6efOl2*txv?{7cJjv%6cmOUuvr zLcb6Gj$!7H3@S7MzskfISvQ$ccWc@$Du4#P$>Vf;ODui??Tt4x9=rMW zc_I9N|9juB%EfQZ za?+WgQ>JEV*`{HFd)sAtDv_bO!EuC|;g7uJW-fAeKD0y8Iz*OrOTTtFr=FuUW;rwi^X4oS?*y;)gpODx5r-5r6$^mZlicTW% zA|JehyL1)6P6bm5nR#dd)dD^Hw_`^){{R`<)KuQV3Y0fBN!?R82#;YD(CR0r@Qqo)K)+0Ar!)4%BAYgM&~ z^49O9tO^7_U7Kx>Ny9)Sn1W>l-8w5Ai}mO#eBB-!!}sko?umm07lZSmfIJq$AA9Vv z<%w_Sq=)C9t1jf_ENS?GUdFE?u`%B-VehQ&Y`RZrQFW1>W9xTvE?>L2ymI`^a`?!D z%T3pIEHC}|KbLzC-WFYCvM-CUoEjf>0CxD(%4*ej<)ee@hUa`E8{YRtKXgvVbgh!J z(Ou7B*RCA4mU!#Ed(*1h#$o2n3Oh}XUJa~1Et|GimXO?Nph@;ATntvK$noVej|vHU=EzM^4E<{wU+IGNr; ze>+;O>dJVfTh1^atrF&}8=N~%c_Wu&S@=0ZRD3$_7j}+!7kuJ z6RZG6nTz*~?zNNwd%2vG{~dT{TV>@54i$UlOB@XHz=faQJ{Tt)+*zh#V=9sw?&RpJ zTs!~(Ad^W%K~xPKTJmUjtFVUnI1*-c9lc{{n0Bx3RPMs0VPZWnU<5`;PBxA{ao{P( z`K<7&G#G@582GA&3iHy8c<`i4q^6za2n*+MTeC7o_3LSAGpgGp-lu0Az~^PV;=t`x z6xNemmx7ye$+0fO@D831s?zB4%DWme3XjOzEE@={P43RDE+gP545@l3^q4N2@#p>b zKe+rb8}GGDz6?2aGW(L#gv639p(|(KE`sR$=6D6lyJtWK5|7bQxPZA@GrOchBv}*CXe15@U3-M}->MuVk$> z?jPFwQdXOB#69^2_m1VMXa0Nn#m}EjrC_tc)jAlRUia^7Cov47>q7?*hKGD;krN%1 zx2|}XPn177gK3!sVwb*w;ac?*$*3-z_+6I6Q;X+j4xYATPM;jpbc>#Vg$Ch#csFnJ~{_dwDc1?`Qw)D@WG_;^_il zLJ`EXi&DK4R4wSfF3`ZqPk2mHp!k8S!bm!2$6yJTJ4fPx0j-)|UV+fYQs5*h9DJ#1 z)oz~U6K~d+Q_=-`H9k$tVJTyjQO;l8LtSq($WdrAY|#roumbteop*&xmMxvcFfoOe zbxmpWJE&^amalbbseyy~OiLw$cXuB=`(<;5W}<`Ba}$5wpgNsv5nJ zZcRDvY6(IAGJ#$hS!{55Gqg{g%hto3DfQqZA6xd`ad3xC-)EJ`q~ay^Svxyeg}n-5y(YXRBpjc@~Opcz_>YVA#Ug-q@}(G>%u!1M3;Q zAP;#2=`KRduxV(@qcSA7I5a1ZIPV*u`}40J9fQOGWfLxa zx`$v1lp2$Rn#35UF`)Jt1IEbI7SaSvB}4A|<`mVAQep?BY5Rmomu5aBm(QV68N<~W zyK7o>$fI>5l!LA~r7oKZB!+2go|?Ojpl5*@)hHcLlsE-6iWFbQ6@0!c#Sbg!!f09O z0e#&*vS`5_s#d#l;&coPU0WaFzvte2YrLiVcr~>xW9L}-^W~6&q)Xpo@*&@bYF#ep zK(>75xx6($O?cOl_oQ*&Q?1sG$gwlS89Y(%4(CAS=o#H?>%C{rW`;a?cE`zry^b@* z>vc4eAwBL?Ea@qPzA>)W>P*I@%Yi$W_y0<^2Hto7vY7+3cc+WX(eb%I6JD2NJA31_ zr%#_r*Y=eh0{3EVL~+h92kjnD#E;I{mswL4lVu;z;y7a2Vzg?BhYgyvF$6H&p)a2R z)Y+*gj4$0qX_qQZr&3AM9{ z6BzP@GU^m4#p!ZT6h;vkcsK^XeBKFJj+=LI1Phv61Swx#@zSl8eBjX$;KwTmB^ZZR z!*YBuBxQ8^u=p80Lll93$D47TN%)y zI5r5@yXUsa?gn!jwxoRIVFN0Ups3JsofmI-tg;DgkOrRn_xyx+lU?k4?263npU&yK zY%=L^jJ*w?WSG`|=LkSi!U#YiGD;_$i3sm@nJAn(yc(e}Rsv%tLUJ~6gU>TX2siRv z_b>%70qP!s)|DrZ%VZWjX3wfn1IHN$_b5mq%HL~iP0RS2pzuo@I@22%Wr&Aw9mX@9 zqYHWP1C}lnq^t6*3VUFqjBWt}mny9CjXcIO7Wd6K!-EILrbT)-KR$IVUDw0;p3zSy z)Xxv5JFJyc__pX-qqV)z#_n2r%Ght&ky+YI`s@pi&5U<9v-o<)k@qfd&2q?p{M!@D zi_iXKdB^Qp_OSWgQK0GeYPHXv&F&zr>#j@^-be+<$l@TI+4r|A&ZC7o_S%UorDW#z z!u93$d)~kN+9yA?WGbmz+?0B?!sqgf0mt&YJSu9H0KUjeK6g6OTCpF`WL5!~5qB<< zUV$D0cyR0voU#+d3(9E9ldi$|O&ez*pJeY|anqc9WFzI*^=n77}n)IP$Fy6&8gQ`=5+k}$V zad^XzV^YFO!K?6u*{e+VCr=y?d~cAj;oI-HngO*2skK(M)2X1c)NRWqrR6GT4L_GM z8RT=b-HhOJoapH%m!JROJIldavLAR)@&ndF+>{BcIO=lU*BTk;Iqg2JlcP~=z987^ z`H`mUd)~IZ^Zg%L?tR}Q%k_MUcQvYcKD(nXWW0JV6@#kAr9ALYKgvmuVDv&aWUjGw zV7$`}eQ?y&@HL+FJ_gFklr?}46`c1JLGeYO@(apb`ASy- zqNztF&)Ai8@odJOZ1S)323O6LhqfTirTc7&N#1&2vn95HS2OC;HB@D5;R$|m-rd*z zv1Iv|Z~QDX+v6zBFaZFImr*8)W30n80VqZ~M8ez&EnPmwi#d#VpT7GrX1t_@8O8=r z?62riEYqJLpE&%DV58`v2^U`6g~=nIOPahCiMQdGEYjs8Cwu~!p$8W@`p|nwhDzI_ z5qQSa`50L%9WIxp5sqed8%`%L&?}mC1;%kQujLeY0gUp{!r3%%@X?^#{(Tv*TuI6L;d28@g&5 zcpNmhrTgTiv$l=4PxP!CD^KzAqJyT%C^kN2gZAWyyZ2l7@{K%InI~RX8Q!r8uyC+_ z!SKsFM}@B@`U1mOz>1rzCrDHNfBfmcK8gW|-ScmC?-^g+6i5ky=^=_Sfnq$nzB&6#^Q9wC1A=3d5A%`M2>@m7jS03ZoeL*DtsxcpiSl!P$n@wWUHL z4_>;kJLeSQ=tw%gws$-A%%F6IfsW^qB^uqLYdlsC8yLEsG0`|4{={o>;OSN<?wYJfme_En^R{ z(2w)RIL~Ca&r2`8oUd|TT#ld0k6)P~P8Y_@m`?9R9V;s9#Li zm~*G(jH<(vvP`8CXXvkmv?9@MBr=pK$0JY1arh&HODj~)8{L7W(=kXoP`-3CwnA6p z=w)nF9`P!MXt&agjm6DMfjb+N54@nYM90Y|V3}B=~kN?b!Vumu@ zA#outkGnuH!g$B%dc0>X6I!FFHqPx<6b!szCg?g+2;72a`MXfI6(VmbZl!E|P8`^I zM-zS*e$a!@GZ+E^<%uzyVlt`wz_;a{QFH7yITBghQQ%%KG5nqIb#3n{Nh($&hNIK zIh7q*Da$y{)$ESCo)%TBY6a#-b{hS9zA3$)pZYkK?(myxb~_rv=EXPrbc$U&b3k0y zJvXz{$oLt5^vLkY%t_b_o~InfnZVIIosBcmo$@2MFl9zg&l3k;{)&%nU`mg`I~{{f za_M)lcYKn`a*>ChK4+J z#kS#v-!-_#MoQi^cf=mRc1a%6g=Pq^~iGyVIhV_<+?O?VJ?YXyNoaOn(2J_R6690sH#fQi97 z@nEEjm#&Lj1-8nELAWqEc^l96I_kPW_*oYk?lSbyk>5Lfa15+xaYKtSc?LT$?%?2( z&MC;mNQXXnM%w&!Va+;i>udkn)dmwwvbyS!Q^ z`D#d#*3=feR0lD-bOmELy2gH*!G;dM$6Bz_Mb-W>Jcow)Rqdz#>qpryoQ(+iUD)%P zseK`Ph(Gy5V4cSTHEFc3UU7&z~QhtGX* zQ{d2$ujT1V2mXPR2G?Bj%r&$Wm;xFYce0gSajaUj4oX*{>yb>eaX96Xy=gzc7H>aDdl{j_g^>ov+ z91|Qj0(UVj@7r=LsfnRyfAX{1%zrkYs6FuLuPz_`?cZ5`{Nl^{j=`B_FcnCg*?82C zYT3VQ-XMGYlP{m{mC<)Oj2y!=haJb0ri@%6Xh2*U*;-EWA_ccvjvGw{0_3#>%;iy3)~rLmqOv z3-a1hDs61eedO?rE*UG|pM2(Tj}9;j7&PFvIN#l}Hkd)aj$1QErrvjra{EQs( zdH1OwUMG)uG!(r1z`KJ>eN$oN?f4#7jZDT{ZG^4I`UbNyb|K=4Txw+%nEkODC7TAW z=2XKo`S^N&oF|QUHsi<9@~mrjJU_r|E1mtdPUz18_GzVWTh3)%`_zw~Sq{JFf#uVW ze|dR6@0-cHsZ4{P!~0o^otc#4q<%sJ-y8XD+B)Pcu)59o$2U1w#anlds^X<1Iy0UI z>zQ6vK!q>mFgkN5Cq21F$K>`79zHN|aXz#sZpy<%89HOfVBvBf*q#XM#zvep4x)k{ ze((QV|PpBe}P6WITJiL4a?<(5KJFZ13H0+!#Num{MFEK@`AIMZt|YZA3key^e0F4(}b2#Y^k(1d;G+y$e4{H znYFbs!KYb2f8nJ#>0d5a6Mrs0%)DXQAsZidI`b%wX@bf|e~tfU&g?aQjCp2xDzmhS zGpkD`VPtW)lggb=$8J31+fafowLW0j+2o&c@IK`+cqh;-x6Sx22bV;g3B@dgK2`8=-& z@!*AlE7-sI($|j87zso2Nf6v!oDhLhY~l%`HSF<)dc9#027>@0hp8b5Pbn;hV2WPT zZ=5B_Hw+HV;wRX>H*fu1)}B0^7|kBvwF1%Vxb4~uA8^9~m*80dKX=bAurANKL<&uT z3%d7sm0xWPW?kXp<&EY_2i^xSIC77U#CsMz;}d%Ymwde@+xbnT)FE4e1H*xBnY1}y z_t^*MT2i!aYx5nc8DTnhGN0D(&O7f4otu_dbE4s!SueG>_{D6T|KOt^UGD$T!^=w< zm&PyCLyDotR`z76Cg?(pj+M`(UgNcvy3C0J3ONVvzoQ`7+MtKYX@m?tP7^aaN)x>%;Pbj1&F%IW$dMYq>4Zol&4 zOUpfn4=tDWT&|P;4&-}Qnh(R zIissg*6iIA!wqkju4Gvw%Q_orHGRW6`Xd*eRCz}>!y@15ZgSQ<*N4@qoUTOQ-3qlH zPdEjt|15UQgHs$eN zcA7Z(<&h5FGdg+)V2Xzo&*bPb+O`e2CX7t(y2K+J9AukIdf|l-5W>}R2AKp+cqql` zdZyE^7nvY%C!CLCDf@gFHrC*v^1?@mVHR`ZZ50%U4&^zcmqDUU&wna%&cSGB?aT%6Fap(!ihzv>2Sjql0_+WT(Fu{P=9Y+g8@7CTE^DvZ#pT}IKf zGn@_to*ht6EFgTvMzW6TWBD6Sj0nwhI0E1E6N$;-@x}f2#-7SeXvq%wy z949Wtgg-Dt1AdbQ_=0cl;%A})-lDxyz!W-Oq>*<^2a!p7$r+qz3VIUa!0Hj`ZqrMB z=C$HeYX=WnU_H~3_o1Oj;aR!~yWZWD0?JoOX!Ul!PPcZf+X&XtqU3{*0l*j*Z6n#-ii~(@J9c57(MyArseNV5M`*rsUm89HVtx& zJ<^9Wylm`1Lp)yT6;C!8;7hzaTJXBpuiHhIysDXtH~8okjNlzl=nM2EPO^KIPxzM3 zdBq70(~Vm7l2Qg$K_%ntrb2-oS>BqSC>{m;!NCVrI`p=+<}u6x)D$HPg% zD7TdDZE&s21jiT<81ECe9*?H@nmkLP+2F7d~Oh82ME;88-$`4@AWam=t_KAxIR`UJcds=U(SJIW`>Oq{TOh!1{-w_ z@mfECNJ4(w8&Xp32#Brpwa01<2&Jt6#n)$@1 zoHg-{r=4@`0I$V&RIX<51TX%TWOhgBI~8FqV0jUP=qjL9UjzgJcK|9>ULa zr*$hl1?mppE+@pth`^N5rvOTEn9rX$N(HMBMiB6DFc{ZV7K0zWOBh&p7yRJd$+sTX z^GrT8!4>Wm4o0M`#M22}6c4s*^&S?$CrzMu@9?-6&*_mEHx1w5JP%DvBU(9E64JsX zEd|X`;K1XAkxhpsRHC=t^~1{c_ui5X4)8j<)2DNOZ4SS5I`_sk-N0T-@eu%d>L>35 z-#^lo3M53QT;vC`K-oa1VfTvgZ#BqelJ1}%GlR1-yHhJn? z<>)6Fi!@!~Wprl#LxUp>FK9`34PE6hFhw`pEWwKhGp#jx@C$}B(s$&GB(Q9ydQy5q zxvK0~cgTYqTtgS$xzKd&93}y2g1rM8hAZ>LVIs^_ZJIbrDMr>>1B^Cv%_R7u5vCAq z{GA3?x4DE0zJ_fkt2;;7#Jh$cyv;k?%}b?u4rHYj-jxD8H_bTK3YYhZhgWbPMUR1q zM-P9}OQ{9l&IJ!0lujatLL1!;&cP4X<$dD76ekuzJ^x>rz>1r+G$1w97+z$aEsWsM zp3A9(48%cSx97x%*Ry$_Q!1+Jh6PsF%CpvoBf?>6M~{aiJB5%gjA?zXqKoMu^Xf@8 zQ(L_TJPi*VW$)fDPs>}QmodnJb4RNTF?rw^{eeZ>@h#$}7d9~Ry5to%II4`+%1gyI zq3NwAYF6%|S28r+@jiSu-R&OYtS+FX%q}Dqryt zy1Ucr(*@-QOFf5THoN!C;mCmxw8%WIl{EOJt@CRfUZq#G@E=)n75(IaUzq%sJJxyA zBEa|m1vELAC8pCkqO)|Df;UTf_o0Ke9rAL%pxMmF?C?5n)1Ka2vo5Qbqhh!v+ZRpB zpyL2Krz!ZMDr3q}FDH~XJi5arh@Z0czODmlbfmj2Uh6O5Q40=_XSm#(Mpx?61m&+1 zkRPt0?LIB4e1i1B1D+R^qa|Kod(#@r55|3XnD@$CIl5e8mEEIDSs5Czk)bJX<*T7# wcuZP|R&Hyp{nTSskznWYPIvXI0^#w000-9r6eby5E&u=k07*qoM6N<$f^;Z#od5s; literal 0 HcmV?d00001 From fab86caa2f1b900823d14f9b002eb88edf9d2f01 Mon Sep 17 00:00:00 2001 From: Abirdcfly Date: Tue, 30 Aug 2022 16:34:23 +0800 Subject: [PATCH 279/366] chore: remove duplicate word in comments Signed-off-by: Abirdcfly --- pkg/install/install.go | 2 +- pkg/restore/prioritize_group_version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/install/install.go b/pkg/install/install.go index ebe4c3751e..e163be95ef 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -208,7 +208,7 @@ func DeploymentIsReady(factory client.DynamicFactory, namespace string) (bool, e } // DaemonSetIsReady will poll the kubernetes API server to ensure the restic daemonset is ready, i.e. that -// pods are scheduled and available on all of the the desired nodes. +// pods are scheduled and available on all of the desired nodes. func DaemonSetIsReady(factory client.DynamicFactory, namespace string) (bool, error) { gvk := schema.FromAPIVersionAndKind(appsv1.SchemeGroupVersion.String(), "DaemonSet") apiResource := metav1.APIResource{ diff --git a/pkg/restore/prioritize_group_version.go b/pkg/restore/prioritize_group_version.go index 1fde8b6058..d4ad9331c5 100644 --- a/pkg/restore/prioritize_group_version.go +++ b/pkg/restore/prioritize_group_version.go @@ -339,7 +339,7 @@ func findSupportedUserVersion(userGVs, targetGVs, sourceGVs []metav1.GroupVersio return "" } -// versionsContain will check if a version can be found in a a slice of versions. +// versionsContain will check if a version can be found in a slice of versions. func versionsContain(list []metav1.GroupVersionForDiscovery, version string) bool { for _, v := range list { if v.Version == version { From 6fea973c5790cb6947516a40f324b38ac3d209b9 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Tue, 30 Aug 2022 16:55:39 +0800 Subject: [PATCH 280/366] Migrate backup sync controller from code-generator to kubebuilder (#5218) * Migrate backup sync controller from code-generator to kubebuilder 1. use kubebuilder's reconcile logic to replace controller's old logic. 2. use ginkgo and gomega to replace testing. 3. modify BSL reconciler registration method. Signed-off-by: Xun Jiang * Add Option for PeriodicalEnqueueSource. Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang Co-authored-by: Xun Jiang --- changelogs/unreleased/5218-jxun | 1 + pkg/cmd/server/server.go | 75 +- pkg/controller/backup_deletion_controller.go | 2 +- .../backup_storage_location_controller.go | 69 +- ...backup_storage_location_controller_test.go | 32 +- pkg/controller/backup_sync_controller.go | 534 ++++++----- pkg/controller/backup_sync_controller_test.go | 904 ++++++++---------- pkg/controller/gc_controller.go | 2 +- .../restic_repository_controller.go | 2 +- pkg/controller/schedule_controller.go | 2 +- pkg/util/kube/periodical_enqueue_source.go | 35 +- .../kube/periodical_enqueue_source_test.go | 82 +- 12 files changed, 890 insertions(+), 850 deletions(-) create mode 100644 changelogs/unreleased/5218-jxun diff --git a/changelogs/unreleased/5218-jxun b/changelogs/unreleased/5218-jxun new file mode 100644 index 0000000000..d2274476e0 --- /dev/null +++ b/changelogs/unreleased/5218-jxun @@ -0,0 +1 @@ +Migrate backup sync controller from code-generator to kubebuilder. \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3a85b6492f..5d739e3cb7 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -53,6 +53,7 @@ import ( snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" + "github.com/vmware-tanzu/velero/internal/storage" "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" @@ -79,7 +80,6 @@ import ( ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" - "github.com/vmware-tanzu/velero/internal/storage" "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" @@ -607,29 +607,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string csiVSLister, csiVSCLister, csiVSClassLister := s.getCSISnapshotListers() - backupSyncControllerRunInfo := func() controllerRunInfo { - backupSyncContoller := controller.NewBackupSyncController( - s.veleroClient.VeleroV1(), - s.mgr.GetClient(), - s.veleroClient.VeleroV1(), - s.sharedInformerFactory.Velero().V1().Backups().Lister(), - csiVSLister, - s.config.backupSyncPeriod, - s.namespace, - s.csiSnapshotClient, - s.kubeClient, - s.config.defaultBackupLocation, - newPluginManager, - backupStoreGetter, - s.logger, - ) - - return controllerRunInfo{ - controller: backupSyncContoller, - numWorkers: defaultControllerWorkers, - } - } - backupTracker := controller.NewBackupTracker() backupControllerRunInfo := func() controllerRunInfo { @@ -717,10 +694,13 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } + // By far, PodVolumeBackup, PodVolumeRestore, BackupStorageLocation controllers + // are not included in --disable-controllers list. + // This is because of PVB and PVR are used by Restic DaemonSet, + // and BSL controller is mandatory for Velero to work. enabledControllers := map[string]func() controllerRunInfo{ - controller.BackupSync: backupSyncControllerRunInfo, - controller.Backup: backupControllerRunInfo, - controller.Restore: restoreControllerRunInfo, + controller.Backup: backupControllerRunInfo, + controller.Restore: restoreControllerRunInfo, } // Note: all runtime type controllers that can be disabled are grouped separately, below: enabledRuntimeControllers := map[string]struct{}{ @@ -729,6 +709,8 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string controller.Schedule: {}, controller.ResticRepo: {}, controller.BackupDeletion: {}, + controller.GarbageCollection: {}, + controller.BackupSync: {}, } if s.config.restoreOnly { @@ -742,7 +724,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } // Remove disabled controllers so they are not initialized. If a match is not found we want - // to hault the system so the user knows this operation was not possible. + // to halt the system so the user knows this operation was not possible. if err := removeControllers(s.config.disabledControllers, enabledControllers, enabledRuntimeControllers, s.logger); err != nil { log.Fatal(err, "unable to disable a controller") } @@ -776,18 +758,18 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.WithField("informer", informer).Info("Informer cache synced") } - bslr := controller.BackupStorageLocationReconciler{ - Ctx: s.ctx, - Client: s.mgr.GetClient(), - Scheme: s.mgr.GetScheme(), - DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ + bslr := controller.NewBackupStorageLocationReconciler( + s.ctx, + s.mgr.GetClient(), + s.mgr.GetScheme(), + storage.DefaultBackupLocationInfo{ StorageLocation: s.config.defaultBackupLocation, ServerValidationFrequency: s.config.storeValidationFrequency, }, - NewPluginManager: newPluginManager, - BackupStoreGetter: backupStoreGetter, - Log: s.logger, - } + newPluginManager, + backupStoreGetter, + s.logger, + ) if err := bslr.SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupStorageLocation) } @@ -844,6 +826,25 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } + if _, ok := enabledRuntimeControllers[controller.BackupSync]; ok { + syncPeriod := s.config.backupSyncPeriod + if syncPeriod <= 0 { + syncPeriod = time.Minute + } + + backupSyncReconciler := controller.NewBackupSyncReconciler( + s.mgr.GetClient(), + s.namespace, + syncPeriod, + newPluginManager, + backupStoreGetter, + s.logger, + ) + if err := backupSyncReconciler.SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, " unable to create controller ", "controller ", controller.BackupSync) + } + } + if _, ok := enabledRuntimeControllers[controller.GarbageCollection]; ok { r := controller.NewGCReconciler(s.logger, s.mgr.GetClient(), s.config.garbageCollectionFrequency) if err := r.SetupWithManager(s.mgr); err != nil { diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index a2193cd927..db26290955 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -90,7 +90,7 @@ func NewBackupDeletionReconciler( func (r *backupDeletionReconciler) SetupWithManager(mgr ctrl.Manager) error { // Make sure the expired requests can be deleted eventually - s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.DeleteBackupRequestList{}, time.Hour) + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.DeleteBackupRequestList{}, time.Hour, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.DeleteBackupRequest{}). Watches(s, nil). diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index 1b08da897e..3791e01e68 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -44,35 +44,55 @@ const ( ) // BackupStorageLocationReconciler reconciles a BackupStorageLocation object -type BackupStorageLocationReconciler struct { - Ctx context.Context - Client client.Client - Scheme *runtime.Scheme - DefaultBackupLocationInfo storage.DefaultBackupLocationInfo +type backupStorageLocationReconciler struct { + ctx context.Context + client client.Client + scheme *runtime.Scheme + defaultBackupLocationInfo storage.DefaultBackupLocationInfo // use variables to refer to these functions so they can be // replaced with fakes for testing. - NewPluginManager func(logrus.FieldLogger) clientmgmt.Manager - BackupStoreGetter persistence.ObjectBackupStoreGetter + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager + backupStoreGetter persistence.ObjectBackupStoreGetter - Log logrus.FieldLogger + log logrus.FieldLogger +} + +// NewBackupStorageLocationReconciler initialize and return a backupStorageLocationReconciler struct +func NewBackupStorageLocationReconciler( + ctx context.Context, + client client.Client, + scheme *runtime.Scheme, + defaultBackupLocationInfo storage.DefaultBackupLocationInfo, + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, + backupStoreGetter persistence.ObjectBackupStoreGetter, + log logrus.FieldLogger) *backupStorageLocationReconciler { + return &backupStorageLocationReconciler{ + ctx: ctx, + client: client, + scheme: scheme, + defaultBackupLocationInfo: defaultBackupLocationInfo, + newPluginManager: newPluginManager, + backupStoreGetter: backupStoreGetter, + log: log, + } } // +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations/status,verbs=get;update;patch -func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *backupStorageLocationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var unavailableErrors []string var location velerov1api.BackupStorageLocation - log := r.Log.WithField("controller", BackupStorageLocation).WithField(BackupStorageLocation, req.NamespacedName.String()) + log := r.log.WithField("controller", BackupStorageLocation).WithField(BackupStorageLocation, req.NamespacedName.String()) log.Debug("Validating availability of BackupStorageLocation") - locationList, err := storage.ListBackupStorageLocations(r.Ctx, r.Client, req.Namespace) + locationList, err := storage.ListBackupStorageLocations(r.ctx, r.client, req.Namespace) if err != nil { log.WithError(err).Error("No BackupStorageLocations found, at least one is required") return ctrl.Result{}, nil } - pluginManager := r.NewPluginManager(log) + pluginManager := r.newPluginManager(log) defer pluginManager.CleanupClients() var defaultFound bool @@ -93,7 +113,7 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr isDefault := location.Spec.Default // TODO(2.0) remove this check since the server default will be deprecated - if !defaultFound && location.Name == r.DefaultBackupLocationInfo.StorageLocation { + if !defaultFound && location.Name == r.defaultBackupLocationInfo.StorageLocation { // For backward-compatible, to configure the backup storage location as the default if // none of the BSLs be marked as the default and the BSL name matches against the // "velero server --default-backup-storage-location". @@ -117,12 +137,12 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr location.Status.Phase = velerov1api.BackupStorageLocationPhaseAvailable location.Status.Message = "" } - if err := r.Client.Patch(r.Ctx, &location, client.MergeFrom(original)); err != nil { + if err := r.client.Patch(r.ctx, &location, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Error updating BackupStorageLocation phase") } }() - backupStore, err := r.BackupStoreGetter.Get(&location, pluginManager, log) + backupStore, err := r.backupStoreGetter.Get(&location, pluginManager, log) if err != nil { log.WithError(err).Error("Error getting a backup store") return @@ -144,11 +164,11 @@ func (r *BackupStorageLocationReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, nil } -func (r *BackupStorageLocationReconciler) logReconciledPhase(defaultFound bool, locationList velerov1api.BackupStorageLocationList, errs []string) { +func (r *backupStorageLocationReconciler) logReconciledPhase(defaultFound bool, locationList velerov1api.BackupStorageLocationList, errs []string) { var availableBSLs []*velerov1api.BackupStorageLocation var unAvailableBSLs []*velerov1api.BackupStorageLocation var unknownBSLs []*velerov1api.BackupStorageLocation - log := r.Log.WithField("controller", BackupStorageLocation) + log := r.log.WithField("controller", BackupStorageLocation) for i, location := range locationList.Items { phase := location.Status.Phase @@ -181,16 +201,19 @@ func (r *BackupStorageLocationReconciler) logReconciledPhase(defaultFound bool, } } -func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *backupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) error { g := kube.NewPeriodicalEnqueueSource( - r.Log, + r.log, mgr.GetClient(), &velerov1api.BackupStorageLocationList{}, bslValidationEnqueuePeriod, - // Add filter function to enqueue BSL per ValidationFrequency setting. - func(object client.Object) bool { - location := object.(*velerov1api.BackupStorageLocation) - return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.DefaultBackupLocationInfo.ServerValidationFrequency, r.Log.WithField("controller", BackupStorageLocation)) + kube.PeriodicalEnqueueSourceOption{ + FilterFuncs: []func(object client.Object) bool{ + func(object client.Object) bool { + location := object.(*velerov1api.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.defaultBackupLocationInfo.ServerValidationFrequency, r.log.WithField("controller", BackupStorageLocation)) + }, + }, }, ) return ctrl.NewControllerManagedBy(mgr). diff --git a/pkg/controller/backup_storage_location_controller_test.go b/pkg/controller/backup_storage_location_controller_test.go index 75ad691a25..06d3458c4d 100644 --- a/pkg/controller/backup_storage_location_controller_test.go +++ b/pkg/controller/backup_storage_location_controller_test.go @@ -79,16 +79,16 @@ var _ = Describe("Backup Storage Location Reconciler", func() { // Setup reconciler Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - r := BackupStorageLocationReconciler{ - Ctx: ctx, - Client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), - DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ + r := backupStorageLocationReconciler{ + ctx: ctx, + client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), + defaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ StorageLocation: "location-1", ServerValidationFrequency: 0, }, - NewPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - BackupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), - Log: velerotest.NewLogger(), + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), + log: velerotest.NewLogger(), } // Assertions @@ -101,7 +101,7 @@ var _ = Describe("Backup Storage Location Reconciler", func() { key := client.ObjectKey{Name: location.Name, Namespace: location.Namespace} instance := &velerov1api.BackupStorageLocation{} - err = r.Client.Get(ctx, key, instance) + err = r.client.Get(ctx, key, instance) Expect(err).To(BeNil()) Expect(instance.Spec.Default).To(BeIdenticalTo(tests[i].expectedIsDefault)) Expect(instance.Status.Phase).To(BeIdenticalTo(tests[i].expectedPhase)) @@ -144,16 +144,16 @@ var _ = Describe("Backup Storage Location Reconciler", func() { // Setup reconciler Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - r := BackupStorageLocationReconciler{ - Ctx: ctx, - Client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), - DefaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ + r := backupStorageLocationReconciler{ + ctx: ctx, + client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(locations).Build(), + defaultBackupLocationInfo: storage.DefaultBackupLocationInfo{ StorageLocation: "default", ServerValidationFrequency: 0, }, - NewPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - BackupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), - Log: velerotest.NewLogger(), + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), + log: velerotest.NewLogger(), } // Assertions @@ -166,7 +166,7 @@ var _ = Describe("Backup Storage Location Reconciler", func() { key := client.ObjectKey{Name: location.Name, Namespace: location.Namespace} instance := &velerov1api.BackupStorageLocation{} - err = r.Client.Get(ctx, key, instance) + err = r.client.Get(ctx, key, instance) Expect(err).To(BeNil()) Expect(instance.Spec.Default).To(BeIdenticalTo(tests[i].expectedIsDefault)) } diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index c19badd6e3..cc79bbfe81 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright The Velero Contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,379 +20,421 @@ import ( "context" "time" + "github.com/apex/log" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" - snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" kuberrs "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/kubernetes" "github.com/vmware-tanzu/velero/pkg/util/kube" - "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/features" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" ) -type backupSyncController struct { - *genericController +const ( + backupSyncReconcilePeriod = time.Minute +) - backupClient velerov1client.BackupsGetter - kbClient client.Client - podVolumeBackupClient velerov1client.PodVolumeBackupsGetter - backupLister velerov1listers.BackupLister - csiVSLister snapshotv1listers.VolumeSnapshotLister - csiSnapshotClient *snapshotterClientSet.Clientset - kubeClient kubernetes.Interface +type backupSyncReconciler struct { + client client.Client namespace string - defaultBackupLocation string defaultBackupSyncPeriod time.Duration newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupStoreGetter persistence.ObjectBackupStoreGetter + logger logrus.FieldLogger } -func NewBackupSyncController( - backupClient velerov1client.BackupsGetter, - kbClient client.Client, - podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, - backupLister velerov1listers.BackupLister, - csiVSLister snapshotv1listers.VolumeSnapshotLister, - syncPeriod time.Duration, +// NewBackupSyncReconciler is used to generate BackupSync reconciler structure. +func NewBackupSyncReconciler( + client client.Client, namespace string, - csiSnapshotClient *snapshotterClientSet.Clientset, - kubeClient kubernetes.Interface, - defaultBackupLocation string, + defaultBackupSyncPeriod time.Duration, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupStoreGetter persistence.ObjectBackupStoreGetter, - logger logrus.FieldLogger, -) Interface { - if syncPeriod <= 0 { - syncPeriod = time.Minute - } - logger.Infof("Backup sync period is %v", syncPeriod) - - c := &backupSyncController{ - genericController: newGenericController(BackupSync, logger), - backupClient: backupClient, - kbClient: kbClient, - podVolumeBackupClient: podVolumeBackupClient, + logger logrus.FieldLogger) *backupSyncReconciler { + return &backupSyncReconciler{ + client: client, namespace: namespace, - defaultBackupLocation: defaultBackupLocation, - defaultBackupSyncPeriod: syncPeriod, - backupLister: backupLister, - csiVSLister: csiVSLister, - csiSnapshotClient: csiSnapshotClient, - kubeClient: kubeClient, - - // use variables to refer to these functions so they can be - // replaced with fakes for testing. - newPluginManager: newPluginManager, - backupStoreGetter: backupStoreGetter, + defaultBackupSyncPeriod: defaultBackupSyncPeriod, + newPluginManager: newPluginManager, + backupStoreGetter: backupStoreGetter, + logger: logger, } - - c.resyncFunc = c.run - c.resyncPeriod = 30 * time.Second - - return c } -// orderedBackupLocations returns a new slice with the default backup location first (if it exists), -// followed by the rest of the locations in no particular order. -func orderedBackupLocations(locationList *velerov1api.BackupStorageLocationList, defaultLocationName string) []velerov1api.BackupStorageLocation { - var result []velerov1api.BackupStorageLocation - - for i := range locationList.Items { - if locationList.Items[i].Name == defaultLocationName { - // put the default location first - result = append(result, locationList.Items[i]) - // append everything before the default - result = append(result, locationList.Items[:i]...) - // append everything after the default - result = append(result, locationList.Items[i+1:]...) +// Reconcile syncs between the backups in cluster and backups metadata in object store. +func (b *backupSyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := b.logger.WithField("controller", BackupSync) + log = log.WithField("backupLocation", req.String()) + log.Debug("Begin to sync between backups' metadata in BSL object storage and cluster's existing backups.") - return result + location := &velerov1api.BackupStorageLocation{} + err := b.client.Get(ctx, req.NamespacedName, location) + if err != nil { + if apierrors.IsNotFound(err) { + log.Debug("BackupStorageLocation is not found") + return ctrl.Result{}, nil } + return ctrl.Result{}, errors.Wrapf(err, "error getting BackupStorageLocation %s", req.String()) } - return locationList.Items -} + pluginManager := b.newPluginManager(log) + defer pluginManager.CleanupClients() -func (c *backupSyncController) run() { - c.logger.Debug("Checking for existing backup storage locations to sync into cluster") + log.Debug("Checking backup location for backups to sync into cluster") - locationList, err := storage.ListBackupStorageLocations(context.Background(), c.kbClient, c.namespace) + backupStore, err := b.backupStoreGetter.Get(location, pluginManager, log) if err != nil { - c.logger.WithError(err).Error("No backup storage locations found, at least one is required") - return + log.WithError(err).Error("Error getting backup store for this location") + return ctrl.Result{}, nil } - // sync the default backup storage location first, if it exists - for _, location := range locationList.Items { - if location.Spec.Default { - c.defaultBackupLocation = location.Name - break - } + // get a list of all the backups that are stored in the backup storage location + res, err := backupStore.ListBackups() + if err != nil { + log.WithError(err).Error("Error listing backups in backup store") + return ctrl.Result{}, nil + } + backupStoreBackups := sets.NewString(res...) + log.WithField("backupCount", len(backupStoreBackups)).Debug("Got backups from backup store") + + // get a list of all the backups that exist as custom resources in the cluster + var clusterBackupList velerov1api.BackupList + listOption := client.ListOptions{ + LabelSelector: labels.Everything(), + Namespace: b.namespace, } - locations := orderedBackupLocations(&locationList, c.defaultBackupLocation) - - pluginManager := c.newPluginManager(c.logger) - defer pluginManager.CleanupClients() - - for _, location := range locations { - log := c.logger.WithField("backupLocation", location.Name) - syncPeriod := c.defaultBackupSyncPeriod - if location.Spec.BackupSyncPeriod != nil { - syncPeriod = location.Spec.BackupSyncPeriod.Duration - if syncPeriod == 0 { - log.Debug("Backup sync period for this location is set to 0, skipping sync") - continue - } + err = b.client.List(ctx, &clusterBackupList, &listOption) + if err != nil { + log.WithError(errors.WithStack(err)).Error("Error getting backups from cluster, proceeding with sync into cluster") + } else { + log.WithField("backupCount", len(clusterBackupList.Items)).Debug("Got backups from cluster") + } - if syncPeriod < 0 { - log.Debug("Backup sync period must be non-negative") - syncPeriod = c.defaultBackupSyncPeriod - } - } + // get a list of backups that *are* in the backup storage location and *aren't* in the cluster + clusterBackupsSet := sets.NewString() + for _, b := range clusterBackupList.Items { + clusterBackupsSet.Insert(b.Name) + } + backupsToSync := backupStoreBackups.Difference(clusterBackupsSet) - lastSync := location.Status.LastSyncedTime - if lastSync != nil { - log.Debug("Checking if backups need to be synced at this time for this location") - nextSync := lastSync.Add(syncPeriod) - if time.Now().UTC().Before(nextSync) { - continue - } - } + if count := backupsToSync.Len(); count > 0 { + log.Infof("Found %v backups in the backup location that do not exist in the cluster and need to be synced", count) + } else { + log.Debug("No backups found in the backup location that need to be synced into the cluster") + } - log.Debug("Checking backup location for backups to sync into cluster") + // sync each backup + for backupName := range backupsToSync { + log = log.WithField("backup", backupName) + log.Info("Attempting to sync backup into cluster") - backupStore, err := c.backupStoreGetter.Get(&location, pluginManager, log) + backup, err := backupStore.GetBackupMetadata(backupName) if err != nil { - log.WithError(err).Error("Error getting backup store for this location") + log.WithError(errors.WithStack(err)).Error("Error getting backup metadata from backup store") continue } - // get a list of all the backups that are stored in the backup storage location - res, err := backupStore.ListBackups() - if err != nil { - log.WithError(err).Error("Error listing backups in backup store") - continue - } - backupStoreBackups := sets.NewString(res...) - log.WithField("backupCount", len(backupStoreBackups)).Debug("Got backups from backup store") + backup.Namespace = b.namespace + backup.ResourceVersion = "" - // get a list of all the backups that exist as custom resources in the cluster - clusterBackups, err := c.backupLister.Backups(c.namespace).List(labels.Everything()) - if err != nil { - log.WithError(errors.WithStack(err)).Error("Error getting backups from cluster, proceeding with sync into cluster") - } else { - log.WithField("backupCount", len(clusterBackups)).Debug("Got backups from cluster") + // update the StorageLocation field and label since the name of the location + // may be different in this cluster than in the cluster that created the + // backup. + backup.Spec.StorageLocation = location.Name + if backup.Labels == nil { + backup.Labels = make(map[string]string) } + backup.Labels[velerov1api.StorageLocationLabel] = label.GetValidName(backup.Spec.StorageLocation) - // get a list of backups that *are* in the backup storage location and *aren't* in the cluster - clusterBackupsSet := sets.NewString() - for _, b := range clusterBackups { - clusterBackupsSet.Insert(b.Name) + // attempt to create backup custom resource via API + err = b.client.Create(ctx, backup, &client.CreateOptions{}) + switch { + case err != nil && kuberrs.IsAlreadyExists(err): + log.Debug("Backup already exists in cluster") + continue + case err != nil && !kuberrs.IsAlreadyExists(err): + log.WithError(errors.WithStack(err)).Error("Error syncing backup into cluster") + continue + default: + log.Info("Successfully synced backup into cluster") } - backupsToSync := backupStoreBackups.Difference(clusterBackupsSet) - if count := backupsToSync.Len(); count > 0 { - log.Infof("Found %v backups in the backup location that do not exist in the cluster and need to be synced", count) - } else { - log.Debug("No backups found in the backup location that need to be synced into the cluster") + // process the pod volume backups from object store, if any + podVolumeBackups, err := backupStore.GetPodVolumeBackups(backupName) + if err != nil { + log.WithError(errors.WithStack(err)).Error("Error getting pod volume backups for this backup from backup store") + continue } - // sync each backup - for backupName := range backupsToSync { - log = log.WithField("backup", backupName) - log.Info("Attempting to sync backup into cluster") + for _, podVolumeBackup := range podVolumeBackups { + log := log.WithField("podVolumeBackup", podVolumeBackup.Name) + log.Debug("Checking this pod volume backup to see if it needs to be synced into the cluster") - backup, err := backupStore.GetBackupMetadata(backupName) - if err != nil { - log.WithError(errors.WithStack(err)).Error("Error getting backup metadata from backup store") - continue + for i, ownerRef := range podVolumeBackup.OwnerReferences { + if ownerRef.APIVersion == velerov1api.SchemeGroupVersion.String() && ownerRef.Kind == "Backup" && ownerRef.Name == backup.Name { + log.WithField("uid", backup.UID).Debugf("Updating pod volume backup's owner reference UID") + podVolumeBackup.OwnerReferences[i].UID = backup.UID + } } - backup.Namespace = c.namespace - backup.ResourceVersion = "" - - // update the StorageLocation field and label since the name of the location - // may be different in this cluster than in the cluster that created the - // backup. - backup.Spec.StorageLocation = location.Name - if backup.Labels == nil { - backup.Labels = make(map[string]string) + if _, ok := podVolumeBackup.Labels[velerov1api.BackupUIDLabel]; ok { + podVolumeBackup.Labels[velerov1api.BackupUIDLabel] = string(backup.UID) } - backup.Labels[velerov1api.StorageLocationLabel] = label.GetValidName(backup.Spec.StorageLocation) - // attempt to create backup custom resource via API - backup, err = c.backupClient.Backups(backup.Namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) + podVolumeBackup.Namespace = backup.Namespace + podVolumeBackup.ResourceVersion = "" + + err = b.client.Create(ctx, podVolumeBackup, &client.CreateOptions{}) switch { case err != nil && kuberrs.IsAlreadyExists(err): - log.Debug("Backup already exists in cluster") + log.Debug("Pod volume backup already exists in cluster") continue case err != nil && !kuberrs.IsAlreadyExists(err): - log.WithError(errors.WithStack(err)).Error("Error syncing backup into cluster") + log.WithError(errors.WithStack(err)).Error("Error syncing pod volume backup into cluster") continue default: - log.Info("Successfully synced backup into cluster") + log.Debug("Synced pod volume backup into cluster") } + } - // process the pod volume backups from object store, if any - podVolumeBackups, err := backupStore.GetPodVolumeBackups(backupName) + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + // we are syncing these objects only to ensure that the storage snapshots are cleaned up + // on backup deletion or expiry. + log.Info("Syncing CSI VolumeSnapshotClasses in backup") + vsClasses, err := backupStore.GetCSIVolumeSnapshotClasses(backupName) if err != nil { - log.WithError(errors.WithStack(err)).Error("Error getting pod volume backups for this backup from backup store") + log.WithError(errors.WithStack(err)).Error("Error getting CSI VolumeSnapClasses for this backup from backup store") continue } - - for _, podVolumeBackup := range podVolumeBackups { - log := log.WithField("podVolumeBackup", podVolumeBackup.Name) - log.Debug("Checking this pod volume backup to see if it needs to be synced into the cluster") - - for i, ownerRef := range podVolumeBackup.OwnerReferences { - if ownerRef.APIVersion == velerov1api.SchemeGroupVersion.String() && ownerRef.Kind == "Backup" && ownerRef.Name == backup.Name { - log.WithField("uid", backup.UID).Debugf("Updating pod volume backup's owner reference UID") - podVolumeBackup.OwnerReferences[i].UID = backup.UID - } - } - - if _, ok := podVolumeBackup.Labels[velerov1api.BackupUIDLabel]; ok { - podVolumeBackup.Labels[velerov1api.BackupUIDLabel] = string(backup.UID) - } - - podVolumeBackup.Namespace = backup.Namespace - podVolumeBackup.ResourceVersion = "" - - _, err = c.podVolumeBackupClient.PodVolumeBackups(backup.Namespace).Create(context.TODO(), podVolumeBackup, metav1.CreateOptions{}) + for _, vsClass := range vsClasses { + vsClass.ResourceVersion = "" + err := b.client.Create(ctx, vsClass, &client.CreateOptions{}) switch { case err != nil && kuberrs.IsAlreadyExists(err): - log.Debug("Pod volume backup already exists in cluster") + log.Debugf("VolumeSnapshotClass %s already exists in cluster", vsClass.Name) continue case err != nil && !kuberrs.IsAlreadyExists(err): - log.WithError(errors.WithStack(err)).Error("Error syncing pod volume backup into cluster") + log.WithError(errors.WithStack(err)).Errorf("Error syncing VolumeSnapshotClass %s into cluster", vsClass.Name) continue default: - log.Debug("Synced pod volume backup into cluster") + log.Infof("Created CSI VolumeSnapshotClass %s", vsClass.Name) } } - if features.IsEnabled(velerov1api.CSIFeatureFlag) { - // we are syncing these objects only to ensure that the storage snapshots are cleaned up - // on backup deletion or expiry. - log.Info("Syncing CSI volumesnapshotclasses in backup") - vsClasses, err := backupStore.GetCSIVolumeSnapshotClasses(backupName) - if err != nil { - log.WithError(errors.WithStack(err)).Error("Error getting CSI volumesnapclasses for this backup from backup store") - continue - } - for _, vsClass := range vsClasses { - vsClass.ResourceVersion = "" - created, err := c.csiSnapshotClient.SnapshotV1().VolumeSnapshotClasses().Create(context.TODO(), vsClass, metav1.CreateOptions{}) - if err != nil { - log.WithError(errors.WithStack(err)).Errorf("Error syncing volumesnapshotclass %s into cluster", vsClass.Name) - continue - } - log.Infof("Created CSI volumesnapshotclass %s", created.Name) - } + log.Info("Syncing CSI volumesnapshotcontents in backup") + snapConts, err := backupStore.GetCSIVolumeSnapshotContents(backupName) + if err != nil { + log.WithError(errors.WithStack(err)).Error("Error getting CSI volumesnapshotcontents for this backup from backup store") + continue + } - log.Info("Syncing CSI volumesnapshotcontents in backup") - snapConts, err := backupStore.GetCSIVolumeSnapshotContents(backupName) - if err != nil { - log.WithError(errors.WithStack(err)).Error("Error getting CSI volumesnapshotcontents for this backup from backup store") + log.Infof("Syncing %d CSI volumesnapshotcontents in backup", len(snapConts)) + for _, snapCont := range snapConts { + // TODO: Reset ResourceVersion prior to persisting VolumeSnapshotContents + snapCont.ResourceVersion = "" + err := b.client.Create(ctx, snapCont, &client.CreateOptions{}) + switch { + case err != nil && kuberrs.IsAlreadyExists(err): + log.Debugf("volumesnapshotcontent %s already exists in cluster", snapCont.Name) continue - } - - log.Infof("Syncing %d CSI volumesnapshotcontents in backup", len(snapConts)) - for _, snapCont := range snapConts { - // TODO: Reset ResourceVersion prior to persisting VolumeSnapshotContents - snapCont.ResourceVersion = "" - created, err := c.csiSnapshotClient.SnapshotV1().VolumeSnapshotContents().Create(context.TODO(), snapCont, metav1.CreateOptions{}) - switch { - case err != nil && kuberrs.IsAlreadyExists(err): - log.Debugf("volumesnapshotcontent %s already exists in cluster", snapCont.Name) - continue - case err != nil && !kuberrs.IsAlreadyExists(err): - log.WithError(errors.WithStack(err)).Errorf("Error syncing volumesnapshotcontent %s into cluster", snapCont.Name) - continue - default: - log.Infof("Created CSI volumesnapshotcontent %s", created.Name) - } + case err != nil && !kuberrs.IsAlreadyExists(err): + log.WithError(errors.WithStack(err)).Errorf("Error syncing volumesnapshotcontent %s into cluster", snapCont.Name) + continue + default: + log.Infof("Created CSI volumesnapshotcontent %s", snapCont.Name) } } } + } - c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) + b.deleteOrphanedBackups(ctx, location.Name, backupStoreBackups, log) - // update the location's last-synced time field - statusPatch := client.MergeFrom(location.DeepCopy()) - location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} - if err := c.kbClient.Patch(context.Background(), &location, statusPatch); err != nil { - log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time") - continue - } + // update the location's last-synced time field + statusPatch := client.MergeFrom(location.DeepCopy()) + location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} + if err := b.client.Patch(ctx, location, statusPatch); err != nil { + log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time") + return ctrl.Result{}, nil } + + return ctrl.Result{}, nil +} + +// SetupWithManager is used to setup controller and its watching sources. +func (b *backupSyncReconciler) SetupWithManager(mgr ctrl.Manager) error { + backupSyncSource := kube.NewPeriodicalEnqueueSource( + b.logger, + mgr.GetClient(), + &velerov1api.BackupStorageLocationList{}, + backupSyncReconcilePeriod, + kube.PeriodicalEnqueueSourceOption{ + OrderFunc: backupSyncSourceOrderFunc, + FilterFuncs: []func(object client.Object) bool{ + func(object client.Object) bool { + location := object.(*velerov1api.BackupStorageLocation) + return b.locationFilterFunc(location) + }, + }, + }, + ) + + return ctrl.NewControllerManagedBy(mgr). + For(&velerov1api.BackupStorageLocation{}). + // Filter all BSL events, because this controller is supposed to run periodically, not by event. + WithEventFilter(predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(ue event.UpdateEvent) bool { + return false + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + GenericFunc: func(ge event.GenericEvent) bool { + return false + }, + }). + Watches(backupSyncSource, nil). + Complete(b) } // deleteOrphanedBackups deletes backup objects (CRDs) from Kubernetes that have the specified location // and a phase of Completed, but no corresponding backup in object storage. -func (c *backupSyncController) deleteOrphanedBackups(locationName string, backupStoreBackups sets.String, log logrus.FieldLogger) { - locationSelector := labels.Set(map[string]string{ - velerov1api.StorageLocationLabel: label.GetValidName(locationName), - }).AsSelector() - - backups, err := c.backupLister.Backups(c.namespace).List(locationSelector) +func (b *backupSyncReconciler) deleteOrphanedBackups(ctx context.Context, locationName string, backupStoreBackups sets.String, log logrus.FieldLogger) { + var backupList velerov1api.BackupList + listOption := client.ListOptions{ + LabelSelector: labels.Set(map[string]string{ + velerov1api.StorageLocationLabel: label.GetValidName(locationName), + }).AsSelector(), + } + err := b.client.List(ctx, &backupList, &listOption) if err != nil { log.WithError(errors.WithStack(err)).Error("Error listing backups from cluster") return } - if len(backups) == 0 { + + if len(backupList.Items) == 0 { return } - for _, backup := range backups { + for _, backup := range backupList.Items { log = log.WithField("backup", backup.Name) if backup.Status.Phase != velerov1api.BackupPhaseCompleted || backupStoreBackups.Has(backup.Name) { continue } - if err := c.backupClient.Backups(backup.Namespace).Delete(context.TODO(), backup.Name, metav1.DeleteOptions{}); err != nil { + + if err := b.client.Delete(ctx, &backup, &client.DeleteOptions{}); err != nil { log.WithError(errors.WithStack(err)).Error("Error deleting orphaned backup from cluster") } else { log.Debug("Deleted orphaned backup from cluster") - c.deleteCSISnapshotsByBackup(backup.Name, log) + b.deleteCSISnapshotsByBackup(ctx, backup.Name, log) } } } -func (c *backupSyncController) deleteCSISnapshotsByBackup(backupName string, log logrus.FieldLogger) { +func (b *backupSyncReconciler) deleteCSISnapshotsByBackup(ctx context.Context, backupName string, log logrus.FieldLogger) { if !features.IsEnabled(velerov1api.CSIFeatureFlag) { return } m := client.MatchingLabels{velerov1api.BackupNameLabel: label.GetValidName(backupName)} - if vsList, err := c.csiVSLister.List(label.NewSelectorForBackup(label.GetValidName(backupName))); err != nil { + var vsList snapshotv1api.VolumeSnapshotList + listOptions := &client.ListOptions{ + LabelSelector: label.NewSelectorForBackup(label.GetValidName(backupName)), + } + if err := b.client.List(ctx, &vsList, listOptions); err != nil { log.WithError(err).Warnf("Failed to list volumesnapshots for backup: %s, the deletion will be skipped", backupName) } else { - for _, vs := range vsList { + for _, vs := range vsList.Items { name := kube.NamespaceAndName(vs.GetObjectMeta()) log.Debugf("Deleting volumesnapshot %s", name) - if err := c.kbClient.Delete(context.TODO(), vs); err != nil { + if err := b.client.Delete(context.TODO(), &vs); err != nil { log.WithError(err).Warnf("Failed to delete volumesnapshot %s", name) } } } vsc := &snapshotv1api.VolumeSnapshotContent{} log.Debugf("Deleting volumesnapshotcontents for backup: %s", backupName) - if err := c.kbClient.DeleteAllOf(context.TODO(), vsc, m); err != nil { + if err := b.client.DeleteAllOf(context.TODO(), vsc, m); err != nil { log.WithError(err).Warnf("Failed to delete volumesnapshotcontents for backup: %s", backupName) } } + +// backupSyncSourceOrderFunc returns a new slice with the default backup location first (if it exists), +// followed by the rest of the locations in no particular order. +func backupSyncSourceOrderFunc(objList client.ObjectList) client.ObjectList { + inputBSLList := objList.(*velerov1api.BackupStorageLocationList) + resultBSLList := &velerov1api.BackupStorageLocationList{} + bslArray := make([]runtime.Object, 0) + + if len(inputBSLList.Items) <= 0 { + return objList + } + + for i := range inputBSLList.Items { + location := inputBSLList.Items[i] + + // sync the default backup storage location first, if it exists + if location.Spec.Default { + // put the default location first + bslArray = append(bslArray, &inputBSLList.Items[i]) + // append everything before the default + for _, bsl := range inputBSLList.Items[:i] { + bslArray = append(bslArray, &bsl) + } + // append everything after the default + for _, bsl := range inputBSLList.Items[i+1:] { + bslArray = append(bslArray, &bsl) + } + meta.SetList(resultBSLList, bslArray) + + return resultBSLList + } + } + + // No default BSL found. Return the input. + return objList +} + +func (b *backupSyncReconciler) locationFilterFunc(location *velerov1api.BackupStorageLocation) bool { + syncPeriod := b.defaultBackupSyncPeriod + if location.Spec.BackupSyncPeriod != nil { + syncPeriod = location.Spec.BackupSyncPeriod.Duration + if syncPeriod == 0 { + log.Debug("Backup sync period for this location is set to 0, skipping sync") + return false + } + + if syncPeriod < 0 { + log.Debug("Backup sync period must be non-negative") + syncPeriod = b.defaultBackupSyncPeriod + } + } + + lastSync := location.Status.LastSyncedTime + if lastSync != nil { + log.Debug("Checking if backups need to be synced at this time for this location") + nextSync := lastSync.Add(syncPeriod) + if time.Now().UTC().Before(nextSync) { + return false + } + } + return true +} diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 6ba8253733..4f1e280c55 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -18,21 +18,27 @@ package controller import ( "context" - "testing" + "fmt" "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" core "k8s.io/client-go/testing" + ctrl "sigs.k8s.io/controller-runtime" + ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" + ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/label" persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" @@ -40,12 +46,30 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) +func defaultLocation(namespace string) *velerov1api.BackupStorageLocation { + return &velerov1api.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "location-1", + }, + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "objStoreProvider", + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "bucket-1", + }, + }, + Default: true, + }, + } +} + func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation { return []*velerov1api.BackupStorageLocation{ { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - Name: "location-1", + Name: "location-0", }, Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", @@ -54,32 +78,27 @@ func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation Bucket: "bucket-1", }, }, - Default: true, }, }, { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - Name: "location-2", + Name: "location-1", }, Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", StorageType: velerov1api.StorageType{ ObjectStorage: &velerov1api.ObjectStorageLocation{ - Bucket: "bucket-2", + Bucket: "bucket-1", }, }, + Default: true, }, }, - } -} - -func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api.BackupStorageLocation { - return []*velerov1api.BackupStorageLocation{ { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - Name: "the-really-long-location-name-that-is-much-more-than-63-characters-1", + Name: "location-2", }, Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", @@ -93,13 +112,13 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - Name: "the-really-long-location-name-that-is-much-more-than-63-characters-2", + Name: "location-3", }, Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", StorageType: velerov1api.StorageType{ ObjectStorage: &velerov1api.ObjectStorageLocation{ - Bucket: "bucket-2", + Bucket: "bucket-1", }, }, }, @@ -107,485 +126,447 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api } } -func TestBackupSyncControllerRun(t *testing.T) { - type cloudBackupData struct { - backup *velerov1api.Backup - podVolumeBackups []*velerov1api.PodVolumeBackup +func defaultLocationWithLongerLocationName(namespace string) *velerov1api.BackupStorageLocation { + return &velerov1api.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "the-really-long-location-name-that-is-much-more-than-63-characters-1", + }, + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "objStoreProvider", + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "bucket-1", + }, + }, + }, } +} - tests := []struct { - name string - namespace string - locations []*velerov1api.BackupStorageLocation - cloudBuckets map[string][]*cloudBackupData - existingBackups []*velerov1api.Backup - existingPodVolumeBackups []*velerov1api.PodVolumeBackup - longLocationNameEnabled bool - }{ - { - name: "no cloud backups", - }, - { - name: "normal case", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ +func numBackups(c ctrlClient.WithWatch, ns string) (int, error) { + var existingK8SBackups velerov1api.BackupList + err := c.List(context.TODO(), &existingK8SBackups, &ctrlClient.ListOptions{}) + if err != nil { + return 0, err + } + + return len(existingK8SBackups.Items), nil +} + +var _ = Describe("Backup Sync Reconciler", func() { + It("Test Backup Sync Reconciler basic function", func() { + type cloudBackupData struct { + backup *velerov1api.Backup + podVolumeBackups []*velerov1api.PodVolumeBackup + } + + tests := []struct { + name string + namespace string + location *velerov1api.BackupStorageLocation + cloudBackups []*cloudBackupData + existingBackups []*velerov1api.Backup + existingPodVolumeBackups []*velerov1api.PodVolumeBackup + longLocationNameEnabled bool + }{ + { + name: "no cloud backups", + namespace: "ns-1", + location: defaultLocation("ns-1"), + }, + { + name: "normal case", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").Result(), - }, - }, }, - }, - { - name: "all synced backups get created in Velero server's namespace", - namespace: "velero", - locations: defaultLocationsList("velero"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "all synced backups get created in Velero server's namespace", + namespace: "velero", + location: defaultLocation("velero"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-2", "backup-3").Result(), - }, - &cloudBackupData{ - backup: builder.ForBackup("velero", "backup-4").Result(), - }, - }, }, - }, - { - name: "new backups get synced when some cloud backups already exist in the cluster", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "new backups get synced when some cloud backups already exist in the cluster", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").Result(), - }, - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-4").Result(), - }, + existingBackups: []*velerov1api.Backup{ + // add a label to each existing backup so we can differentiate it from the cloud + // backup during verification + builder.ForBackup("ns-1", "backup-1").StorageLocation("location-1").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), + builder.ForBackup("ns-1", "backup-3").StorageLocation("location-2").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), }, }, - existingBackups: []*velerov1api.Backup{ - // add a label to each existing backup so we can differentiate it from the cloud - // backup during verification - builder.ForBackup("ns-1", "backup-1").StorageLocation("location-1").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), - builder.ForBackup("ns-1", "backup-3").StorageLocation("location-2").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), - }, - }, - { - name: "existing backups without a StorageLocation get it filled in", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "existing backups without a StorageLocation get it filled in", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), }, }, + existingBackups: []*velerov1api.Backup{ + // add a label to each existing backup so we can differentiate it from the cloud + // backup during verification + builder.ForBackup("ns-1", "backup-1").ObjectMeta(builder.WithLabels("i-exist", "true")).StorageLocation("location-1").Result(), + }, }, - existingBackups: []*velerov1api.Backup{ - // add a label to each existing backup so we can differentiate it from the cloud - // backup during verification - builder.ForBackup("ns-1", "backup-1").ObjectMeta(builder.WithLabels("i-exist", "true")).StorageLocation("location-1").Result(), - }, - }, - { - name: "backup storage location names and labels get updated", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "backup storage location names and labels get updated", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(), }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(), - }, - }, }, - }, - { - name: "backup storage location names and labels get updated with location name greater than 63 chars", - namespace: "ns-1", - locations: defaultLocationsListWithLongerLocationName("ns-1"), - longLocationNameEnabled: true, - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "backup storage location names and labels get updated with location name greater than 63 chars", + namespace: "ns-1", + location: defaultLocationWithLongerLocationName("ns-1"), + longLocationNameEnabled: true, + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(), }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(), - }, - }, }, - }, - { - name: "all synced backups and pod volume backups get created in Velero server's namespace", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "all synced backups and pod volume backups get created in Velero server's namespace", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), }, }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), }, }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").Result(), - }, - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-4").Result(), - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), - builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), - builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(), - }, - }, - }, }, - }, - { - name: "new pod volume backups get synched when some pod volume backups already exist in the cluster", - namespace: "ns-1", - locations: defaultLocationsList("ns-1"), - cloudBuckets: map[string][]*cloudBackupData{ - "bucket-1": { - &cloudBackupData{ + { + name: "new pod volume backups get synched when some pod volume backups already exist in the cluster", + namespace: "ns-1", + location: defaultLocation("ns-1"), + cloudBackups: []*cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-1").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), }, }, - &cloudBackupData{ + { backup: builder.ForBackup("ns-1", "backup-2").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(), }, }, }, - "bucket-2": { - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-3").Result(), - }, - &cloudBackupData{ - backup: builder.ForBackup("ns-1", "backup-4").Result(), - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), - builder.ForPodVolumeBackup("ns-1", "pvb-5").Result(), - builder.ForPodVolumeBackup("ns-1", "pvb-6").Result(), - }, - }, + existingPodVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), }, }, - existingPodVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), - builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), - }, - }, - } + } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + for _, test := range tests { var ( - client = fake.NewSimpleClientset() - fakeClient = velerotest.NewFakeControllerRuntimeClient(t) - sharedInformers = informers.NewSharedInformerFactory(client, 0) - pluginManager = &pluginmocks.Manager{} - backupStores = make(map[string]*persistencemocks.BackupStore) + client = ctrlfake.NewClientBuilder().Build() + pluginManager = &pluginmocks.Manager{} + backupStores = make(map[string]*persistencemocks.BackupStore) ) - c := NewBackupSyncController( - client.VeleroV1(), - fakeClient, - client.VeleroV1(), - sharedInformers.Velero().V1().Backups().Lister(), - nil, // csiVSLister - time.Duration(0), - test.namespace, - nil, // csiSnapshotClient - nil, // kubeClient - "", - func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - NewFakeObjectBackupStoreGetter(backupStores), - velerotest.NewLogger(), - ).(*backupSyncController) - pluginManager.On("CleanupClients").Return(nil) - - for _, location := range test.locations { - require.NoError(t, fakeClient.Create(context.Background(), location)) - backupStores[location.Name] = &persistencemocks.BackupStore{} + r := backupSyncReconciler{ + client: client, + namespace: test.namespace, + defaultBackupSyncPeriod: time.Second * 10, + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), + logger: velerotest.NewLogger(), } - for _, location := range test.locations { - backupStore, ok := backupStores[location.Name] - require.True(t, ok, "no mock backup store for location %s", location.Name) + if test.location != nil { + Expect(r.client.Create(ctx, test.location)).ShouldNot(HaveOccurred()) + backupStores[test.location.Name] = &persistencemocks.BackupStore{} + + backupStore, ok := backupStores[test.location.Name] + Expect(ok).To(BeTrue(), "no mock backup store for location %s", test.location.Name) var backupNames []string - for _, bucket := range test.cloudBuckets[location.Spec.ObjectStorage.Bucket] { - backupNames = append(backupNames, bucket.backup.Name) - backupStore.On("GetBackupMetadata", bucket.backup.Name).Return(bucket.backup, nil) - backupStore.On("GetPodVolumeBackups", bucket.backup.Name).Return(bucket.podVolumeBackups, nil) + for _, backup := range test.cloudBackups { + backupNames = append(backupNames, backup.backup.Name) + backupStore.On("GetBackupMetadata", backup.backup.Name).Return(backup.backup, nil) + backupStore.On("GetPodVolumeBackups", backup.backup.Name).Return(backup.podVolumeBackups, nil) } backupStore.On("ListBackups").Return(backupNames, nil) } for _, existingBackup := range test.existingBackups { - require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(existingBackup)) - - _, err := client.VeleroV1().Backups(test.namespace).Create(context.TODO(), existingBackup, metav1.CreateOptions{}) - require.NoError(t, err) + err := client.Create(context.TODO(), existingBackup, &ctrlClient.CreateOptions{}) + Expect(err).ShouldNot(HaveOccurred()) } for _, existingPodVolumeBackup := range test.existingPodVolumeBackups { - require.NoError(t, sharedInformers.Velero().V1().PodVolumeBackups().Informer().GetStore().Add(existingPodVolumeBackup)) - - _, err := client.VeleroV1().PodVolumeBackups(test.namespace).Create(context.TODO(), existingPodVolumeBackup, metav1.CreateOptions{}) - require.NoError(t, err) + err := client.Create(context.TODO(), existingPodVolumeBackup, &ctrlClient.CreateOptions{}) + Expect(err).ShouldNot(HaveOccurred()) } - client.ClearActions() - c.run() - - for bucket, backupDataSet := range test.cloudBuckets { - // figure out which location this bucket is for; we need this for verification - // purposes later - var location *velerov1api.BackupStorageLocation - for _, loc := range test.locations { - if loc.Spec.ObjectStorage.Bucket == bucket { - location = loc + actualResult, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: test.location.Namespace, Name: test.location.Name}, + }) + + Expect(actualResult).To(BeEquivalentTo(ctrl.Result{})) + Expect(err).To(BeNil()) + + // process the cloud backups + for _, cloudBackupData := range test.cloudBackups { + obj := &velerov1api.Backup{} + err := client.Get( + context.TODO(), + types.NamespacedName{ + Namespace: cloudBackupData.backup.Namespace, + Name: cloudBackupData.backup.Name}, + obj) + Expect(err).To(BeNil()) + + // did this cloud backup already exist in the cluster? + var existing *velerov1api.Backup + for _, obj := range test.existingBackups { + if obj.Name == cloudBackupData.backup.Name { + existing = obj break } } - require.NotNil(t, location) - - // process the cloud backups - for _, cloudBackupData := range backupDataSet { - obj, err := client.VeleroV1().Backups(test.namespace).Get(context.TODO(), cloudBackupData.backup.Name, metav1.GetOptions{}) - require.NoError(t, err) - - // did this cloud backup already exist in the cluster? - var existing *velerov1api.Backup - for _, obj := range test.existingBackups { - if obj.Name == cloudBackupData.backup.Name { - existing = obj - break - } - } - if existing != nil { - // if this cloud backup already exists in the cluster, make sure that what we get from the - // client is the existing backup, not the cloud one. + if existing != nil { + // if this cloud backup already exists in the cluster, make sure that what we get from the + // client is the existing backup, not the cloud one. - // verify that the in-cluster backup has its storage location populated, if it's not already. - expected := existing.DeepCopy() - expected.Spec.StorageLocation = location.Name + // verify that the in-cluster backup has its storage location populated, if it's not already. + expected := existing.DeepCopy() + expected.Spec.StorageLocation = test.location.Name - assert.Equal(t, expected, obj) - } else { - // verify that the storage location field and label are set properly - assert.Equal(t, location.Name, obj.Spec.StorageLocation) + Expect(expected).To(BeEquivalentTo(obj)) + } else { + // verify that the storage location field and label are set properly + Expect(test.location.Name).To(BeEquivalentTo(obj.Spec.StorageLocation)) - locationName := location.Name - if test.longLocationNameEnabled { - locationName = label.GetValidName(locationName) - } - assert.Equal(t, locationName, obj.Labels[velerov1api.StorageLocationLabel]) - assert.Equal(t, true, len(obj.Labels[velerov1api.StorageLocationLabel]) <= validation.DNS1035LabelMaxLength) + locationName := test.location.Name + if test.longLocationNameEnabled { + locationName = label.GetValidName(locationName) } + Expect(locationName).To(BeEquivalentTo(obj.Labels[velerov1api.StorageLocationLabel])) + Expect(len(obj.Labels[velerov1api.StorageLocationLabel]) <= validation.DNS1035LabelMaxLength).To(BeTrue()) + } - // process the cloud pod volume backups for this backup, if any - for _, podVolumeBackup := range cloudBackupData.podVolumeBackups { - objPodVolumeBackup, err := client.VeleroV1().PodVolumeBackups(test.namespace).Get(context.TODO(), podVolumeBackup.Name, metav1.GetOptions{}) - require.NoError(t, err) - - // did this cloud pod volume backup already exist in the cluster? - var existingPodVolumeBackup *velerov1api.PodVolumeBackup - for _, objPodVolumeBackup := range test.existingPodVolumeBackups { - if objPodVolumeBackup.Name == podVolumeBackup.Name { - existingPodVolumeBackup = objPodVolumeBackup - break - } + // process the cloud pod volume backups for this backup, if any + for _, podVolumeBackup := range cloudBackupData.podVolumeBackups { + objPodVolumeBackup := &velerov1api.PodVolumeBackup{} + err := client.Get( + context.TODO(), + types.NamespacedName{ + Namespace: podVolumeBackup.Namespace, + Name: podVolumeBackup.Name, + }, + objPodVolumeBackup) + Expect(err).ShouldNot(HaveOccurred()) + + // did this cloud pod volume backup already exist in the cluster? + var existingPodVolumeBackup *velerov1api.PodVolumeBackup + for _, objPodVolumeBackup := range test.existingPodVolumeBackups { + if objPodVolumeBackup.Name == podVolumeBackup.Name { + existingPodVolumeBackup = objPodVolumeBackup + break } + } - if existingPodVolumeBackup != nil { - // if this cloud pod volume backup already exists in the cluster, make sure that what we get from the - // client is the existing backup, not the cloud one. - expected := existingPodVolumeBackup.DeepCopy() - assert.Equal(t, expected, objPodVolumeBackup) - } + if existingPodVolumeBackup != nil { + // if this cloud pod volume backup already exists in the cluster, make sure that what we get from the + // client is the existing backup, not the cloud one. + expected := existingPodVolumeBackup.DeepCopy() + Expect(expected).To(BeEquivalentTo(objPodVolumeBackup)) } } } - }) - } -} + } + }) -func TestDeleteOrphanedBackups(t *testing.T) { - baseBuilder := func(name string) *builder.BackupBuilder { - return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) - } + It("Test deleting orphaned backups.", func() { + longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters" - tests := []struct { - name string - cloudBackups sets.String - k8sBackups []*velerov1api.Backup - namespace string - expectedDeletes sets.String - }{ - { - name: "no overlapping backups", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backupB").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backupC").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder := func(name string) *builder.BackupBuilder { + return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) + } + + tests := []struct { + name string + cloudBackups sets.String + k8sBackups []*velerov1api.Backup + namespace string + expectedDeletes sets.String + useLongBSLName bool + }{ + { + name: "no overlapping backups", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backupB").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backupC").Phase(velerov1api.BackupPhaseCompleted).Result(), + }, + expectedDeletes: sets.NewString("backupA", "backupB", "backupC"), }, - expectedDeletes: sets.NewString("backupA", "backupB", "backupC"), - }, - { - name: "some overlapping backups", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), + { + name: "some overlapping backups", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), + }, + expectedDeletes: sets.NewString("backup-C"), }, - expectedDeletes: sets.NewString("backup-C"), - }, - { - name: "all overlapping backups", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-3").Phase(velerov1api.BackupPhaseCompleted).Result(), + { + name: "all overlapping backups", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-3").Phase(velerov1api.BackupPhaseCompleted).Result(), + }, + expectedDeletes: sets.NewString(), }, - expectedDeletes: sets.NewString(), - }, - { - name: "no overlapping backups but including backups that are not complete", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("Deleting").Phase(velerov1api.BackupPhaseDeleting).Result(), - baseBuilder("Failed").Phase(velerov1api.BackupPhaseFailed).Result(), - baseBuilder("FailedValidation").Phase(velerov1api.BackupPhaseFailedValidation).Result(), - baseBuilder("InProgress").Phase(velerov1api.BackupPhaseInProgress).Result(), - baseBuilder("New").Phase(velerov1api.BackupPhaseNew).Result(), + { + name: "no overlapping backups but including backups that are not complete", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("Deleting").Phase(velerov1api.BackupPhaseDeleting).Result(), + baseBuilder("Failed").Phase(velerov1api.BackupPhaseFailed).Result(), + baseBuilder("FailedValidation").Phase(velerov1api.BackupPhaseFailedValidation).Result(), + baseBuilder("InProgress").Phase(velerov1api.BackupPhaseInProgress).Result(), + baseBuilder("New").Phase(velerov1api.BackupPhaseNew).Result(), + }, + expectedDeletes: sets.NewString("backupA"), }, - expectedDeletes: sets.NewString("backupA"), - }, - { - name: "all overlapping backups and all backups that are not complete", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backup-1").Phase(velerov1api.BackupPhaseFailed).Result(), - baseBuilder("backup-2").Phase(velerov1api.BackupPhaseFailedValidation).Result(), - baseBuilder("backup-3").Phase(velerov1api.BackupPhaseInProgress).Result(), + { + name: "all overlapping backups and all backups that are not complete", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseFailed).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseFailedValidation).Result(), + baseBuilder("backup-3").Phase(velerov1api.BackupPhaseInProgress).Result(), + }, + expectedDeletes: sets.NewString(), }, - expectedDeletes: sets.NewString(), - }, - { - name: "no completed backups in other locations are deleted", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), - - baseBuilder("backup-4").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-5").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), - baseBuilder("backup-6").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + { + name: "no completed backups in other locations are deleted", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), + + baseBuilder("backup-4").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-5").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-6").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + }, + expectedDeletes: sets.NewString("backup-C"), }, - expectedDeletes: sets.NewString("backup-C"), - }, - } + { + name: "some overlapping backups", + namespace: "ns-1", + cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), + k8sBackups: []*velerov1api.Backup{ + builder.ForBackup("ns-1", "backup-1"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), + builder.ForBackup("ns-1", "backup-2"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), + builder.ForBackup("ns-1", "backup-C"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), + }, + expectedDeletes: sets.NewString("backup-C"), + useLongBSLName: true, + }, + } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + for _, test := range tests { var ( - client = fake.NewSimpleClientset() - fakeClient = velerotest.NewFakeControllerRuntimeClient(t) - sharedInformers = informers.NewSharedInformerFactory(client, 0) + client = ctrlfake.NewClientBuilder().Build() + pluginManager = &pluginmocks.Manager{} + backupStores = make(map[string]*persistencemocks.BackupStore) ) - c := NewBackupSyncController( - client.VeleroV1(), - fakeClient, - client.VeleroV1(), - sharedInformers.Velero().V1().Backups().Lister(), - nil, // csiVSLister - time.Duration(0), - test.namespace, - nil, // csiSnapshotClient - nil, // kubeClient - "", - nil, // new plugin manager func - nil, // backupStoreGetter - velerotest.NewLogger(), - ).(*backupSyncController) + r := backupSyncReconciler{ + client: client, + namespace: test.namespace, + defaultBackupSyncPeriod: time.Second * 10, + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeObjectBackupStoreGetter(backupStores), + logger: velerotest.NewLogger(), + } expectedDeleteActions := make([]core.Action, 0) for _, backup := range test.k8sBackups { - // add test backup to informer - require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup), "Error adding backup to informer") - // add test backup to client - _, err := client.VeleroV1().Backups(test.namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) - require.NoError(t, err, "Error adding backup to clientset") + err := client.Create(context.TODO(), backup, &ctrlClient.CreateOptions{}) + Expect(err).ShouldNot(HaveOccurred()) // if we expect this backup to be deleted, set up the expected DeleteAction if test.expectedDeletes.Has(backup.Name) { @@ -598,140 +579,45 @@ func TestDeleteOrphanedBackups(t *testing.T) { } } - c.deleteOrphanedBackups("default", test.cloudBackups, velerotest.NewLogger()) - - numBackups, err := numBackups(t, client, c.namespace) - assert.NoError(t, err) - - expected := len(test.k8sBackups) - len(test.expectedDeletes) - assert.Equal(t, expected, numBackups) - - velerotest.CompareActions(t, expectedDeleteActions, getDeleteActions(client.Actions())) - }) - } -} - -func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { - longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters" - tests := []struct { - name string - cloudBackups sets.String - k8sBackups []*velerov1api.Backup - namespace string - expectedDeletes sets.String - }{ - { - name: "some overlapping backups", - namespace: "ns-1", - cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), - k8sBackups: []*velerov1api.Backup{ - builder.ForBackup("ns-1", "backup-1"). - ObjectMeta( - builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), - ). - Phase(velerov1api.BackupPhaseCompleted). - Result(), - builder.ForBackup("ns-1", "backup-2"). - ObjectMeta( - builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), - ). - Phase(velerov1api.BackupPhaseCompleted). - Result(), - builder.ForBackup("ns-1", "backup-C"). - ObjectMeta( - builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), - ). - Phase(velerov1api.BackupPhaseCompleted). - Result(), - }, - expectedDeletes: sets.NewString("backup-C"), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var ( - client = fake.NewSimpleClientset() - fakeClient = velerotest.NewFakeControllerRuntimeClient(t) - sharedInformers = informers.NewSharedInformerFactory(client, 0) - ) - - c := NewBackupSyncController( - client.VeleroV1(), - fakeClient, - client.VeleroV1(), - sharedInformers.Velero().V1().Backups().Lister(), - nil, // csiVSLister - time.Duration(0), - test.namespace, - nil, // csiSnapshotClient - nil, // kubeClient - "", - nil, // new plugin manager func - nil, // backupStoreGetter - velerotest.NewLogger(), - ).(*backupSyncController) - - expectedDeleteActions := make([]core.Action, 0) - - for _, backup := range test.k8sBackups { - // add test backup to informer - require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup), "Error adding backup to informer") - - // add test backup to client - _, err := client.VeleroV1().Backups(test.namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) - require.NoError(t, err, "Error adding backup to clientset") - - // if we expect this backup to be deleted, set up the expected DeleteAction - if test.expectedDeletes.Has(backup.Name) { - actionDelete := core.NewDeleteAction( - velerov1api.SchemeGroupVersion.WithResource("backups"), - test.namespace, - backup.Name, - ) - expectedDeleteActions = append(expectedDeleteActions, actionDelete) - } + bslName := "default" + if test.useLongBSLName { + bslName = longLabelName } + r.deleteOrphanedBackups(ctx, bslName, test.cloudBackups, velerotest.NewLogger()) - c.deleteOrphanedBackups(longLabelName, test.cloudBackups, velerotest.NewLogger()) + numBackups, err := numBackups(client, r.namespace) + Expect(err).ShouldNot(HaveOccurred()) - numBackups, err := numBackups(t, client, c.namespace) - assert.NoError(t, err) + fmt.Println("") expected := len(test.k8sBackups) - len(test.expectedDeletes) - assert.Equal(t, expected, numBackups) + Expect(expected).To(BeEquivalentTo(numBackups)) + } + }) - velerotest.CompareActions(t, expectedDeleteActions, getDeleteActions(client.Actions())) - }) - } -} + It("Test moving default BSL at the head of BSL array.", func() { + locationList := &velerov1api.BackupStorageLocationList{} + objArray := make([]runtime.Object, 0) -func getDeleteActions(actions []core.Action) []core.Action { - var deleteActions []core.Action - for _, action := range actions { - if action.GetVerb() == "delete" { - deleteActions = append(deleteActions, action) + // Generate BSL array. + locations := defaultLocationsList("velero") + for _, bsl := range locations { + objArray = append(objArray, bsl) } - } - return deleteActions -} -func numBackups(t *testing.T, c *fake.Clientset, ns string) (int, error) { - t.Helper() - existingK8SBackups, err := c.VeleroV1().Backups(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return 0, err - } + meta.SetList(locationList, objArray) - return len(existingK8SBackups.Items), nil -} + testObjList := backupSyncSourceOrderFunc(locationList) + testObjArray, err := meta.ExtractList(testObjList) + Expect(err).ShouldNot(HaveOccurred()) -func numPodVolumeBackups(t *testing.T, c *fake.Clientset, ns string) (int, error) { - t.Helper() - existingK8SPodvolumeBackups, err := c.VeleroV1().PodVolumeBackups(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return 0, err - } + expectLocation := testObjArray[0].(*velerov1api.BackupStorageLocation) + Expect(expectLocation.Spec.Default).To(BeEquivalentTo(true)) - return len(existingK8SPodvolumeBackups.Items), nil -} + // If BSL list without default BSL is passed in, the output should be same with input. + locationList.Items = testObjList.(*velerov1api.BackupStorageLocationList).Items[1:] + testObjList = backupSyncSourceOrderFunc(locationList) + Expect(testObjList).To(BeEquivalentTo(locationList)) + + }) +}) diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 60bfb40669..4e61cd25d3 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -73,7 +73,7 @@ func NewGCReconciler( // Other Events will be filtered to decrease the number of reconcile call. Especially UpdateEvent must be filtered since we removed // the backup status as the sub-resource of backup in v1.9, every change on it will be treated as UpdateEvent and trigger reconcile call. func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, c.frequency) + s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, c.frequency, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.Backup{}). WithEventFilter(predicate.Funcs{ diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index d6cd869e3e..55b7fb6967 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -69,7 +69,7 @@ func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client } func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod) + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.BackupRepository{}). Watches(s, nil). diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index e62969b27d..9ca9de1edb 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -65,7 +65,7 @@ func NewScheduleReconciler( } func (c *scheduleReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1.ScheduleList{}, scheduleSyncPeriod) + s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1.ScheduleList{}, scheduleSyncPeriod, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). For(&velerov1.Schedule{}). Watches(s, nil). diff --git a/pkg/util/kube/periodical_enqueue_source.go b/pkg/util/kube/periodical_enqueue_source.go index 20b658c61a..16c4db0ce0 100644 --- a/pkg/util/kube/periodical_enqueue_source.go +++ b/pkg/util/kube/periodical_enqueue_source.go @@ -34,13 +34,18 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" ) -func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, objList client.ObjectList, period time.Duration, filters ...func(object client.Object) bool) *PeriodicalEnqueueSource { +func NewPeriodicalEnqueueSource( + logger logrus.FieldLogger, + client client.Client, + objList client.ObjectList, + period time.Duration, + option PeriodicalEnqueueSourceOption) *PeriodicalEnqueueSource { return &PeriodicalEnqueueSource{ - logger: logger.WithField("resource", reflect.TypeOf(objList).String()), - Client: client, - objList: objList, - period: period, - filterFuncs: filters, + logger: logger.WithField("resource", reflect.TypeOf(objList).String()), + Client: client, + objList: objList, + period: period, + option: option, } } @@ -49,10 +54,15 @@ func NewPeriodicalEnqueueSource(logger logrus.FieldLogger, client client.Client, // the reconcile logic periodically type PeriodicalEnqueueSource struct { client.Client - logger logrus.FieldLogger - objList client.ObjectList - period time.Duration - filterFuncs []func(object client.Object) bool + logger logrus.FieldLogger + objList client.ObjectList + period time.Duration + option PeriodicalEnqueueSourceOption +} + +type PeriodicalEnqueueSourceOption struct { + FilterFuncs []func(object client.Object) bool + OrderFunc func(objList client.ObjectList) client.ObjectList } func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHandler, q workqueue.RateLimitingInterface, pre ...predicate.Predicate) error { @@ -66,13 +76,16 @@ func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHand p.logger.Debug("no resources, skip") return } + if p.option.OrderFunc != nil { + p.objList = p.option.OrderFunc(p.objList) + } if err := meta.EachListItem(p.objList, func(object runtime.Object) error { obj, ok := object.(metav1.Object) if !ok { p.logger.Error("%s's type isn't metav1.Object", object.GetObjectKind().GroupVersionKind().String()) return nil } - for _, filter := range p.filterFuncs { + for _, filter := range p.option.FilterFuncs { if filter != nil { if enqueueObj := filter(object.(client.Object)); !enqueueObj { p.logger.Debugf("skip enqueue object %s/%s due to filter function.", obj.GetNamespace(), obj.GetName()) diff --git a/pkg/util/kube/periodical_enqueue_source_test.go b/pkg/util/kube/periodical_enqueue_source_test.go index 3621533477..e209d6d9eb 100644 --- a/pkg/util/kube/periodical_enqueue_source_test.go +++ b/pkg/util/kube/periodical_enqueue_source_test.go @@ -23,11 +23,14 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/util/workqueue" crclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/vmware-tanzu/velero/internal/storage" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -39,7 +42,7 @@ func TestStart(t *testing.T) { ctx, cancelFunc := context.WithCancel(context.TODO()) client := (&fake.ClientBuilder{}).Build() queue := workqueue.NewRateLimitingQueue(workqueue.DefaultItemBasedRateLimiter()) - source := NewPeriodicalEnqueueSource(logrus.WithContext(ctx), client, &velerov1.ScheduleList{}, 1*time.Second) + source := NewPeriodicalEnqueueSource(logrus.WithContext(ctx), client, &velerov1.ScheduleList{}, 1*time.Second, PeriodicalEnqueueSourceOption{}) require.Nil(t, source.Start(ctx, nil, queue)) @@ -76,9 +79,11 @@ func TestFilter(t *testing.T) { client, &velerov1.BackupStorageLocationList{}, 1*time.Second, - func(object crclient.Object) bool { - location := object.(*velerov1.BackupStorageLocation) - return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, 1*time.Minute, logrus.WithContext(ctx).WithField("BackupStorageLocation", location.Name)) + PeriodicalEnqueueSourceOption{ + FilterFuncs: []func(object crclient.Object) bool{func(object crclient.Object) bool { + location := object.(*velerov1.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, 1*time.Minute, logrus.WithContext(ctx).WithField("BackupStorageLocation", location.Name)) + }}, }, ) @@ -104,3 +109,72 @@ func TestFilter(t *testing.T) { cancelFunc() } + +func TestOrder(t *testing.T) { + require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) + + ctx, cancelFunc := context.WithCancel(context.TODO()) + client := (&fake.ClientBuilder{}).Build() + queue := workqueue.NewRateLimitingQueue(workqueue.DefaultItemBasedRateLimiter()) + source := NewPeriodicalEnqueueSource( + logrus.WithContext(ctx), + client, + &velerov1.BackupStorageLocationList{}, + 1*time.Second, + PeriodicalEnqueueSourceOption{ + OrderFunc: func(objList crclient.ObjectList) crclient.ObjectList { + locationList := &velerov1.BackupStorageLocationList{} + objArray := make([]runtime.Object, 0) + + // Generate BSL array. + locations, _ := meta.ExtractList(objList) + // Move default BSL to tail of array. + objArray = append(objArray, locations[1]) + objArray = append(objArray, locations[0]) + + meta.SetList(locationList, objArray) + + return locationList + }, + }, + ) + + require.Nil(t, source.Start(ctx, nil, queue)) + + // Should not patch a backup storage location object status phase + // if the location's validation frequency is specifically set to zero + require.Nil(t, client.Create(ctx, &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "location1", + Namespace: "default", + }, + Spec: velerov1.BackupStorageLocationSpec{ + ValidationFrequency: &metav1.Duration{Duration: 0}, + }, + Status: velerov1.BackupStorageLocationStatus{ + LastValidationTime: &metav1.Time{Time: time.Now()}, + }, + })) + require.Nil(t, client.Create(ctx, &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "location2", + Namespace: "default", + }, + Spec: velerov1.BackupStorageLocationSpec{ + ValidationFrequency: &metav1.Duration{Duration: 0}, + Default: true, + }, + Status: velerov1.BackupStorageLocationStatus{ + LastValidationTime: &metav1.Time{Time: time.Now()}, + }, + })) + time.Sleep(2 * time.Second) + + first, _ := queue.Get() + bsl := &velerov1.BackupStorageLocation{} + require.Equal(t, "location2", first.(reconcile.Request).Name) + require.Nil(t, client.Get(ctx, first.(reconcile.Request).NamespacedName, bsl)) + require.Equal(t, true, bsl.Spec.Default) + + cancelFunc() +} From eb974687a707ea1ecd69acffa8f2dd93b9dc53ca Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 30 Aug 2022 07:52:11 +0000 Subject: [PATCH 281/366] merge upstream code Signed-off-by: Ming --- pkg/builder/backup_repository_builer.go | 59 ----- .../pod_volume_backup_controller.go | 56 ++--- .../pod_volume_backup_controller_test.go | 41 ++-- .../pod_volume_restore_controller.go | 38 ++-- pkg/repository/util/backup_repo_op.go | 45 ++++ pkg/restic/exec_commands.go | 153 ++++++++++++- pkg/restic/exec_commands_test.go | 6 +- pkg/restic/mocks/fake_restic_executer.go | 37 ---- pkg/uploader/kopia/snapshot.go | 22 +- pkg/uploader/provider/kopia.go | 20 +- pkg/uploader/provider/kopia_test.go | 16 +- pkg/uploader/provider/provider.go | 7 +- pkg/uploader/provider/restic.go | 207 ++++-------------- pkg/uploader/provider/restic_test.go | 60 ++--- 14 files changed, 350 insertions(+), 417 deletions(-) delete mode 100644 pkg/builder/backup_repository_builer.go create mode 100644 pkg/repository/util/backup_repo_op.go delete mode 100644 pkg/restic/mocks/fake_restic_executer.go diff --git a/pkg/builder/backup_repository_builer.go b/pkg/builder/backup_repository_builer.go deleted file mode 100644 index a78f3238af..0000000000 --- a/pkg/builder/backup_repository_builer.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package builder - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" -) - -// BackupRepositoryBuilder builds BackupRepository objects. -type BackupRepositoryBuilder struct { - object *velerov1api.BackupRepository -} - -// ForBackupRepository is the constructor for a BackupRepositoryBuilder. -func ForBackupRepository(ns, name string) *BackupRepositoryBuilder { - return &BackupRepositoryBuilder{ - object: &velerov1api.BackupRepository{ - Spec: velerov1api.BackupRepositorySpec{ResticIdentifier: ""}, - TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), - Kind: "BackupRepository", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - }, - } -} - -// Result returns the built BackupRepository. -func (b *BackupRepositoryBuilder) Result() *velerov1api.BackupRepository { - return b.object -} - -// ObjectMeta applies functional options to the BackupRepository's ObjectMeta. -func (b *BackupRepositoryBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupRepositoryBuilder { - for _, opt := range opts { - opt(b.object) - } - - return b -} diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 97b0993413..4e07edb894 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -19,7 +19,6 @@ package controller import ( "context" "fmt" - "strings" "time" "github.com/pkg/errors" @@ -27,6 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" @@ -34,8 +34,10 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/metrics" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" + "github.com/vmware-tanzu/velero/pkg/repository/util" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -143,30 +145,17 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ }, backupLocation); err != nil { return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } - - // name of ResticRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters - // it could not retrieve the ResticRepository CR with namespace + name. so first list all CRs with in the volumeNamespace - // then filtering the matched CR with prefix volumeNamespace-backupLocation- - backupRepos := &velerov1api.BackupRepositoryList{} - var backupRepo velerov1api.BackupRepository - isFoundRepo := false - if r.Client.List(ctx, backupRepos, &client.ListOptions{ - Namespace: pvb.Namespace, - }); err != nil { + selector := labels.SelectorFromSet( + map[string]string{ + //TODO + //velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), + velerov1api.StorageLocationLabel: label.GetValidName(pvb.Spec.BackupStorageLocation), + //velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), + }, + ) + backupRepo, err := util.GetBackupRepositoryByLabel(ctx, r.Client, pvb.Namespace, selector) + if err != nil { return ctrl.Result{}, errors.Wrap(err, "error getting backup repository") - } else if len(backupRepos.Items) == 0 { - return ctrl.Result{}, errors.Errorf("find empty BackupRepository found for workload namespace %s, backup storage location %s", pvb.Namespace, pvb.Spec.BackupStorageLocation) - } else { - for _, repo := range backupRepos.Items { - if strings.HasPrefix(repo.Name, fmt.Sprintf("%s-%s-", pvb.Spec.Pod.Namespace, pvb.Spec.BackupStorageLocation)) { - backupRepo = repo - isFoundRepo = true - break - } - } - if !isFoundRepo { - return ctrl.Result{}, errors.Errorf("could not found match BackupRepository for workload namespace %s, backup storage location %s", pvb.Namespace, pvb.Spec.BackupStorageLocation) - } } var uploaderProv provider.Provider @@ -177,17 +166,17 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // If this is a PVC, look for the most recent completed pod volume backup for it and get - // its restic snapshot ID to use as the value of the `--parent` flag. Without this, + // its snapshot ID to do new backup based on it. Without this, // if the pod using the PVC (and therefore the directory path under /host_pods/) has - // changed since the PVC's last backup, restic will not be able to identify a suitable + // changed since the PVC's last backup, for backup, it will not be able to identify a suitable // parent snapshot to use, and will have to do a full rescan of the contents of the PVC. var parentSnapshotID string if pvcUID, ok := pvb.Labels[velerov1api.PVCUIDLabel]; ok { parentSnapshotID = r.getParentSnapshot(ctx, log, pvb.Namespace, pvcUID, pvb.Spec.BackupStorageLocation) if parentSnapshotID == "" { - log.Info("No parent snapshot found for PVC, not using --parent flag for this backup") + log.Info("No parent snapshot found for PVC, not based on parent snapshot for this backup") } else { - log.WithField("parentSnapshotID", parentSnapshotID).Info("Setting --parent flag for this backup") + log.WithField("parentSnapshotID", parentSnapshotID).Info("Based on parent snapshot for this backup") } } @@ -197,14 +186,9 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ } }() - var emptySnapshot bool - snapshotID, err := uploaderProv.RunBackup(ctx, path, pvb.Spec.Tags, parentSnapshotID, r.NewBackupProgressUpdater(&pvb, log, ctx)) + snapshotID, emptySnapshot, err := uploaderProv.RunBackup(ctx, path, pvb.Spec.Tags, parentSnapshotID, r.NewBackupProgressUpdater(&pvb, log, ctx)) if err != nil { - if strings.Contains(err.Error(), "snapshot is empty") { - emptySnapshot = true - } else { - return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running Restic backup, stderr=%v", err), log) - } + return r.updateStatusToFailed(ctx, &pvb, err, fmt.Sprintf("running backup, stderr=%v", err), log) } // Update status to Completed with path & snapshot ID. @@ -224,7 +208,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ latencyDuration := pvb.Status.CompletionTimestamp.Time.Sub(pvb.Status.StartTimestamp.Time) latencySeconds := float64(latencyDuration / time.Second) backupName := fmt.Sprintf("%s/%s", req.Namespace, pvb.OwnerReferences[0].Name) - generateOpName := fmt.Sprintf("%s-%s-%s-%s-backup", pvb.Name, backupRepo.Name, pvb.Spec.BackupStorageLocation, pvb.Namespace) + generateOpName := fmt.Sprintf("%s-%s-%s-%s-%s-backup", pvb.Name, backupRepo.Name, pvb.Spec.BackupStorageLocation, pvb.Namespace, pvb.Spec.UploaderType) r.Metrics.ObserveResticOpLatency(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) r.Metrics.RegisterResticOpLatencyGauge(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) r.Metrics.RegisterPodVolumeBackupDequeue(r.NodeName) diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index 794606cb0a..03370f5382 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -71,8 +71,21 @@ func bslBuilder() *builder.BackupStorageLocationBuilder { ForBackupStorageLocation(velerov1api.DefaultNamespace, "bsl-loc") } -func backupRepoBuilder() *builder.BackupRepositoryBuilder { - return builder.ForBackupRepository(velerov1api.DefaultNamespace, fmt.Sprintf("%s-bsl-loc-dn24h", velerov1api.DefaultNamespace)) +func buildBackupRepo() *velerov1api.BackupRepository { + return &velerov1api.BackupRepository{ + Spec: velerov1api.BackupRepositorySpec{ResticIdentifier: ""}, + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "BackupRepository", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: fmt.Sprintf("%s-bsl-loc-dn24h", velerov1api.DefaultNamespace), + Labels: map[string]string{ + velerov1api.StorageLocationLabel: "bsl-loc", + }, + }, + } } var _ = Describe("PodVolumeBackup Reconciler", func() { @@ -176,7 +189,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { pvb: pvbBuilder().Phase("").Node("test_node").Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: true, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -190,7 +203,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: true, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -204,7 +217,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseInProgress). @@ -218,7 +231,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -232,7 +245,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -246,7 +259,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -260,7 +273,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseNew). @@ -274,7 +287,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseInProgress). @@ -288,7 +301,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseCompleted). @@ -302,7 +315,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { Result(), pod: podBuilder().Result(), bsl: bslBuilder().Result(), - backupRepo: backupRepoBuilder().Result(), + backupRepo: buildBackupRepo(), expectedProcessed: false, expected: builder.ForPodVolumeBackup(velerov1api.DefaultNamespace, "pvb-1"). Phase(velerov1api.PodVolumeBackupPhaseFailed). @@ -320,8 +333,8 @@ func (f *fakeProvider) RunBackup( path string, tags map[string]string, parentSnapshot string, - updater uploader.ProgressUpdater) (string, error) { - return "", nil + updater uploader.ProgressUpdater) (string, bool, error) { + return "", false, nil } func (f *fakeProvider) RunRestore( diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index e3e83fb8e4..0302d1e628 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -22,7 +22,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -40,9 +39,10 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/podvolume" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/repository/util" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -250,29 +250,17 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error getting backup storage location") } - // name of ResticRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters - // it could not retrieve the ResticRepository CR with namespace + name. so first list all CRs with in the volumeNamespace - // then filtering the matched CR with prefix volumeNamespace-backupLocation- - backupRepos := &velerov1api.BackupRepositoryList{} - var backupRepo velerov1api.BackupRepository - isFoundRepo := false - if c.List(ctx, backupRepos, &client.ListOptions{ - Namespace: req.Namespace, - }); err != nil { + selector := labels.SelectorFromSet( + map[string]string{ + //TODO + //velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), + velerov1api.StorageLocationLabel: label.GetValidName(req.Spec.BackupStorageLocation), + //velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), + }, + ) + backupRepo, err := util.GetBackupRepositoryByLabel(ctx, c.Client, req.Namespace, selector) + if err != nil { return errors.Wrap(err, "error getting backup repository") - } else if len(backupRepos.Items) == 0 { - return errors.Errorf("find empty BackupRepository found for workload namespace %s, backup storage location %s", req.Namespace, req.Spec.BackupStorageLocation) - } else { - for _, repo := range backupRepos.Items { - if strings.HasPrefix(repo.Name, fmt.Sprintf("%s-%s-", req.Spec.Pod.Namespace, req.Spec.BackupStorageLocation)) { - backupRepo = repo - isFoundRepo = true - break - } - } - if !isFoundRepo { - return errors.Errorf("could not found match BackupRepository for workload namespace %s, backup storage location %s", req.Namespace, req.Spec.BackupStorageLocation) - } } uploaderProv, err := provider.NewUploaderProvider(ctx, c.Client, req.Spec.UploaderType, @@ -288,7 +276,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve }() if err = uploaderProv.RunRestore(ctx, req.Spec.SnapshotID, volumePath, c.NewRestoreProgressUpdater(req, log, ctx)); err != nil { - return errors.Wrapf(err, "error running restic restore err=%v", err) + return errors.Wrapf(err, "error running restore err=%v", err) } // Remove the .velero directory from the restored volume (it may contain done files from previous restores diff --git a/pkg/repository/util/backup_repo_op.go b/pkg/repository/util/backup_repo_op.go new file mode 100644 index 0000000000..0252464f48 --- /dev/null +++ b/pkg/repository/util/backup_repo_op.go @@ -0,0 +1,45 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// GetBackupRepositoryByLabel which find backup repository through pvbNamespace, label +// name of BackupRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters +// it could not retrieve the BackupRepository CR with namespace + name. so first list all CRs with in the pvbNamespace +// then filtering the matched CR by label +func GetBackupRepositoryByLabel(ctx context.Context, cli client.Client, pvbNamespace string, selector labels.Selector) (velerov1api.BackupRepository, error) { + backupRepoList := &velerov1api.BackupRepositoryList{} + if err := cli.List(ctx, backupRepoList, &client.ListOptions{ + Namespace: pvbNamespace, + LabelSelector: selector, + }); err != nil { + return velerov1api.BackupRepository{}, errors.Wrap(err, "error getting backup repository list") + } else if len(backupRepoList.Items) == 1 { + return backupRepoList.Items[0], nil + } else { + return velerov1api.BackupRepository{}, errors.Errorf("unexpectedly find %d BackupRepository for workload namespace %s with label selector %v", len(backupRepoList.Items), pvbNamespace, selector) + } +} diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index 1a4b055bf2..22c1a96659 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -20,10 +20,13 @@ import ( "bytes" "encoding/json" "fmt" + "strings" "time" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -66,7 +69,82 @@ func GetSnapshotID(snapshotIdCmd *Command) (string, error) { return snapshots[0].ShortID, nil } -func DecodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { +// RunBackup runs a `restic backup` command and watches the output to provide +// progress updates to the caller. +func RunBackup(backupCmd *Command, log logrus.FieldLogger, updater uploader.ProgressUpdater) (string, string, error) { + // buffers for copying command stdout/err output into + stdoutBuf := new(bytes.Buffer) + stderrBuf := new(bytes.Buffer) + + // create a channel to signal when to end the goroutine scanning for progress + // updates + quit := make(chan struct{}) + + cmd := backupCmd.Cmd() + cmd.Stdout = stdoutBuf + cmd.Stderr = stderrBuf + + err := cmd.Start() + if err != nil { + return stdoutBuf.String(), stderrBuf.String(), err + } + + go func() { + ticker := time.NewTicker(backupProgressCheckInterval) + for { + select { + case <-ticker.C: + lastLine := getLastLine(stdoutBuf.Bytes()) + if len(lastLine) > 0 { + stat, err := decodeBackupStatusLine(lastLine) + if err != nil { + log.WithError(err).Errorf("error getting restic backup progress") + } + + // if the line contains a non-empty bytes_done field, we can update the + // caller with the progress + if stat.BytesDone != 0 { + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: stat.TotalBytesProcessed, + BytesDone: stat.TotalBytesProcessed, + }) + } + } + case <-quit: + ticker.Stop() + return + } + } + }() + + err = cmd.Wait() + if err != nil { + return stdoutBuf.String(), stderrBuf.String(), err + } + quit <- struct{}{} + + summary, err := getSummaryLine(stdoutBuf.Bytes()) + if err != nil { + return stdoutBuf.String(), stderrBuf.String(), err + } + stat, err := decodeBackupStatusLine(summary) + if err != nil { + return stdoutBuf.String(), stderrBuf.String(), err + } + if stat.MessageType != "summary" { + return stdoutBuf.String(), stderrBuf.String(), errors.WithStack(fmt.Errorf("error getting restic backup summary: %s", string(summary))) + } + + // update progress to 100% + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: stat.TotalBytesProcessed, + BytesDone: stat.TotalBytesProcessed, + }) + + return string(summary), stderrBuf.String(), nil +} + +func decodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { var stat backupStatusLine if err := json.Unmarshal(lastLine, &stat); err != nil { return stat, errors.Wrapf(err, "unable to decode backup JSON line: %s", string(lastLine)) @@ -74,10 +152,10 @@ func DecodeBackupStatusLine(lastLine []byte) (backupStatusLine, error) { return stat, nil } -// GetLastLine returns the last line of a byte array. The string is assumed to +// getLastLine returns the last line of a byte array. The string is assumed to // have a newline at the end of it, so this returns the substring between the // last two newlines. -func GetLastLine(b []byte) []byte { +func getLastLine(b []byte) []byte { if b == nil || len(b) == 0 { return []byte("") } @@ -86,12 +164,12 @@ func GetLastLine(b []byte) []byte { return b[lastNewLineIdx+1 : len(b)-1] } -// GetSummaryLine looks for the summary JSON line +// getSummaryLine looks for the summary JSON line // (`{"message_type:"summary",...`) in the restic backup command output. Due to // an issue in Restic, this might not always be the last line // (https://github.com/restic/restic/issues/2389). It returns an error if it // can't be found. -func GetSummaryLine(b []byte) ([]byte, error) { +func getSummaryLine(b []byte) ([]byte, error) { summaryLineIdx := bytes.LastIndex(b, []byte(`{"message_type":"summary"`)) if summaryLineIdx < 0 { return nil, errors.New("unable to find summary in restic backup command output") @@ -104,7 +182,66 @@ func GetSummaryLine(b []byte) ([]byte, error) { return b[summaryLineIdx : summaryLineIdx+newLineIdx], nil } -func GetSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { +// RunRestore runs a `restic restore` command and monitors the volume size to +// provide progress updates to the caller. +func RunRestore(restoreCmd *Command, log logrus.FieldLogger, updater uploader.ProgressUpdater) (string, string, error) { + insecureTLSFlag := "" + + for _, extraFlag := range restoreCmd.ExtraFlags { + if strings.Contains(extraFlag, resticInsecureTLSFlag) { + insecureTLSFlag = extraFlag + } + } + + snapshotSize, err := getSnapshotSize(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env, insecureTLSFlag) + if err != nil { + return "", "", errors.Wrap(err, "error getting snapshot size") + } + + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + }) + + // create a channel to signal when to end the goroutine scanning for progress + // updates + quit := make(chan struct{}) + + go func() { + ticker := time.NewTicker(restoreProgressCheckInterval) + for { + select { + case <-ticker.C: + volumeSize, err := getVolumeSize(restoreCmd.Dir) + if err != nil { + log.WithError(err).Errorf("error getting restic restore progress") + } + + if volumeSize != 0 { + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + BytesDone: volumeSize, + }) + } + case <-quit: + ticker.Stop() + return + } + } + }() + + stdout, stderr, err := exec.RunCommand(restoreCmd.Cmd()) + quit <- struct{}{} + + // update progress to 100% + updater.UpdateProgress(&uploader.UploaderProgress{ + TotalBytes: snapshotSize, + BytesDone: snapshotSize, + }) + + return stdout, stderr, err +} + +func getSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { cmd := StatsCommand(repoIdentifier, passwordFile, snapshotID) cmd.Env = env cmd.CACertFile = caCertFile @@ -129,7 +266,7 @@ func GetSnapshotSize(repoIdentifier, passwordFile, caCertFile, snapshotID string return snapshotStats.TotalSize, nil } -func GetVolumeSize(path string) (int64, error) { +func getVolumeSize(path string) (int64, error) { var size int64 files, err := fileSystem.ReadDir(path) @@ -139,7 +276,7 @@ func GetVolumeSize(path string) (int64, error) { for _, file := range files { if file.IsDir() { - s, err := GetVolumeSize(fmt.Sprintf("%s/%s", path, file.Name())) + s, err := getVolumeSize(fmt.Sprintf("%s/%s", path, file.Name())) if err != nil { return 0, err } diff --git a/pkg/restic/exec_commands_test.go b/pkg/restic/exec_commands_test.go index e8ca9076fe..0353f3e2c3 100644 --- a/pkg/restic/exec_commands_test.go +++ b/pkg/restic/exec_commands_test.go @@ -52,7 +52,7 @@ func Test_getSummaryLine(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - summary, err := GetSummaryLine([]byte(tt.output)) + summary, err := getSummaryLine([]byte(tt.output)) if tt.wantErr { assert.Error(t, err) } else { @@ -79,7 +79,7 @@ third line } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { - assert.Equal(t, []byte(tt.want), GetLastLine([]byte(tt.output))) + assert.Equal(t, []byte(tt.want), getLastLine([]byte(tt.output))) }) } } @@ -103,7 +103,7 @@ func Test_getVolumeSize(t *testing.T) { fileSystem = fakefs defer func() { fileSystem = filesystem.NewFileSystem() }() - actualSize, err := GetVolumeSize("/") + actualSize, err := getVolumeSize("/") assert.NoError(t, err) assert.Equal(t, expectedSize, actualSize) diff --git a/pkg/restic/mocks/fake_restic_executer.go b/pkg/restic/mocks/fake_restic_executer.go deleted file mode 100644 index 9dcae9574c..0000000000 --- a/pkg/restic/mocks/fake_restic_executer.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Velero Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mocks - -import ( - "github.com/sirupsen/logrus" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/restic" -) - -// FakeResticBackupExec represents an object that can run backups. -type FakeResticBackupExec struct{} - -// RunBackup runs a Restic backup. -func (exec FakeResticBackupExec) RunBackup(cmd *restic.Command, log logrus.FieldLogger, updateFn func(velerov1api.PodVolumeOperationProgress)) (string, string, error) { - return "", "", nil -} - -// GetSnapshotID gets the Restic snapshot ID. -func (exec FakeResticBackupExec) GetSnapshotID(cmd *restic.Command) (string, error) { - return "", nil -} \ No newline at end of file diff --git a/pkg/uploader/kopia/snapshot.go b/pkg/uploader/kopia/snapshot.go index c3ab2d10e0..fc5a71fa7f 100644 --- a/pkg/uploader/kopia/snapshot.go +++ b/pkg/uploader/kopia/snapshot.go @@ -18,6 +18,7 @@ package kopia import ( "context" + "io/ioutil" "math" "os" "path/filepath" @@ -83,13 +84,21 @@ func setupDefaultPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceIn //Backup backup specific sourcePath and update progress func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath string, - parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { + parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) { if fsUploader == nil { - return nil, errors.New("get empty kopia uploader") + return nil, false, errors.New("get empty kopia uploader") } dir, err := filepath.Abs(sourcePath) if err != nil { - return nil, errors.Wrapf(err, "Invalid source path '%s'", sourcePath) + return nil, false, errors.Wrapf(err, "Invalid source path '%s'", sourcePath) + } + + // to be consistent with restic when backup empty dir returns one error for upper logic handle + dirs, err := ioutil.ReadDir(dir) + if err != nil { + return nil, false, errors.Wrapf(err, "Unable to read dir in path %s", dir) + } else if len(dirs) == 0 { + return nil, true, nil } sourceInfo := snapshot.SourceInfo{ @@ -97,14 +106,13 @@ func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter rep Host: udmrepo.GetRepoDomain(), Path: filepath.Clean(dir), } - rootDir, err := getLocalFSEntry(sourceInfo.Path) if err != nil { - return nil, errors.Wrap(err, "Unable to get local filesystem entry") + return nil, false, errors.Wrap(err, "Unable to get local filesystem entry") } snapID, snapshotSize, err := SnapshotSource(ctx, repoWriter, fsUploader, sourceInfo, rootDir, parentSnapshot, log, "Kopia Uploader") if err != nil { - return nil, err + return nil, false, err } snapshotInfo := &uploader.SnapshotInfo{ @@ -112,7 +120,7 @@ func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter rep Size: snapshotSize, } - return snapshotInfo, nil + return snapshotInfo, false, nil } func getLocalFSEntry(path0 string) (fs.Entry, error) { diff --git a/pkg/uploader/provider/kopia.go b/pkg/uploader/provider/kopia.go index 73798d9ac3..9890a8a91b 100644 --- a/pkg/uploader/provider/kopia.go +++ b/pkg/uploader/provider/kopia.go @@ -101,15 +101,16 @@ func (kp *kopiaProvider) Close(ctx context.Context) error { return kp.bkRepo.Close(ctx) } -//RunBackup which will backup specific path and update backup progress +// RunBackup which will backup specific path and update backup progress +// return snapshotID, isEmptySnapshot, error func (kp *kopiaProvider) RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updater uploader.ProgressUpdater) (string, error) { + updater uploader.ProgressUpdater) (string, bool, error) { if updater == nil { - return "", errors.New("Need to initial backup progress updater first") + return "", false, errors.New("Need to initial backup progress updater first") } log := kp.log.WithFields(logrus.Fields{ @@ -130,13 +131,16 @@ func (kp *kopiaProvider) RunBackup( close(quit) }() - snapshotInfo, err := BackupFunc(ctx, kpUploader, repoWriter, path, parentSnapshot, log) - + snapshotInfo, isSnapshotEmpty, err := BackupFunc(ctx, kpUploader, repoWriter, path, parentSnapshot, log) if err != nil { - return "", errors.Wrapf(err, "Failed to run kopia backup") + return "", false, errors.Wrapf(err, "Failed to run kopia backup") + } else if isSnapshotEmpty { + log.Debugf("Kopia backup got empty dir with path %s", path) + return "", true, nil } else if snapshotInfo == nil { - return "", fmt.Errorf("failed to get kopia backup snapshot info for path %v", path) + return "", false, fmt.Errorf("failed to get kopia backup snapshot info for path %v", path) } + // which ensure that the statistic data of TotalBytes equal to BytesDone when finished updater.UpdateProgress( &uploader.UploaderProgress{ @@ -146,7 +150,7 @@ func (kp *kopiaProvider) RunBackup( ) log.Debugf("Kopia backup finished, snapshot ID %s, backup size %d", snapshotInfo.ID, snapshotInfo.Size) - return snapshotInfo.ID, nil + return snapshotInfo.ID, false, nil } func (kp *kopiaProvider) GetPassword(param interface{}) (string, error) { diff --git a/pkg/uploader/provider/kopia_test.go b/pkg/uploader/provider/kopia_test.go index ac061d6c74..955bf83f42 100644 --- a/pkg/uploader/provider/kopia_test.go +++ b/pkg/uploader/provider/kopia_test.go @@ -40,27 +40,27 @@ func TestRunBackup(t *testing.T) { updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: kp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { name string - hookBackupFunc func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) + hookBackupFunc func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) notError bool }{ { name: "success to backup", - hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { - return &uploader.SnapshotInfo{}, nil + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) { + return &uploader.SnapshotInfo{}, false, nil }, notError: true, }, { name: "get error to backup", - hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { - return &uploader.SnapshotInfo{}, errors.New("failed to backup") + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) { + return &uploader.SnapshotInfo{}, false, errors.New("failed to backup") }, notError: false, }, { name: "got empty snapshot", - hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, error) { - return nil, nil + hookBackupFunc: func(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath, parentSnapshot string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) { + return nil, true, errors.New("snapshot is empty") }, notError: false, }, @@ -68,7 +68,7 @@ func TestRunBackup(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { BackupFunc = tc.hookBackupFunc - _, err := kp.RunBackup(context.Background(), "var", nil, "", &updater) + _, _, err := kp.RunBackup(context.Background(), "var", nil, "", &updater) if tc.notError { assert.NoError(t, err) } else { diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index cac5427986..1a74214f12 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -38,14 +38,14 @@ const backupProgressCheckInterval = 10 * time.Second // Provider which is designed for one pod volumn to do the backup or restore type Provider interface { - // RunBackup which will do backup for one specific volumn and return snapshotID error + // RunBackup which will do backup for one specific volumn and return snapshotID, isSnapshotEmpty, error // updater is used for updating backup progress which implement by third-party RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updater uploader.ProgressUpdater) (string, error) + updater uploader.ProgressUpdater) (string, bool, error) // RunRestore which will do restore for one specific volumn with given snapshot id and return error // updater is used for updating backup progress which implement by third-party RunRestore( @@ -78,8 +78,7 @@ func NewUploaderProvider( } return NewKopiaUploaderProvider(ctx, credGetter, backupRepo, log) } else { - err := provider.NewResticRepositoryProvider(credGetter.FromFile, nil, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}) - if err != nil { + if err := provider.NewResticRepositoryProvider(credGetter.FromFile, nil, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { return nil, errors.Wrap(err, "failed to connect repository") } return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go index 1f9e18ebaa..549d395778 100644 --- a/pkg/uploader/provider/restic.go +++ b/pkg/uploader/provider/restic.go @@ -17,11 +17,10 @@ limitations under the License. package provider import ( - "bytes" "context" "fmt" "os" - "time" + "strings" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -31,25 +30,21 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/uploader" - "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) -// BackupFunc mainly used to make testing more convenient -var ResticBackupFunc = restic.BackupCommand -var ResticRunCMDFunc = exec.RunCommand -var ResticGetSnapshotSizeFunc = restic.GetSnapshotSize -var ResticGetVolumeSizeFunc = restic.GetVolumeSize -var ResticRestoreFunc = restic.RestoreCommand +// mainly used to make testing more convenient +var ResticBackupCMDFunc = restic.BackupCommand +var ResticRestoreCMDFunc = restic.RestoreCommand type resticProvider struct { repoIdentifier string credentialsFile string caCertFile string - repoEnv []string - backupTags map[string]string - log logrus.FieldLogger + cmdEnv []string + extraFlags []string bsl *velerov1api.BackupStorageLocation + log logrus.FieldLogger } func NewResticUploaderProvider( @@ -61,8 +56,8 @@ func NewResticUploaderProvider( ) (Provider, error) { provider := resticProvider{ repoIdentifier: repoIdentifier, - log: log, bsl: bsl, + log: log, } var err error @@ -79,38 +74,42 @@ func NewResticUploaderProvider( } } - provider.repoEnv, err = restic.CmdEnv(bsl, credGetter.FromFile) + provider.cmdEnv, err = restic.CmdEnv(bsl, credGetter.FromFile) if err != nil { return nil, errors.Wrap(err, "error generating repository cmnd env") } - return &provider, nil -} + // #4820: restrieve insecureSkipTLSVerify from BSL configuration for + // AWS plugin. If nothing is return, that means insecureSkipTLSVerify + // is not enable for Restic command. + skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(bsl, log) + if len(skipTLSRet) > 0 { + provider.extraFlags = append(provider.extraFlags, skipTLSRet) + } -// Not implement yet -func (rp *resticProvider) Cancel() { + return &provider, nil } func (rp *resticProvider) Close(ctx context.Context) error { if err := os.Remove(rp.credentialsFile); err != nil { - return errors.Wrapf(err, "failed to remove file %s", rp.credentialsFile) + rp.log.Warnf("Failed to remove file %s with err %v", rp.credentialsFile, err) } if err := os.Remove(rp.caCertFile); err != nil { - return errors.Wrapf(err, "failed to remove file %s", rp.caCertFile) + rp.log.Warnf("Failed to remove file %s with err %v", rp.caCertFile, err) } return nil } // RunBackup runs a `backup` command and watches the output to provide -// progress updates to the caller. +// progress updates to the caller and return snapshotID, isEmptySnapshot, error func (rp *resticProvider) RunBackup( ctx context.Context, path string, tags map[string]string, parentSnapshot string, - updater uploader.ProgressUpdater) (string, error) { + updater uploader.ProgressUpdater) (string, bool, error) { if updater == nil { - return "", errors.New("Need to initial backup progress updater first") + return "", false, errors.New("Need to initial backup progress updater first") } log := rp.log.WithFields(logrus.Fields{ @@ -118,104 +117,33 @@ func (rp *resticProvider) RunBackup( "parentSnapshot": parentSnapshot, }) - // buffers for copying command stdout/err output into - stdoutBuf := new(bytes.Buffer) - stderrBuf := new(bytes.Buffer) - - // create a channel to signal when to end the goroutine scanning for progress - // updates - quit := make(chan struct{}) - - rp.backupTags = tags - backupCmd := ResticBackupFunc(rp.repoIdentifier, rp.credentialsFile, path, tags) - backupCmd.Env = rp.repoEnv + backupCmd := ResticBackupCMDFunc(rp.repoIdentifier, rp.credentialsFile, path, tags) + backupCmd.Env = rp.cmdEnv backupCmd.CACertFile = rp.caCertFile - + backupCmd.ExtraFlags = rp.extraFlags if parentSnapshot != "" { backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, fmt.Sprintf("--parent=%s", parentSnapshot)) } - // #4820: restrieve insecureSkipTLSVerify from BSL configuration for - // AWS plugin. If nothing is return, that means insecureSkipTLSVerify - // is not enable for Restic command. - skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(rp.bsl, rp.log) - if len(skipTLSRet) > 0 { - backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, skipTLSRet) - } - - cmd := backupCmd.Cmd() - cmd.Stdout = stdoutBuf - cmd.Stderr = stderrBuf - - err := cmd.Start() + summary, stderrBuf, err := restic.RunBackup(backupCmd, log, updater) if err != nil { - log.Errorf("failed to execute %v with stderr %v error %v", cmd, stderrBuf.String(), err) - return "", err - } - - go func() { - ticker := time.NewTicker(backupProgressCheckInterval) - for { - select { - case <-ticker.C: - lastLine := restic.GetLastLine(stdoutBuf.Bytes()) - if len(lastLine) > 0 { - stat, err := restic.DecodeBackupStatusLine(lastLine) - if err != nil { - rp.log.WithError(err).Errorf("error getting backup progress") - } - - // if the line contains a non-empty bytes_done field, we can update the - // caller with the progress - if stat.BytesDone != 0 { - updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: stat.TotalBytesProcessed, - BytesDone: stat.TotalBytesProcessed, - }) - } - } - case <-quit: - ticker.Stop() - return - } + if strings.Contains(err.Error(), "snapshot is empty") { + log.Debugf("Restic backup got empty dir with %s path", path) + return "", true, nil } - }() - - err = cmd.Wait() - if err != nil { - return "", errors.WithStack(fmt.Errorf("failed to wait execute %v with stderr %v error %v", cmd, stderrBuf.String(), err)) + return "", false, errors.WithStack(fmt.Errorf("error running restic backup with error: %v", err)) } - quit <- struct{}{} - - summary, err := restic.GetSummaryLine(stdoutBuf.Bytes()) - if err != nil { - return "", errors.WithStack(fmt.Errorf("failed to get summary %v with error %v", stderrBuf.String(), err)) - } - stat, err := restic.DecodeBackupStatusLine(summary) - if err != nil { - return "", errors.WithStack(fmt.Errorf("failed to decode summary %v with error %v", string(summary), err)) - } - if stat.MessageType != "summary" { - return "", errors.WithStack(fmt.Errorf("error getting backup summary: %s", string(summary))) - } - - // update progress to 100% - updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: stat.TotalBytesProcessed, - BytesDone: stat.TotalBytesProcessed, - }) - - //GetSnapshtotID - snapshotIdCmd := restic.GetSnapshotCommand(rp.repoIdentifier, rp.credentialsFile, rp.backupTags) - snapshotIdCmd.Env = rp.repoEnv + // GetSnapshotID + snapshotIdCmd := restic.GetSnapshotCommand(rp.repoIdentifier, rp.credentialsFile, tags) + snapshotIdCmd.Env = rp.cmdEnv snapshotIdCmd.CACertFile = rp.caCertFile snapshotID, err := restic.GetSnapshotID(snapshotIdCmd) if err != nil { - return "", errors.WithStack(fmt.Errorf("error getting snapshot id with error: %s", err)) + return "", false, errors.WithStack(fmt.Errorf("error getting snapshot id with error: %v", err)) } - log.Debugf("Ran command=%s, stdout=%s, stderr=%s", cmd.String(), summary, stderrBuf.String()) - return snapshotID, nil + log.Debugf("Run command=%s, stdout=%s, stderr=%s", backupCmd.String(), summary, stderrBuf) + return snapshotID, false, nil } // RunRestore runs a `restore` command and monitors the volume size to @@ -225,68 +153,21 @@ func (rp *resticProvider) RunRestore( snapshotID string, volumePath string, updater uploader.ProgressUpdater) error { + if updater == nil { + return errors.New("Need to initial backup progress updater first") + } log := rp.log.WithFields(logrus.Fields{ "snapshotID": snapshotID, "volumePath": volumePath, }) - restoreCmd := ResticRestoreFunc(rp.repoIdentifier, rp.credentialsFile, snapshotID, volumePath) - restoreCmd.Env = rp.repoEnv + restoreCmd := ResticRestoreCMDFunc(rp.repoIdentifier, rp.credentialsFile, snapshotID, volumePath) + restoreCmd.Env = rp.cmdEnv restoreCmd.CACertFile = rp.caCertFile + restoreCmd.ExtraFlags = rp.extraFlags - // #4820: restrieve insecureSkipTLSVerify from BSL configuration for - // AWS plugin. If nothing is return, that means insecureSkipTLSVerify - // is not enable for Restic command. - skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(rp.bsl, log) - if len(skipTLSRet) > 0 { - restoreCmd.ExtraFlags = append(restoreCmd.ExtraFlags, skipTLSRet) - } - snapshotSize, err := ResticGetSnapshotSizeFunc(restoreCmd.RepoIdentifier, restoreCmd.PasswordFile, restoreCmd.CACertFile, restoreCmd.Args[0], restoreCmd.Env, skipTLSRet) - if err != nil { - return errors.Wrap(err, "error getting snapshot size") - } - - updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: snapshotSize, - }) + stdout, stderr, err := restic.RunRestore(restoreCmd, log, updater) - // create a channel to signal when to end the goroutine scanning for progress - // updates - quit := make(chan struct{}) - - go func() { - ticker := time.NewTicker(restoreProgressCheckInterval) - for { - select { - case <-ticker.C: - volumeSize, err := ResticGetVolumeSizeFunc(restoreCmd.Dir) - if err != nil { - log.WithError(err).Errorf("error getting volume size for restore dir %v", restoreCmd.Dir) - return - } - if volumeSize != 0 { - updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: snapshotSize, - BytesDone: volumeSize, - }) - } - case <-quit: - ticker.Stop() - return - } - } - }() - - stdout, stderr, err := ResticRunCMDFunc(restoreCmd.Cmd()) - quit <- struct{}{} - if err != nil { - return errors.Wrap(err, fmt.Sprintf("failed to execute restore command %v with stderr %v", restoreCmd.Cmd().String(), stderr)) - } - // update progress to 100% - updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: snapshotSize, - BytesDone: snapshotSize, - }) - log.Debugf("Ran command=%s, stdout=%s, stderr=%s", restoreCmd.Command, stdout, stderr) + log.Debugf("Run command=%s, stdout=%s, stderr=%s", restoreCmd.Command, stdout, stderr) return err } diff --git a/pkg/uploader/provider/restic_test.go b/pkg/uploader/provider/restic_test.go index 22b44c2acb..042602777a 100644 --- a/pkg/uploader/provider/restic_test.go +++ b/pkg/uploader/provider/restic_test.go @@ -18,7 +18,6 @@ package provider import ( "context" - "errors" "strings" "testing" @@ -29,6 +28,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/uploader" ) func TestResticRunBackup(t *testing.T) { @@ -36,9 +36,10 @@ func TestResticRunBackup(t *testing.T) { rp.log = logrus.New() updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: rp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} testCases := []struct { - name string - hookBackupFunc func(repoIdentifier string, passwordFile string, path string, tags map[string]string) *restic.Command - errorHandleFunc func(err error) bool + name string + hookBackupFunc func(repoIdentifier string, passwordFile string, path string, tags map[string]string) *restic.Command + hookRunBackupFunc func(backupCmd *restic.Command, log logrus.FieldLogger, updater uploader.ProgressUpdater) (string, string, error) + errorHandleFunc func(err error) bool }{ { name: "wrong restic execute command", @@ -62,71 +63,40 @@ func TestResticRunBackup(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - ResticBackupFunc = tc.hookBackupFunc - _, err := rp.RunBackup(context.Background(), "var", nil, "", &updater) + ResticBackupCMDFunc = tc.hookBackupFunc + _, _, err := rp.RunBackup(context.Background(), "var", nil, "", &updater) rp.log.Infof("test name %v error %v", tc.name, err) require.Equal(t, true, tc.errorHandleFunc(err)) }) } } + func TestResticRunRestore(t *testing.T) { var rp resticProvider rp.log = logrus.New() updater := FakeBackupProgressUpdater{PodVolumeBackup: &velerov1api.PodVolumeBackup{}, Log: rp.log, Ctx: context.Background(), Cli: fake.NewFakeClientWithScheme(scheme.Scheme)} - ResticRestoreFunc = func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { + ResticRestoreCMDFunc = func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { return &restic.Command{Args: []string{""}} } testCases := []struct { - name string - hookResticGetVolumeSizeFunc func(path string) (int64, error) - hookResticGetSnapshotSizeFunc func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) - hookResticRestoreFunc func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command - errorHandleFunc func(err error) bool + name string + hookResticRestoreFunc func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command + errorHandleFunc func(err error) bool }{ { - name: "failed to get snapshot", - hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, nil }, - hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { - return 100, errors.New("failed to get snapshot") - }, - hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { - return &restic.Command{Args: []string{""}} - }, - errorHandleFunc: func(err error) bool { return strings.Contains(err.Error(), "failed to get snapshot") }, - }, - { - name: "failed to get volume size", - hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, errors.New("failed to get volume size") }, - hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { - return 100, nil - }, - hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { - return &restic.Command{Args: []string{""}} - }, - errorHandleFunc: func(err error) bool { - return strings.Contains(err.Error(), "failed to execute restore command restic") - }, - }, - { - name: "wrong restic execute command", - hookResticGetVolumeSizeFunc: func(path string) (int64, error) { return 100, nil }, - hookResticGetSnapshotSizeFunc: func(repoIdentifier, passwordFile, caCertFile, snapshotID string, env []string, insecureTLS string) (int64, error) { - return 100, nil - }, + name: "wrong restic execute command", hookResticRestoreFunc: func(repoIdentifier, passwordFile, snapshotID, target string) *restic.Command { return &restic.Command{Args: []string{"date"}} }, errorHandleFunc: func(err error) bool { - return strings.Contains(err.Error(), "failed to execute restore command restic") + return strings.Contains(err.Error(), "executable file not found ") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - ResticGetSnapshotSizeFunc = tc.hookResticGetSnapshotSizeFunc - ResticGetVolumeSizeFunc = tc.hookResticGetVolumeSizeFunc - ResticRestoreFunc = tc.hookResticRestoreFunc + ResticRestoreCMDFunc = tc.hookResticRestoreFunc err := rp.RunRestore(context.Background(), "", "var", &updater) rp.log.Infof("test name %v error %v", tc.name, err) require.Equal(t, true, tc.errorHandleFunc(err)) From 0282e65221d59a179e13cc8b8553bb1c888a0e3a Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:29:30 +0800 Subject: [PATCH 282/366] Kopia Pod Volume Backup/Restore (#5259) * kopia pvbr Signed-off-by: Lyndon-Li --- changelogs/unreleased/5259-lyndon | 1 + pkg/apis/velero/v1/backup_repository_types.go | 4 +- pkg/apis/velero/v1/labels_annotations.go | 11 ++- pkg/backup/backup.go | 5 +- pkg/backup/backup_test.go | 2 +- pkg/cmd/server/server.go | 50 +++++----- pkg/controller/backup_deletion_controller.go | 16 +--- .../backup_deletion_controller_test.go | 3 + .../restic_repository_controller.go | 44 +++++---- .../restic_repository_controller_test.go | 65 +++++++++++-- pkg/podvolume/backupper.go | 14 ++- pkg/podvolume/backupper_factory.go | 6 +- pkg/podvolume/restorer.go | 43 ++++++++- pkg/podvolume/restorer_test.go | 92 +++++++++++++++++++ pkg/podvolume/util.go | 89 +++++++++++++++++- pkg/repository/ensurer.go | 36 +++++--- pkg/repository/manager.go | 30 +++++- pkg/repository/manager_test.go | 2 +- pkg/repository/mocks/repository_manager.go | 23 +++++ 19 files changed, 441 insertions(+), 95 deletions(-) create mode 100644 changelogs/unreleased/5259-lyndon create mode 100644 pkg/podvolume/restorer_test.go diff --git a/changelogs/unreleased/5259-lyndon b/changelogs/unreleased/5259-lyndon new file mode 100644 index 0000000000..c56e2a3ef9 --- /dev/null +++ b/changelogs/unreleased/5259-lyndon @@ -0,0 +1 @@ +Fill gaps for Kopia path of PVBR: integrate Repo Manager with Unified Repo; pass UploaderType to PVBR backupper and restorer; pass RepositoryType to BackupRepository controller and Repo Ensurer \ No newline at end of file diff --git a/pkg/apis/velero/v1/backup_repository_types.go b/pkg/apis/velero/v1/backup_repository_types.go index a64e3be689..6a062c4fee 100644 --- a/pkg/apis/velero/v1/backup_repository_types.go +++ b/pkg/apis/velero/v1/backup_repository_types.go @@ -52,8 +52,8 @@ const ( BackupRepositoryPhaseReady BackupRepositoryPhase = "Ready" BackupRepositoryPhaseNotReady BackupRepositoryPhase = "NotReady" - BackupRepositoryTypeRestic string = "restic" - BackupRepositoryTypeUnified string = "unified" + BackupRepositoryTypeRestic string = "restic" + BackupRepositoryTypeKopia string = "kopia" ) // BackupRepositoryStatus is the current status of a BackupRepository. diff --git a/pkg/apis/velero/v1/labels_annotations.go b/pkg/apis/velero/v1/labels_annotations.go index 172b436a83..64c83525a4 100644 --- a/pkg/apis/velero/v1/labels_annotations.go +++ b/pkg/apis/velero/v1/labels_annotations.go @@ -40,16 +40,19 @@ const ( // PodVolumeOperationTimeoutAnnotation is the annotation key used to apply // a backup/restore-specific timeout value for pod volume operations (i.e. - // restic backups/restores). + // pod volume backups/restores). PodVolumeOperationTimeoutAnnotation = "velero.io/pod-volume-timeout" // StorageLocationLabel is the label key used to identify the storage // location of a backup. StorageLocationLabel = "velero.io/storage-location" - // ResticVolumeNamespaceLabel is the label key used to identify which - // namespace a restic repository stores pod volume backups for. - ResticVolumeNamespaceLabel = "velero.io/volume-namespace" + // VolumeNamespaceLabel is the label key used to identify which + // namespace a repository stores backups for. + VolumeNamespaceLabel = "velero.io/volume-namespace" + + // RepositoryTypeLabel is the label key used to identify the type of a repository + RepositoryTypeLabel = "velero.io/repository-type" // SourceClusterK8sVersionAnnotation is the label key used to identify the k8s // git version of the backup , i.e. v1.16.4 diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index c613cba521..771ee5ee15 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -79,6 +79,7 @@ type kubernetesBackupper struct { resticTimeout time.Duration defaultVolumesToRestic bool clientPageSize int + uploaderType string } func (i *itemKey) String() string { @@ -105,6 +106,7 @@ func NewKubernetesBackupper( resticTimeout time.Duration, defaultVolumesToRestic bool, clientPageSize int, + uploaderType string, ) (Backupper, error) { return &kubernetesBackupper{ backupClient: backupClient, @@ -115,6 +117,7 @@ func NewKubernetesBackupper( resticTimeout: resticTimeout, defaultVolumesToRestic: defaultVolumesToRestic, clientPageSize: clientPageSize, + uploaderType: uploaderType, }, nil } @@ -240,7 +243,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, var resticBackupper podvolume.Backupper if kb.resticBackupperFactory != nil { - resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup) + resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup, kb.uploaderType) if err != nil { log.WithError(errors.WithStack(err)).Debugf("Error from NewBackupper") return errors.WithStack(err) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 42257290a7..e218d2636a 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2596,7 +2596,7 @@ func TestBackupWithHooks(t *testing.T) { type fakeResticBackupperFactory struct{} -func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup) (podvolume.Backupper, error) { +func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup, string) (podvolume.Backupper, error) { return &fakeResticBackupper{}, nil } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 5d739e3cb7..2253d9dbc6 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -131,7 +131,7 @@ type serverConfig struct { clientPageSize int profilerAddress string formatFlag *logging.FormatFlag - defaultResticMaintenanceFrequency time.Duration + repoMaintenanceFrequency time.Duration garbageCollectionFrequency time.Duration defaultVolumesToRestic bool uploaderType string @@ -147,25 +147,24 @@ func NewCommand(f client.Factory) *cobra.Command { volumeSnapshotLocations = flag.NewMap().WithKeyValueDelimiter(':') logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel) config = serverConfig{ - pluginDir: "/plugins", - metricsAddress: defaultMetricsAddress, - defaultBackupLocation: "default", - defaultVolumeSnapshotLocations: make(map[string]string), - backupSyncPeriod: defaultBackupSyncPeriod, - defaultBackupTTL: defaultBackupTTL, - defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, - storeValidationFrequency: defaultStoreValidationFrequency, - podVolumeOperationTimeout: defaultPodVolumeOperationTimeout, - restoreResourcePriorities: defaultRestorePriorities, - clientQPS: defaultClientQPS, - clientBurst: defaultClientBurst, - clientPageSize: defaultClientPageSize, - profilerAddress: defaultProfilerAddress, - resourceTerminatingTimeout: defaultResourceTerminatingTimeout, - formatFlag: logging.NewFormatFlag(), - defaultResticMaintenanceFrequency: restic.DefaultMaintenanceFrequency, - defaultVolumesToRestic: restic.DefaultVolumesToRestic, - uploaderType: uploader.ResticType, + pluginDir: "/plugins", + metricsAddress: defaultMetricsAddress, + defaultBackupLocation: "default", + defaultVolumeSnapshotLocations: make(map[string]string), + backupSyncPeriod: defaultBackupSyncPeriod, + defaultBackupTTL: defaultBackupTTL, + defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, + storeValidationFrequency: defaultStoreValidationFrequency, + podVolumeOperationTimeout: defaultPodVolumeOperationTimeout, + restoreResourcePriorities: defaultRestorePriorities, + clientQPS: defaultClientQPS, + clientBurst: defaultClientBurst, + clientPageSize: defaultClientPageSize, + profilerAddress: defaultProfilerAddress, + resourceTerminatingTimeout: defaultResourceTerminatingTimeout, + formatFlag: logging.NewFormatFlag(), + defaultVolumesToRestic: restic.DefaultVolumesToRestic, + uploaderType: uploader.ResticType, } ) @@ -228,7 +227,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().StringVar(&config.profilerAddress, "profiler-address", config.profilerAddress, "The address to expose the pprof profiler.") command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "How long to wait on persistent volumes and namespaces to terminate during a restore before timing out.") command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "How long to wait by default before backups can be garbage collected.") - command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default.") + command.Flags().DurationVar(&config.repoMaintenanceFrequency, "default-restic-prune-frequency", config.repoMaintenanceFrequency, "How often 'prune' is run for backup repositories by default.") command.Flags().DurationVar(&config.garbageCollectionFrequency, "garbage-collection-frequency", config.garbageCollectionFrequency, "How often garbage collection is run for expired backups.") command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "Backup all volumes with restic by default.") command.Flags().StringVar(&config.uploaderType, "uploader-type", config.uploaderType, "Type of uploader to handle the transfer of data of pod volumes") @@ -260,6 +259,7 @@ type server struct { config serverConfig mgr manager.Manager credentialFileStore credentials.FileStore + credentialSecretStore credentials.SecretStore } func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) { @@ -349,6 +349,8 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s return nil, err } + credentialSecretStore, err := credentials.NewNamespacedSecretStore(mgr.GetClient(), f.Namespace()) + s := &server{ namespace: f.Namespace(), metricsAddress: config.metricsAddress, @@ -368,6 +370,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s config: config, mgr: mgr, credentialFileStore: credentialFileStore, + credentialSecretStore: credentialSecretStore, } return s, nil @@ -546,7 +549,7 @@ func (s *server) initRestic() error { s.repoLocker = repository.NewRepoLocker() s.repoEnsurer = repository.NewRepositoryEnsurer(s.sharedInformerFactory.Velero().V1().BackupRepositories(), s.veleroClient.VeleroV1(), s.logger) - s.repoManager = repository.NewManager(s.namespace, s.mgr.GetClient(), s.repoLocker, s.repoEnsurer, s.credentialFileStore, s.logger) + s.repoManager = repository.NewManager(s.namespace, s.mgr.GetClient(), s.repoLocker, s.repoEnsurer, s.credentialFileStore, s.credentialSecretStore, s.logger) return nil } @@ -620,6 +623,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.config.podVolumeOperationTimeout, s.config.defaultVolumesToRestic, s.config.clientPageSize, + s.config.uploaderType, ) cmd.CheckError(err) @@ -781,7 +785,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } if _, ok := enabledRuntimeControllers[controller.ResticRepo]; ok { - if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.defaultResticMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { + if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.repoMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) } } diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index db26290955..4144244041 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -45,6 +45,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/kube" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/podvolume" ) const ( @@ -506,17 +508,5 @@ func getSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbCli return nil, errors.WithStack(err) } - var res []repository.SnapshotIdentifier - for _, item := range podVolumeBackups.Items { - if item.Status.SnapshotID == "" { - continue - } - res = append(res, repository.SnapshotIdentifier{ - VolumeNamespace: item.Spec.Pod.Namespace, - BackupStorageLocation: backup.Spec.StorageLocation, - SnapshotID: item.Status.SnapshotID, - }) - } - - return res, nil + return podvolume.GetSnapshotIdentifier(podVolumeBackups), nil } diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 79833958f5..8604c90b83 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -771,10 +771,12 @@ func TestGetSnapshotsInBackup(t *testing.T) { { VolumeNamespace: "ns-1", SnapshotID: "snap-3", + RepositoryType: "restic", }, { VolumeNamespace: "ns-1", SnapshotID: "snap-4", + RepositoryType: "restic", }, }, }, @@ -822,6 +824,7 @@ func TestGetSnapshotsInBackup(t *testing.T) { { VolumeNamespace: "ns-1", SnapshotID: "snap-3", + RepositoryType: "restic", }, }, }, diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 55b7fb6967..39ec50c37f 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -32,39 +32,34 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository" repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) const ( - repoSyncPeriod = 5 * time.Minute + repoSyncPeriod = 5 * time.Minute + defaultMaintainFrequency = 7 * 24 * time.Hour ) type ResticRepoReconciler struct { client.Client - namespace string - logger logrus.FieldLogger - clock clock.Clock - defaultMaintenanceFrequency time.Duration - repositoryManager repository.Manager + namespace string + logger logrus.FieldLogger + clock clock.Clock + maintenanceFrequency time.Duration + repositoryManager repository.Manager } func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client client.Client, - defaultMaintenanceFrequency time.Duration, repositoryManager repository.Manager) *ResticRepoReconciler { + maintenanceFrequency time.Duration, repositoryManager repository.Manager) *ResticRepoReconciler { c := &ResticRepoReconciler{ client, namespace, logger, clock.RealClock{}, - defaultMaintenanceFrequency, + maintenanceFrequency, repositoryManager, } - if c.defaultMaintenanceFrequency <= 0 { - logger.Infof("Invalid default restic maintenance frequency, setting to %v", restic.DefaultMaintenanceFrequency) - c.defaultMaintenanceFrequency = restic.DefaultMaintenanceFrequency - } - return c } @@ -135,7 +130,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady if rr.Spec.MaintenanceFrequency.Duration <= 0 { - rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} + rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)} } }) } @@ -145,7 +140,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 rr.Spec.ResticIdentifier = repoIdentifier if rr.Spec.MaintenanceFrequency.Duration <= 0 { - rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} + rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)} } }); err != nil { return err @@ -161,6 +156,23 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 }) } +func (r *ResticRepoReconciler) getRepositoryMaintenanceFrequency(req *velerov1api.BackupRepository) time.Duration { + if r.maintenanceFrequency > 0 { + r.logger.WithField("frequency", r.maintenanceFrequency).Info("Set user defined maintenance frequency") + return r.maintenanceFrequency + } else { + frequency, err := r.repositoryManager.DefaultMaintenanceFrequency(req) + if err != nil || frequency <= 0 { + r.logger.WithError(err).WithField("returned frequency", frequency).Warn("Failed to get maitanance frequency, use the default one") + frequency = defaultMaintainFrequency + } else { + r.logger.WithField("frequency", frequency).Info("Set matainenance according to repository suggestion") + } + + return frequency + } +} + // ensureRepo checks to see if a repository exists, and attempts to initialize it if // it does not exist. An error is returned if the repository can't be connected to // or initialized. diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index d693f510be..323c547100 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -15,20 +15,23 @@ package controller import ( "context" + "errors" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" repomokes "github.com/vmware-tanzu/velero/pkg/repository/mocks" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -const defaultMaintenanceFrequency = 10 * time.Minute +const testMaintenanceFrequency = 10 * time.Minute func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { mgr := &repomokes.RepositoryManager{} @@ -39,7 +42,7 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mo velerov1api.DefaultNamespace, velerotest.NewLogger(), velerotest.NewFakeControllerRuntimeClient(t), - defaultMaintenanceFrequency, + testMaintenanceFrequency, mgr, ) } @@ -51,7 +54,7 @@ func mockResticRepositoryCR() *velerov1api.BackupRepository { Name: "repo", }, Spec: velerov1api.BackupRepositorySpec{ - MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + MaintenanceFrequency: metav1.Duration{testMaintenanceFrequency}, }, } @@ -138,7 +141,7 @@ func TestResticRepoReconcile(t *testing.T) { Name: "unknown", }, Spec: velerov1api.BackupRepositorySpec{ - MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + MaintenanceFrequency: metav1.Duration{testMaintenanceFrequency}, }, }, expectNil: true, @@ -151,7 +154,7 @@ func TestResticRepoReconcile(t *testing.T) { Name: "repo", }, Spec: velerov1api.BackupRepositorySpec{ - MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + MaintenanceFrequency: metav1.Duration{testMaintenanceFrequency}, }, }, expectNil: true, @@ -164,7 +167,7 @@ func TestResticRepoReconcile(t *testing.T) { Name: "repo", }, Spec: velerov1api.BackupRepositorySpec{ - MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, + MaintenanceFrequency: metav1.Duration{testMaintenanceFrequency}, }, Status: velerov1api.BackupRepositoryStatus{ Phase: velerov1api.BackupRepositoryPhaseNew, @@ -187,3 +190,53 @@ func TestResticRepoReconcile(t *testing.T) { }) } } + +func TestGetRepositoryMaintenanceFrequency(t *testing.T) { + tests := []struct { + name string + mgr repository.Manager + repo *velerov1api.BackupRepository + freqReturn time.Duration + freqError error + userDefinedFreq time.Duration + expectFreq time.Duration + }{ + { + name: "user defined valid", + userDefinedFreq: time.Duration(time.Hour), + expectFreq: time.Duration(time.Hour), + }, + { + name: "repo return valid", + freqReturn: time.Duration(time.Hour * 2), + expectFreq: time.Duration(time.Hour * 2), + }, + { + name: "fall to default", + userDefinedFreq: -1, + freqError: errors.New("fake-error"), + expectFreq: defaultMaintainFrequency, + }, + { + name: "fall to default, no freq error", + freqReturn: -1, + expectFreq: defaultMaintainFrequency, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mgr := repomokes.RepositoryManager{} + mgr.On("DefaultMaintenanceFrequency", mock.Anything).Return(test.freqReturn, test.freqError) + reconciler := NewResticRepoReconciler( + velerov1api.DefaultNamespace, + velerotest.NewLogger(), + velerotest.NewFakeControllerRuntimeClient(t), + test.userDefinedFreq, + &mgr, + ) + + freq := reconciler.getRepositoryMaintenanceFrequency(test.repo) + assert.Equal(t, test.expectFreq, freq) + }) + } +} diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index 116a5c4e77..f4de4fb0d7 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -49,6 +49,7 @@ type backupper struct { veleroClient clientset.Interface pvcClient corev1client.PersistentVolumeClaimsGetter pvClient corev1client.PersistentVolumesGetter + uploaderType string results map[string]chan *velerov1api.PodVolumeBackup resultsLock sync.Mutex @@ -62,6 +63,7 @@ func newBackupper( veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, + uploaderType string, log logrus.FieldLogger, ) *backupper { b := &backupper{ @@ -71,6 +73,7 @@ func newBackupper( veleroClient: veleroClient, pvcClient: pvcClient, pvClient: pvClient, + uploaderType: uploaderType, results: make(map[string]chan *velerov1api.PodVolumeBackup), } @@ -107,7 +110,13 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. return nil, nil } - repo, err := b.repoEnsurer.EnsureRepo(b.ctx, backup.Namespace, pod.Namespace, backup.Spec.StorageLocation) + repositoryType := getRepositoryType(b.uploaderType) + if repositoryType == "" { + err := errors.Errorf("empty repository type, uploader %s", b.uploaderType) + return nil, []error{err} + } + + repo, err := b.repoEnsurer.EnsureRepo(b.ctx, backup.Namespace, pod.Namespace, backup.Spec.StorageLocation, repositoryType) if err != nil { return nil, []error{err} } @@ -182,8 +191,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. continue } - // TODO: Remove the hard-coded uploader type before v1.10 FC - volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, "restic", pvc) + volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, b.uploaderType, pvc) if volumeBackup, err = b.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { errs = append(errs, err) continue diff --git a/pkg/podvolume/backupper_factory.go b/pkg/podvolume/backupper_factory.go index aaaa5e2ac1..5cbd823e24 100644 --- a/pkg/podvolume/backupper_factory.go +++ b/pkg/podvolume/backupper_factory.go @@ -35,7 +35,7 @@ import ( // BackupperFactory can construct pod volumes backuppers. type BackupperFactory interface { // NewBackupper returns a pod volumes backupper for use during a single Velero backup. - NewBackupper(context.Context, *velerov1api.Backup) (Backupper, error) + NewBackupper(context.Context, *velerov1api.Backup, string) (Backupper, error) } func NewBackupperFactory(repoLocker *repository.RepoLocker, @@ -66,7 +66,7 @@ type backupperFactory struct { log logrus.FieldLogger } -func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1api.Backup) (Backupper, error) { +func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1api.Backup, uploaderType string) (Backupper, error) { informer := velerov1informers.NewFilteredPodVolumeBackupInformer( bf.veleroClient, backup.Namespace, @@ -77,7 +77,7 @@ func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1ap }, ) - b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, bf.log) + b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, uploaderType, bf.log) go informer.Run(ctx.Done()) if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, bf.repoInformerSynced) { diff --git a/pkg/podvolume/restorer.go b/pkg/podvolume/restorer.go index daa3a630d7..4cc8770a2e 100644 --- a/pkg/podvolume/restorer.go +++ b/pkg/podvolume/restorer.go @@ -56,6 +56,7 @@ type restorer struct { resultsLock sync.Mutex results map[string]chan *velerov1api.PodVolumeRestore + log logrus.FieldLogger } func newRestorer( @@ -75,6 +76,7 @@ func newRestorer( pvcClient: pvcClient, results: make(map[string]chan *velerov1api.PodVolumeRestore), + log: log, } podVolumeRestoreInformer.AddEventHandler( @@ -101,12 +103,17 @@ func newRestorer( } func (r *restorer) RestorePodVolumes(data RestoreData) []error { - volumesToRestore := GetVolumeBackupsForPod(data.PodVolumeBackups, data.Pod, data.SourceNamespace) + volumesToRestore := getVolumeBackupInfoForPod(data.PodVolumeBackups, data.Pod, data.SourceNamespace) if len(volumesToRestore) == 0 { return nil } - repo, err := r.repoEnsurer.EnsureRepo(r.ctx, data.Restore.Namespace, data.SourceNamespace, data.BackupLocation) + repositoryType, err := getVolumesRepositoryType(volumesToRestore) + if err != nil { + return []error{err} + } + + repo, err := r.repoEnsurer.EnsureRepo(r.ctx, data.Restore.Namespace, data.SourceNamespace, data.BackupLocation, repositoryType) if err != nil { return []error{err} } @@ -132,7 +139,7 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { for _, podVolume := range data.Pod.Spec.Volumes { podVolumes[podVolume.Name] = podVolume } - for volume, snapshot := range volumesToRestore { + for volume, backupInfo := range volumesToRestore { volumeObj, ok := podVolumes[volume] var pvc *corev1api.PersistentVolumeClaim if ok { @@ -144,8 +151,8 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { } } } - // TODO: Remove the hard-coded uploader type before v1.10 FC - volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, "restic", pvc) + + volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, backupInfo.snapshotID, repo.Spec.ResticIdentifier, backupInfo.uploaderType, pvc) if err := errorOnly(r.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) @@ -213,3 +220,29 @@ func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backu } return pvr } + +func getVolumesRepositoryType(volumes map[string]volumeBackupInfo) (string, error) { + if len(volumes) == 0 { + return "", errors.New("empty volume list") + } + + // the podVolumeBackups list come from one backup. In one backup, it is impossible that volumes are + // backed up by different uploaders or to different repositories. Asserting this ensures one repo only, + // which will simplify the following logics + repositoryType := "" + for _, backupInfo := range volumes { + if backupInfo.repositoryType == "" { + return "", errors.Errorf("empty repository type found among volume snapshots, snapshot ID %s, uploader %s", + backupInfo.snapshotID, backupInfo.uploaderType) + } + + if repositoryType == "" { + repositoryType = backupInfo.repositoryType + } else if repositoryType != backupInfo.repositoryType { + return "", errors.Errorf("multiple repository type in one backup, current type %s, differential one [type %s, snapshot ID %s, uploader %s]", + repositoryType, backupInfo.repositoryType, backupInfo.snapshotID, backupInfo.uploaderType) + } + } + + return repositoryType, nil +} diff --git a/pkg/podvolume/restorer_test.go b/pkg/podvolume/restorer_test.go new file mode 100644 index 0000000000..2241884293 --- /dev/null +++ b/pkg/podvolume/restorer_test.go @@ -0,0 +1,92 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podvolume + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetVolumesRepositoryType(t *testing.T) { + testCases := []struct { + name string + volumes map[string]volumeBackupInfo + expected string + expectedErr string + }{ + { + name: "empty volume", + expectedErr: "empty volume list", + }, + { + name: "empty repository type, first one", + volumes: map[string]volumeBackupInfo{ + "volume1": {"fake-snapshot-id-1", "fake-uploader-1", ""}, + "volume2": {"", "", "fake-type"}, + }, + expectedErr: "empty repository type found among volume snapshots, snapshot ID fake-snapshot-id-1, uploader fake-uploader-1", + }, + { + name: "empty repository type, last one", + volumes: map[string]volumeBackupInfo{ + "volume1": {"", "", "fake-type"}, + "volume2": {"", "", "fake-type"}, + "volume3": {"fake-snapshot-id-3", "fake-uploader-3", ""}, + }, + expectedErr: "empty repository type found among volume snapshots, snapshot ID fake-snapshot-id-3, uploader fake-uploader-3", + }, + { + name: "empty repository type, middle one", + volumes: map[string]volumeBackupInfo{ + "volume1": {"", "", "fake-type"}, + "volume2": {"fake-snapshot-id-2", "fake-uploader-2", ""}, + "volume3": {"", "", "fake-type"}, + }, + expectedErr: "empty repository type found among volume snapshots, snapshot ID fake-snapshot-id-2, uploader fake-uploader-2", + }, + { + name: "mismatch repository type", + volumes: map[string]volumeBackupInfo{ + "volume1": {"", "", "fake-type1"}, + "volume2": {"fake-snapshot-id-2", "fake-uploader-2", "fake-type2"}, + }, + expectedErr: "multiple repository type in one backup, current type fake-type1, differential one [type fake-type2, snapshot ID fake-snapshot-id-2, uploader fake-uploader-2]", + }, + { + name: "success", + volumes: map[string]volumeBackupInfo{ + "volume1": {"", "", "fake-type"}, + "volume2": {"", "", "fake-type"}, + "volume3": {"", "", "fake-type"}, + }, + expected: "fake-type", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual, err := getVolumesRepositoryType(tc.volumes) + assert.Equal(t, tc.expected, actual) + + if err != nil { + assert.EqualError(t, err, tc.expectedErr) + } + }) + + } +} diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index 959f05b7ef..7a73ed537f 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -23,6 +23,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" ) const ( @@ -48,10 +50,33 @@ const ( InitContainer = "restic-wait" ) +// volumeBackupInfo describes the backup info of a volume backed up by PodVolumeBackups +type volumeBackupInfo struct { + snapshotID string + uploaderType string + repositoryType string +} + // GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, // of the PodVolumeBackups that exist for the provided pod. func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string { + volumeBkInfo := getVolumeBackupInfoForPod(podVolumeBackups, pod, sourcePodNs) + if volumeBkInfo == nil { + return nil + } + volumes := make(map[string]string) + for k, v := range volumeBkInfo { + volumes[k] = v.snapshotID + } + + return volumes +} + +// getVolumeBackupInfoForPod returns a map, of volume name -> VolumeBackupInfo, +// of the PodVolumeBackups that exist for the provided pod. +func getVolumeBackupInfoForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]volumeBackupInfo { + volumes := make(map[string]volumeBackupInfo) for _, pvb := range podVolumeBackups { if !isPVBMatchPod(pvb, pod.GetName(), sourcePodNs) { @@ -71,14 +96,74 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod continue } - volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID + volumes[pvb.Spec.Volume] = volumeBackupInfo{ + snapshotID: pvb.Status.SnapshotID, + uploaderType: getUploaderTypeOrDefault(pvb.Spec.UploaderType), + repositoryType: getRepositoryType(pvb.Spec.UploaderType), + } } if len(volumes) > 0 { return volumes } - return getPodSnapshotAnnotations(pod) + fromAnnntation := getPodSnapshotAnnotations(pod) + if fromAnnntation == nil { + return nil + } + + for k, v := range fromAnnntation { + volumes[k] = volumeBackupInfo{v, uploader.ResticType, velerov1api.BackupRepositoryTypeRestic} + } + + return volumes +} + +// GetSnapshotIdentifier returns the snapshots represented by SnapshotIdentifier for the given PVBs +func GetSnapshotIdentifier(podVolumeBackups *velerov1api.PodVolumeBackupList) []repository.SnapshotIdentifier { + var res []repository.SnapshotIdentifier + for _, item := range podVolumeBackups.Items { + if item.Status.SnapshotID == "" { + continue + } + + res = append(res, repository.SnapshotIdentifier{ + VolumeNamespace: item.Spec.Pod.Namespace, + BackupStorageLocation: item.Spec.BackupStorageLocation, + SnapshotID: item.Status.SnapshotID, + RepositoryType: getRepositoryType(item.Spec.UploaderType), + }) + } + + return res +} + +func getUploaderTypeOrDefault(uploaderType string) string { + if uploaderType != "" { + return uploaderType + } else { + return uploader.ResticType + } +} + +// getRepositoryType returns the hardcode repositoryType for different backup methods - Restic or Kopia,uploaderType +// indicates the method. +// For Restic backup method, it is always hardcode to BackupRepositoryTypeRestic, never changed. +// For Kopia backup method, this means we hardcode repositoryType as BackupRepositoryTypeKopia for Unified Repo, +// at present (Kopia backup method is using Unified Repo). However, it doesn't mean we could deduce repositoryType +// from uploaderType for Unified Repo. +// TODO: post v1.10, refactor this function for Kopia backup method. In future, when we have multiple implementations of +// Unified Repo (besides Kopia), we will add the repositoryType to BSL, because by then, we are not able to hardcode +// the repositoryType to BackupRepositoryTypeKopia for Unified Repo. +func getRepositoryType(uploaderType string) string { + switch uploaderType { + case "", uploader.ResticType: + return velerov1api.BackupRepositoryTypeRestic + case uploader.KopiaType: + return velerov1api.BackupRepositoryTypeKopia + default: + return "" + } } func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool { diff --git a/pkg/repository/ensurer.go b/pkg/repository/ensurer.go index 15aa107014..7a7e48d9a9 100644 --- a/pkg/repository/ensurer.go +++ b/pkg/repository/ensurer.go @@ -53,6 +53,7 @@ type RepositoryEnsurer struct { type repoKey struct { volumeNamespace string backupLocation string + repositoryType string } func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *RepositoryEnsurer { @@ -83,7 +84,7 @@ func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInforme r.repoChansLock.Lock() defer r.repoChansLock.Unlock() - key := repoLabels(newObj.Spec.VolumeNamespace, newObj.Spec.BackupStorageLocation).String() + key := repoLabels(newObj.Spec.VolumeNamespace, newObj.Spec.BackupStorageLocation, newObj.Spec.RepositoryType).String() repoChan, ok := r.repoChans[key] if !ok { log.Debugf("No ready channel found for repository %s/%s", newObj.Namespace, newObj.Name) @@ -98,20 +99,25 @@ func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInforme return r } -func repoLabels(volumeNamespace, backupLocation string) labels.Set { +func repoLabels(volumeNamespace, backupLocation, repositoryType string) labels.Set { return map[string]string{ - velerov1api.ResticVolumeNamespaceLabel: label.GetValidName(volumeNamespace), - velerov1api.StorageLocationLabel: label.GetValidName(backupLocation), + velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), + velerov1api.StorageLocationLabel: label.GetValidName(backupLocation), + velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), } } -func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { - log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation) +func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation, repositoryType string) (*velerov1api.BackupRepository, error) { + if volumeNamespace == "" || backupLocation == "" || repositoryType == "" { + return nil, errors.Errorf("wrong parameters, namespace %q, backup storage location %q, repository type %q", volumeNamespace, backupLocation, repositoryType) + } + + log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation).WithField("repositoryType", repositoryType) // It's only safe to have one instance of this method executing concurrently for a - // given volumeNamespace + backupLocation, so synchronize based on that. It's fine + // given volumeNamespace + backupLocation + repositoryType, so synchronize based on that. It's fine // to run concurrently for *different* namespaces/locations. If you had 2 goroutines - // running this for the same inputs, both might find no ResticRepository exists, then + // running this for the same inputs, both might find no BackupRepository exists, then // both would create new ones for the same namespace/location. // // This issue could probably be avoided if we had a deterministic name for @@ -121,7 +127,7 @@ func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam // GenerateName) which poses a backwards compatibility problem. log.Debug("Acquiring lock") - repoMu := r.repoLock(volumeNamespace, backupLocation) + repoMu := r.repoLock(volumeNamespace, backupLocation, repositoryType) repoMu.Lock() defer func() { repoMu.Unlock() @@ -130,14 +136,14 @@ func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam log.Debug("Acquired lock") - selector := labels.SelectorFromSet(repoLabels(volumeNamespace, backupLocation)) + selector := labels.SelectorFromSet(repoLabels(volumeNamespace, backupLocation, repositoryType)) repos, err := r.repoLister.BackupRepositories(namespace).List(selector) if err != nil { return nil, errors.WithStack(err) } if len(repos) > 1 { - return nil, errors.Errorf("more than one ResticRepository found for workload namespace %q, backup storage location %q", volumeNamespace, backupLocation) + return nil, errors.Errorf("more than one BackupRepository found for workload namespace %q, backup storage location %q, repository type %q", volumeNamespace, backupLocation, repositoryType) } if len(repos) == 1 { if repos[0].Status.Phase != velerov1api.BackupRepositoryPhaseReady { @@ -154,12 +160,13 @@ func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam repo := &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - GenerateName: fmt.Sprintf("%s-%s-", volumeNamespace, backupLocation), - Labels: repoLabels(volumeNamespace, backupLocation), + GenerateName: fmt.Sprintf("%s-%s-%s-", volumeNamespace, backupLocation, repositoryType), + Labels: repoLabels(volumeNamespace, backupLocation, repositoryType), }, Spec: velerov1api.BackupRepositorySpec{ VolumeNamespace: volumeNamespace, BackupStorageLocation: backupLocation, + RepositoryType: repositoryType, }, } @@ -198,13 +205,14 @@ func (r *RepositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRep return r.repoChans[name] } -func (r *RepositoryEnsurer) repoLock(volumeNamespace, backupLocation string) *sync.Mutex { +func (r *RepositoryEnsurer) repoLock(volumeNamespace, backupLocation, repositoryType string) *sync.Mutex { r.repoLocksMu.Lock() defer r.repoLocksMu.Unlock() key := repoKey{ volumeNamespace: volumeNamespace, backupLocation: backupLocation, + repositoryType: repositoryType, } if r.repoLocks[key] == nil { diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go index ff97931827..8d9773a3b9 100644 --- a/pkg/repository/manager.go +++ b/pkg/repository/manager.go @@ -19,6 +19,7 @@ package repository import ( "context" "fmt" + "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -43,6 +44,10 @@ type SnapshotIdentifier struct { // SnapshotID is the short ID of the restic snapshot. SnapshotID string + + // RepositoryType is the type of the repository where the + // snapshot is stored + RepositoryType string } // Manager manages backup repositories. @@ -65,6 +70,8 @@ type Manager interface { // Forget removes a snapshot from the list of // available snapshots in a repo. Forget(context.Context, SnapshotIdentifier) error + // DefaultMaintenanceFrequency returns the default maintenance frequency from the specific repo + DefaultMaintenanceFrequency(repo *velerov1api.BackupRepository) (time.Duration, error) } type manager struct { @@ -84,6 +91,7 @@ func NewManager( repoLocker *RepoLocker, repoEnsurer *RepositoryEnsurer, credentialFileStore credentials.FileStore, + credentialSecretStore credentials.SecretStore, log logrus.FieldLogger, ) Manager { mgr := &manager{ @@ -97,6 +105,10 @@ func NewManager( } mgr.providers[velerov1api.BackupRepositoryTypeRestic] = provider.NewResticRepositoryProvider(credentialFileStore, mgr.fileSystem, mgr.log) + mgr.providers[velerov1api.BackupRepositoryTypeKopia] = provider.NewUnifiedRepoProvider(credentials.CredentialGetter{ + FromFile: credentialFileStore, + FromSecret: credentialSecretStore, + }, mgr.log) return mgr } @@ -162,7 +174,7 @@ func (m *manager) UnlockRepo(repo *velerov1api.BackupRepository) error { } func (m *manager) Forget(ctx context.Context, snapshot SnapshotIdentifier) error { - repo, err := m.repoEnsurer.EnsureRepo(ctx, m.namespace, snapshot.VolumeNamespace, snapshot.BackupStorageLocation) + repo, err := m.repoEnsurer.EnsureRepo(ctx, m.namespace, snapshot.VolumeNamespace, snapshot.BackupStorageLocation, snapshot.RepositoryType) if err != nil { return err } @@ -181,10 +193,26 @@ func (m *manager) Forget(ctx context.Context, snapshot SnapshotIdentifier) error return prd.Forget(context.Background(), snapshot.SnapshotID, param) } +func (m *manager) DefaultMaintenanceFrequency(repo *velerov1api.BackupRepository) (time.Duration, error) { + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return 0, errors.WithStack(err) + } + + param, err := m.assembleRepoParam(repo) + if err != nil { + return 0, errors.WithStack(err) + } + + return prd.DefaultMaintenanceFrequency(context.Background(), param), nil +} + func (m *manager) getRepositoryProvider(repo *velerov1api.BackupRepository) (provider.Provider, error) { switch repo.Spec.RepositoryType { case "", velerov1api.BackupRepositoryTypeRestic: return m.providers[velerov1api.BackupRepositoryTypeRestic], nil + case velerov1api.BackupRepositoryTypeKopia: + return m.providers[velerov1api.BackupRepositoryTypeKopia], nil default: return nil, fmt.Errorf("failed to get provider for repository %s", repo.Spec.RepositoryType) } diff --git a/pkg/repository/manager_test.go b/pkg/repository/manager_test.go index 7692a8b219..4d84919d2a 100644 --- a/pkg/repository/manager_test.go +++ b/pkg/repository/manager_test.go @@ -26,7 +26,7 @@ import ( ) func TestGetRepositoryProvider(t *testing.T) { - mgr := NewManager("", nil, nil, nil, nil, nil).(*manager) + mgr := NewManager("", nil, nil, nil, nil, nil, nil).(*manager) repo := &velerov1.BackupRepository{} // empty repository type diff --git a/pkg/repository/mocks/repository_manager.go b/pkg/repository/mocks/repository_manager.go index a0ec81db75..b029556bab 100644 --- a/pkg/repository/mocks/repository_manager.go +++ b/pkg/repository/mocks/repository_manager.go @@ -21,6 +21,8 @@ import ( mock "github.com/stretchr/testify/mock" + time "time" + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/repository" @@ -59,6 +61,27 @@ func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 repository.Snapshot return r0 } +// DefaultMaintenanceFrequency provides a mock function with given fields: repo +func (_m *RepositoryManager) DefaultMaintenanceFrequency(repo *v1.BackupRepository) (time.Duration, error) { + ret := _m.Called(repo) + + var r0 time.Duration + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) time.Duration); ok { + r0 = rf(repo) + } else { + r0 = ret.Get(0).(time.Duration) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.BackupRepository) error); ok { + r1 = rf(repo) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // InitRepo provides a mock function with given fields: repo func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) From b15c59ba69e291e1d8548c8e32f01e646e665248 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Tue, 30 Aug 2022 13:49:39 -0400 Subject: [PATCH 283/366] Don't move velero v1 plugins to new proto dir To preserve backwards compatibility, don't move velero v1 plugins to new proto dir. Signed-off-by: Scott Seago --- changelogs/unreleased/5263-sseago | 1 + design/plugin-versioning.md | 7 +- hack/update-2proto.sh | 1 - pkg/plugin/framework/backup_item_action.go | 2 +- .../framework/backup_item_action_client.go | 2 +- .../framework/backup_item_action_server.go | 2 +- .../framework/backup_item_action_test.go | 2 +- .../v1 => }/BackupItemAction.pb.go | 221 +++++++++--------- .../v1 => }/BackupItemAction.proto | 8 +- 9 files changed, 124 insertions(+), 122 deletions(-) create mode 100644 changelogs/unreleased/5263-sseago rename pkg/plugin/generated/{backupitemaction/v1 => }/BackupItemAction.pb.go (55%) rename pkg/plugin/proto/{backupitemaction/v1 => }/BackupItemAction.proto (77%) diff --git a/changelogs/unreleased/5263-sseago b/changelogs/unreleased/5263-sseago new file mode 100644 index 0000000000..384f5f9184 --- /dev/null +++ b/changelogs/unreleased/5263-sseago @@ -0,0 +1 @@ +Don't move velero v1 plugins to new proto dir diff --git a/design/plugin-versioning.md b/design/plugin-versioning.md index c43cecea3c..92279b9754 100644 --- a/design/plugin-versioning.md +++ b/design/plugin-versioning.md @@ -135,8 +135,11 @@ type ObjectStore interface { The proto service definitions of the plugins will also be versioned and arranged by their plugin kind. Currently, all the proto definitions reside under `pkg/plugin/proto` in a file corresponding to their plugin kind. -These files will be rearranged to be grouped by kind and then versioned: `pkg/plugin/proto//`. -The scripts to compile the proto service definitions will need to be updated to place the generated Go code under a matching directory structure. +These files will be rearranged to be grouped by kind and then versioned: `pkg/plugin/proto//`, +except for the current v1 plugins. Those will remain in their current package/location for backwards compatibility. +This will allow plugin images built with earlier versions of velero to work with the latest velero (for v1 plugins +only). The go_package option will be added to all proto service definitions to allow the proto compilation script +to place the generated go code for each plugin api version in the proper go package directory. It is not possible to import an existing proto service into a new one, so any methods will need to be duplicated across versions if they are required by the new version. The message definitions can be shared however, so these could be extracted from the service definition files and placed in a file that can be shared across all versions of the service. diff --git a/hack/update-2proto.sh b/hack/update-2proto.sh index b3c8e0ba29..43afa8d4ce 100755 --- a/hack/update-2proto.sh +++ b/hack/update-2proto.sh @@ -20,6 +20,5 @@ echo "Updating plugin proto" echo protoc --version protoc pkg/plugin/proto/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ --go_opt=module=github.com/vmware-tanzu/velero/pkg/plugin/generated -I pkg/plugin/proto/ -protoc pkg/plugin/proto/backupitemaction/v1/*.proto --go_out=plugins=grpc:pkg/plugin/generated/ --go_opt=module=github.com/vmware-tanzu/velero/pkg/plugin/generated -I pkg/plugin/proto/ echo "Updating plugin proto - done!" diff --git a/pkg/plugin/framework/backup_item_action.go b/pkg/plugin/framework/backup_item_action.go index 94bd6c8c0b..8780c78264 100644 --- a/pkg/plugin/framework/backup_item_action.go +++ b/pkg/plugin/framework/backup_item_action.go @@ -21,7 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" - protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) // BackupItemActionPlugin is an implementation of go-plugin's Plugin diff --git a/pkg/plugin/framework/backup_item_action_client.go b/pkg/plugin/framework/backup_item_action_client.go index a654962c0d..4756ab3920 100644 --- a/pkg/plugin/framework/backup_item_action_client.go +++ b/pkg/plugin/framework/backup_item_action_client.go @@ -27,7 +27,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) diff --git a/pkg/plugin/framework/backup_item_action_server.go b/pkg/plugin/framework/backup_item_action_server.go index 630d2c7185..fec8f07008 100644 --- a/pkg/plugin/framework/backup_item_action_server.go +++ b/pkg/plugin/framework/backup_item_action_server.go @@ -25,7 +25,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" - protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" ) diff --git a/pkg/plugin/framework/backup_item_action_test.go b/pkg/plugin/framework/backup_item_action_test.go index ec4da43cc0..32d7ea5515 100644 --- a/pkg/plugin/framework/backup_item_action_test.go +++ b/pkg/plugin/framework/backup_item_action_test.go @@ -31,7 +31,7 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/backup/mocks" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" - protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1" + protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) diff --git a/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go b/pkg/plugin/generated/BackupItemAction.pb.go similarity index 55% rename from pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go rename to pkg/plugin/generated/BackupItemAction.pb.go index 9e820c69fc..5b216cc6f0 100644 --- a/pkg/plugin/generated/backupitemaction/v1/BackupItemAction.pb.go +++ b/pkg/plugin/generated/BackupItemAction.pb.go @@ -2,14 +2,13 @@ // versions: // protoc-gen-go v1.23.0 // protoc v3.14.0 -// source: backupitemaction/v1/BackupItemAction.proto +// source: BackupItemAction.proto -package v1 +package generated import ( context "context" proto "github.com/golang/protobuf/proto" - generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -43,7 +42,7 @@ type ExecuteRequest struct { func (x *ExecuteRequest) Reset() { *x = ExecuteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0] + mi := &file_BackupItemAction_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -56,7 +55,7 @@ func (x *ExecuteRequest) String() string { func (*ExecuteRequest) ProtoMessage() {} func (x *ExecuteRequest) ProtoReflect() protoreflect.Message { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0] + mi := &file_BackupItemAction_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -69,7 +68,7 @@ func (x *ExecuteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteRequest.ProtoReflect.Descriptor instead. func (*ExecuteRequest) Descriptor() ([]byte, []int) { - return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{0} + return file_BackupItemAction_proto_rawDescGZIP(), []int{0} } func (x *ExecuteRequest) GetPlugin() string { @@ -98,14 +97,14 @@ type ExecuteResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` - AdditionalItems []*generated.ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` + Item []byte `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + AdditionalItems []*ResourceIdentifier `protobuf:"bytes,2,rep,name=additionalItems,proto3" json:"additionalItems,omitempty"` } func (x *ExecuteResponse) Reset() { *x = ExecuteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1] + mi := &file_BackupItemAction_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -118,7 +117,7 @@ func (x *ExecuteResponse) String() string { func (*ExecuteResponse) ProtoMessage() {} func (x *ExecuteResponse) ProtoReflect() protoreflect.Message { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1] + mi := &file_BackupItemAction_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -131,7 +130,7 @@ func (x *ExecuteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteResponse.ProtoReflect.Descriptor instead. func (*ExecuteResponse) Descriptor() ([]byte, []int) { - return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{1} + return file_BackupItemAction_proto_rawDescGZIP(), []int{1} } func (x *ExecuteResponse) GetItem() []byte { @@ -141,7 +140,7 @@ func (x *ExecuteResponse) GetItem() []byte { return nil } -func (x *ExecuteResponse) GetAdditionalItems() []*generated.ResourceIdentifier { +func (x *ExecuteResponse) GetAdditionalItems() []*ResourceIdentifier { if x != nil { return x.AdditionalItems } @@ -159,7 +158,7 @@ type BackupItemActionAppliesToRequest struct { func (x *BackupItemActionAppliesToRequest) Reset() { *x = BackupItemActionAppliesToRequest{} if protoimpl.UnsafeEnabled { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2] + mi := &file_BackupItemAction_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -172,7 +171,7 @@ func (x *BackupItemActionAppliesToRequest) String() string { func (*BackupItemActionAppliesToRequest) ProtoMessage() {} func (x *BackupItemActionAppliesToRequest) ProtoReflect() protoreflect.Message { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2] + mi := &file_BackupItemAction_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -185,7 +184,7 @@ func (x *BackupItemActionAppliesToRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BackupItemActionAppliesToRequest.ProtoReflect.Descriptor instead. func (*BackupItemActionAppliesToRequest) Descriptor() ([]byte, []int) { - return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{2} + return file_BackupItemAction_proto_rawDescGZIP(), []int{2} } func (x *BackupItemActionAppliesToRequest) GetPlugin() string { @@ -200,13 +199,13 @@ type BackupItemActionAppliesToResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` + ResourceSelector *ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` } func (x *BackupItemActionAppliesToResponse) Reset() { *x = BackupItemActionAppliesToResponse{} if protoimpl.UnsafeEnabled { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3] + mi := &file_BackupItemAction_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -219,7 +218,7 @@ func (x *BackupItemActionAppliesToResponse) String() string { func (*BackupItemActionAppliesToResponse) ProtoMessage() {} func (x *BackupItemActionAppliesToResponse) ProtoReflect() protoreflect.Message { - mi := &file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3] + mi := &file_BackupItemAction_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -232,92 +231,91 @@ func (x *BackupItemActionAppliesToResponse) ProtoReflect() protoreflect.Message // Deprecated: Use BackupItemActionAppliesToResponse.ProtoReflect.Descriptor instead. func (*BackupItemActionAppliesToResponse) Descriptor() ([]byte, []int) { - return file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP(), []int{3} + return file_BackupItemAction_proto_rawDescGZIP(), []int{3} } -func (x *BackupItemActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { +func (x *BackupItemActionAppliesToResponse) GetResourceSelector() *ResourceSelector { if x != nil { return x.ResourceSelector } return nil } -var File_backupitemaction_v1_BackupItemAction_proto protoreflect.FileDescriptor - -var file_backupitemaction_v1_BackupItemAction_proto_rawDesc = []byte{ - 0x0a, 0x2a, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x69, 0x74, 0x65, 0x6d, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, - 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, - 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, - 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x22, 0x6e, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x47, 0x0a, 0x0f, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, - 0x74, 0x65, 0x6d, 0x73, 0x22, 0x3a, 0x0a, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, - 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x22, 0x6c, 0x0a, 0x21, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x32, 0xa0, - 0x01, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, - 0x12, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, - 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, - 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x49, 0x5a, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, - 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x69, - 0x74, 0x65, 0x6d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, +var File_BackupItemAction_proto protoreflect.FileDescriptor + +var file_BackupItemAction_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x1a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x54, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, + 0x16, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x6e, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x47, + 0x0a, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x3a, 0x0a, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x22, 0x6c, 0x0a, 0x21, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, + 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, + 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x32, 0xbc, 0x01, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x73, 0x54, 0x6f, 0x12, 0x2b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, + 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, + 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, 0x76, 0x65, 0x6c, 0x65, + 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_backupitemaction_v1_BackupItemAction_proto_rawDescOnce sync.Once - file_backupitemaction_v1_BackupItemAction_proto_rawDescData = file_backupitemaction_v1_BackupItemAction_proto_rawDesc + file_BackupItemAction_proto_rawDescOnce sync.Once + file_BackupItemAction_proto_rawDescData = file_BackupItemAction_proto_rawDesc ) -func file_backupitemaction_v1_BackupItemAction_proto_rawDescGZIP() []byte { - file_backupitemaction_v1_BackupItemAction_proto_rawDescOnce.Do(func() { - file_backupitemaction_v1_BackupItemAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_backupitemaction_v1_BackupItemAction_proto_rawDescData) +func file_BackupItemAction_proto_rawDescGZIP() []byte { + file_BackupItemAction_proto_rawDescOnce.Do(func() { + file_BackupItemAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_BackupItemAction_proto_rawDescData) }) - return file_backupitemaction_v1_BackupItemAction_proto_rawDescData -} - -var file_backupitemaction_v1_BackupItemAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_backupitemaction_v1_BackupItemAction_proto_goTypes = []interface{}{ - (*ExecuteRequest)(nil), // 0: v1.ExecuteRequest - (*ExecuteResponse)(nil), // 1: v1.ExecuteResponse - (*BackupItemActionAppliesToRequest)(nil), // 2: v1.BackupItemActionAppliesToRequest - (*BackupItemActionAppliesToResponse)(nil), // 3: v1.BackupItemActionAppliesToResponse - (*generated.ResourceIdentifier)(nil), // 4: generated.ResourceIdentifier - (*generated.ResourceSelector)(nil), // 5: generated.ResourceSelector -} -var file_backupitemaction_v1_BackupItemAction_proto_depIdxs = []int32{ - 4, // 0: v1.ExecuteResponse.additionalItems:type_name -> generated.ResourceIdentifier - 5, // 1: v1.BackupItemActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector - 2, // 2: v1.BackupItemAction.AppliesTo:input_type -> v1.BackupItemActionAppliesToRequest - 0, // 3: v1.BackupItemAction.Execute:input_type -> v1.ExecuteRequest - 3, // 4: v1.BackupItemAction.AppliesTo:output_type -> v1.BackupItemActionAppliesToResponse - 1, // 5: v1.BackupItemAction.Execute:output_type -> v1.ExecuteResponse + return file_BackupItemAction_proto_rawDescData +} + +var file_BackupItemAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_BackupItemAction_proto_goTypes = []interface{}{ + (*ExecuteRequest)(nil), // 0: generated.ExecuteRequest + (*ExecuteResponse)(nil), // 1: generated.ExecuteResponse + (*BackupItemActionAppliesToRequest)(nil), // 2: generated.BackupItemActionAppliesToRequest + (*BackupItemActionAppliesToResponse)(nil), // 3: generated.BackupItemActionAppliesToResponse + (*ResourceIdentifier)(nil), // 4: generated.ResourceIdentifier + (*ResourceSelector)(nil), // 5: generated.ResourceSelector +} +var file_BackupItemAction_proto_depIdxs = []int32{ + 4, // 0: generated.ExecuteResponse.additionalItems:type_name -> generated.ResourceIdentifier + 5, // 1: generated.BackupItemActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 2, // 2: generated.BackupItemAction.AppliesTo:input_type -> generated.BackupItemActionAppliesToRequest + 0, // 3: generated.BackupItemAction.Execute:input_type -> generated.ExecuteRequest + 3, // 4: generated.BackupItemAction.AppliesTo:output_type -> generated.BackupItemActionAppliesToResponse + 1, // 5: generated.BackupItemAction.Execute:output_type -> generated.ExecuteResponse 4, // [4:6] is the sub-list for method output_type 2, // [2:4] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name @@ -325,13 +323,14 @@ var file_backupitemaction_v1_BackupItemAction_proto_depIdxs = []int32{ 0, // [0:2] is the sub-list for field type_name } -func init() { file_backupitemaction_v1_BackupItemAction_proto_init() } -func file_backupitemaction_v1_BackupItemAction_proto_init() { - if File_backupitemaction_v1_BackupItemAction_proto != nil { +func init() { file_BackupItemAction_proto_init() } +func file_BackupItemAction_proto_init() { + if File_BackupItemAction_proto != nil { return } + file_Shared_proto_init() if !protoimpl.UnsafeEnabled { - file_backupitemaction_v1_BackupItemAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_BackupItemAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExecuteRequest); i { case 0: return &v.state @@ -343,7 +342,7 @@ func file_backupitemaction_v1_BackupItemAction_proto_init() { return nil } } - file_backupitemaction_v1_BackupItemAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_BackupItemAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExecuteResponse); i { case 0: return &v.state @@ -355,7 +354,7 @@ func file_backupitemaction_v1_BackupItemAction_proto_init() { return nil } } - file_backupitemaction_v1_BackupItemAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_BackupItemAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BackupItemActionAppliesToRequest); i { case 0: return &v.state @@ -367,7 +366,7 @@ func file_backupitemaction_v1_BackupItemAction_proto_init() { return nil } } - file_backupitemaction_v1_BackupItemAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_BackupItemAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BackupItemActionAppliesToResponse); i { case 0: return &v.state @@ -384,20 +383,20 @@ func file_backupitemaction_v1_BackupItemAction_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_backupitemaction_v1_BackupItemAction_proto_rawDesc, + RawDescriptor: file_BackupItemAction_proto_rawDesc, NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_backupitemaction_v1_BackupItemAction_proto_goTypes, - DependencyIndexes: file_backupitemaction_v1_BackupItemAction_proto_depIdxs, - MessageInfos: file_backupitemaction_v1_BackupItemAction_proto_msgTypes, + GoTypes: file_BackupItemAction_proto_goTypes, + DependencyIndexes: file_BackupItemAction_proto_depIdxs, + MessageInfos: file_BackupItemAction_proto_msgTypes, }.Build() - File_backupitemaction_v1_BackupItemAction_proto = out.File - file_backupitemaction_v1_BackupItemAction_proto_rawDesc = nil - file_backupitemaction_v1_BackupItemAction_proto_goTypes = nil - file_backupitemaction_v1_BackupItemAction_proto_depIdxs = nil + File_BackupItemAction_proto = out.File + file_BackupItemAction_proto_rawDesc = nil + file_BackupItemAction_proto_goTypes = nil + file_BackupItemAction_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. @@ -426,7 +425,7 @@ func NewBackupItemActionClient(cc grpc.ClientConnInterface) BackupItemActionClie func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemActionAppliesToRequest, opts ...grpc.CallOption) (*BackupItemActionAppliesToResponse, error) { out := new(BackupItemActionAppliesToResponse) - err := c.cc.Invoke(ctx, "/v1.BackupItemAction/AppliesTo", in, out, opts...) + err := c.cc.Invoke(ctx, "/generated.BackupItemAction/AppliesTo", in, out, opts...) if err != nil { return nil, err } @@ -435,7 +434,7 @@ func (c *backupItemActionClient) AppliesTo(ctx context.Context, in *BackupItemAc func (c *backupItemActionClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) { out := new(ExecuteResponse) - err := c.cc.Invoke(ctx, "/v1.BackupItemAction/Execute", in, out, opts...) + err := c.cc.Invoke(ctx, "/generated.BackupItemAction/Execute", in, out, opts...) if err != nil { return nil, err } @@ -473,7 +472,7 @@ func _BackupItemAction_AppliesTo_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/v1.BackupItemAction/AppliesTo", + FullMethod: "/generated.BackupItemAction/AppliesTo", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BackupItemActionServer).AppliesTo(ctx, req.(*BackupItemActionAppliesToRequest)) @@ -491,7 +490,7 @@ func _BackupItemAction_Execute_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/v1.BackupItemAction/Execute", + FullMethod: "/generated.BackupItemAction/Execute", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BackupItemActionServer).Execute(ctx, req.(*ExecuteRequest)) @@ -500,7 +499,7 @@ func _BackupItemAction_Execute_Handler(srv interface{}, ctx context.Context, dec } var _BackupItemAction_serviceDesc = grpc.ServiceDesc{ - ServiceName: "v1.BackupItemAction", + ServiceName: "generated.BackupItemAction", HandlerType: (*BackupItemActionServer)(nil), Methods: []grpc.MethodDesc{ { @@ -513,5 +512,5 @@ var _BackupItemAction_serviceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "backupitemaction/v1/BackupItemAction.proto", + Metadata: "BackupItemAction.proto", } diff --git a/pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto b/pkg/plugin/proto/BackupItemAction.proto similarity index 77% rename from pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto rename to pkg/plugin/proto/BackupItemAction.proto index 2fef696563..66111300a7 100644 --- a/pkg/plugin/proto/backupitemaction/v1/BackupItemAction.proto +++ b/pkg/plugin/proto/BackupItemAction.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package v1; -option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated/backupitemaction/v1"; +package generated; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated"; import "Shared.proto"; @@ -12,7 +12,7 @@ message ExecuteRequest { message ExecuteResponse { bytes item = 1; - repeated generated.ResourceIdentifier additionalItems = 2; + repeated ResourceIdentifier additionalItems = 2; } service BackupItemAction { @@ -25,5 +25,5 @@ message BackupItemActionAppliesToRequest { } message BackupItemActionAppliesToResponse { - generated.ResourceSelector ResourceSelector = 1; + ResourceSelector ResourceSelector = 1; } \ No newline at end of file From 5e6111e6c06e0f8f3252bb7088d92ecb113a35ea Mon Sep 17 00:00:00 2001 From: cleverhu Date: Tue, 6 Sep 2022 20:10:06 +0800 Subject: [PATCH 284/366] add shorthand for labels columns Signed-off-by: cleverhu --- changelogs/unreleased/5291-cleverhu | 1 + pkg/cmd/util/output/output.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5291-cleverhu diff --git a/changelogs/unreleased/5291-cleverhu b/changelogs/unreleased/5291-cleverhu new file mode 100644 index 0000000000..0cf9b7a157 --- /dev/null +++ b/changelogs/unreleased/5291-cleverhu @@ -0,0 +1 @@ +Add more detailed comments for labels columns. \ No newline at end of file diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index ab3f7a95d6..1b0a992198 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -41,7 +41,7 @@ const downloadRequestTimeout = 30 * time.Second func BindFlags(flags *pflag.FlagSet) { flags.StringP("output", "o", "table", "Output display format. For create commands, display the object but do not send it to the server. Valid formats are 'table', 'json', and 'yaml'. 'table' is not valid for the install command.") labelColumns := flag.NewStringArray() - flags.Var(&labelColumns, "label-columns", "A comma-separated list of labels to be displayed as columns") + flags.VarP(&labelColumns, "label-columns", "L", "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...") flags.Bool("show-labels", false, "Show labels in the last column") } From 067a3ec03a42b6969090961df00e4b7d098ea4f8 Mon Sep 17 00:00:00 2001 From: cleverhu Date: Wed, 7 Sep 2022 00:34:26 +0800 Subject: [PATCH 285/366] change CSISnapshotTimeout from point to normal var Signed-off-by: cleverhu --- changelogs/unreleased/5294-cleverhu | 1 + pkg/cmd/util/output/backup_describer.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5294-cleverhu diff --git a/changelogs/unreleased/5294-cleverhu b/changelogs/unreleased/5294-cleverhu new file mode 100644 index 0000000000..b89daf7636 --- /dev/null +++ b/changelogs/unreleased/5294-cleverhu @@ -0,0 +1 @@ +change CSISnapshotTimeout from pointer to normal variables. \ No newline at end of file diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index f8c05c1567..6121bc5da3 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -165,7 +165,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { d.Printf("TTL:\t%s\n", spec.TTL.Duration) d.Println() - d.Printf("CSISnapshotTimeout:\t%s\n", &spec.CSISnapshotTimeout.Duration) + d.Printf("CSISnapshotTimeout:\t%s\n", spec.CSISnapshotTimeout.Duration) d.Println() if len(spec.Hooks.Resources) == 0 { From 7de6f2a2fcdcd07451b330b33eba79963622e169 Mon Sep 17 00:00:00 2001 From: cleverhu Date: Tue, 6 Sep 2022 22:43:57 +0800 Subject: [PATCH 286/366] trim isAlreadyExistsError for restore Signed-off-by: cleverhu --- changelogs/unreleased/5293-cleverhu | 1 + pkg/restore/restore.go | 27 ++++++++++----------------- 2 files changed, 11 insertions(+), 17 deletions(-) create mode 100644 changelogs/unreleased/5293-cleverhu diff --git a/changelogs/unreleased/5293-cleverhu b/changelogs/unreleased/5293-cleverhu new file mode 100644 index 0000000000..8c1c03d754 --- /dev/null +++ b/changelogs/unreleased/5293-cleverhu @@ -0,0 +1 @@ +Optimize code for restore exists resources. \ No newline at end of file diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index c3a022c566..9f20e34fc8 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1252,29 +1252,22 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // check if we want to treat the error as a warning, in some cases the creation call might not get executed due to object API validations // and Velero might not get the already exists error type but in reality the object already exists - objectExists := false var fromCluster *unstructured.Unstructured - if restoreErr != nil && !isAlreadyExistsError { + if restoreErr != nil { // check for the existence of the object in cluster, if no error then it implies that object exists - // and if err then we want to fallthrough and do another get call later + // and if err then we want to judge whether there is an existing error in the previous creation. + // if so, we will return the 'get' error. + // otherwise, we will return the original creation error. fromCluster, err = resourceClient.Get(name, metav1.GetOptions{}) - if err == nil { - objectExists = true + if err != nil && isAlreadyExistsError { + ctx.log.Errorf("Error retrieving in-cluster version of %s: %v", kube.NamespaceAndName(obj), err) + errs.Add(namespace, err) + return warnings, errs } } - if isAlreadyExistsError || objectExists { - // do a get call if we did not run this previously i.e. - // we've only run this for errors other than isAlreadyExistError - if fromCluster == nil { - fromCluster, err = resourceClient.Get(name, metav1.GetOptions{}) - if err != nil { - ctx.log.Errorf("Error retrieving cluster version of %s: %v", kube.NamespaceAndName(obj), err) - errs.Add(namespace, err) - return warnings, errs - } - } + if fromCluster != nil { // Remove insubstantial metadata. fromCluster, err = resetMetadataAndStatus(fromCluster) if err != nil { @@ -1462,7 +1455,7 @@ func isAlreadyExistsError(ctx *restoreContext, obj *unstructured.Unstructured, e } } - // the "already allocated" error may caused by other services, check whether the expected service exists or not + // the "already allocated" error may be caused by other services, check whether the expected service exists or not if _, err = client.Get(obj.GetName(), metav1.GetOptions{}); err != nil { if apierrors.IsNotFound(err) { ctx.log.Debugf("Service %s not found", kube.NamespaceAndName(obj)) From b49e39c0218bf0eead88cb209bca42abfceeaed3 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 7 Sep 2022 08:46:00 +0800 Subject: [PATCH 287/366] Remove github.com/apex/log logger. Signed-off-by: Xun Jiang --- changelogs/unreleased/5297-blackpiglet | 1 + go.mod | 1 - go.sum | 27 ------------------------ pkg/cmd/cli/restic/server.go | 25 +++++++++++----------- pkg/controller/backup_controller.go | 5 ++--- pkg/controller/backup_sync_controller.go | 7 +++--- 6 files changed, 18 insertions(+), 48 deletions(-) create mode 100644 changelogs/unreleased/5297-blackpiglet diff --git a/changelogs/unreleased/5297-blackpiglet b/changelogs/unreleased/5297-blackpiglet new file mode 100644 index 0000000000..8e46fb5d0a --- /dev/null +++ b/changelogs/unreleased/5297-blackpiglet @@ -0,0 +1 @@ +Remove github.com/apex/log logger. \ No newline at end of file diff --git a/go.mod b/go.mod index 03733cfe55..6d36169039 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/Azure/go-autorest/autorest v0.11.21 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.3.0 - github.com/apex/log v1.9.0 github.com/aws/aws-sdk-go v1.43.31 github.com/bombsimon/logrusr v1.1.0 github.com/evanphx/json-patch v4.11.0+incompatible diff --git a/go.sum b/go.sum index 75a473f1c3..8fa8ba1958 100644 --- a/go.sum +++ b/go.sum @@ -141,11 +141,6 @@ github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= -github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= -github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -153,12 +148,10 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -504,7 +497,6 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -570,8 +562,6 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= @@ -580,7 +570,6 @@ github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqf github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -590,7 +579,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -659,7 +647,6 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -740,7 +727,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= @@ -754,7 +740,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sanity-io/litter v1.5.4/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -765,10 +750,7 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= @@ -814,13 +796,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/studio-b12/gowebdav v0.0.0-20211106090535-29e74efa701f/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= -github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= -github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -912,7 +887,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1470,7 +1444,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 73d7cdec56..e7b133ac69 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -24,7 +24,6 @@ import ( "strings" "time" - "github.com/apex/log" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" @@ -280,7 +279,7 @@ func (s *resticServer) markInProgressCRsFailed() { // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here client, err := ctrlclient.New(s.mgr.GetConfig(), ctrlclient.Options{Scheme: s.mgr.GetScheme()}) if err != nil { - log.WithError(errors.WithStack(err)).Error("failed to create client") + s.logger.WithError(errors.WithStack(err)).Error("failed to create client") return } @@ -292,16 +291,16 @@ func (s *resticServer) markInProgressCRsFailed() { func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvbs := &velerov1api.PodVolumeBackupList{} if err := client.List(s.ctx, pvbs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { - log.WithError(errors.WithStack(err)).Error("failed to list podvolumebackups") + s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumebackups") return } for _, pvb := range pvbs.Items { if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseInProgress { - log.Debugf("the status of podvolumebackup %q is %q, skip", pvb.GetName(), pvb.Status.Phase) + s.logger.Debugf("the status of podvolumebackup %q is %q, skip", pvb.GetName(), pvb.Status.Phase) continue } if pvb.Spec.Node != s.nodeName { - log.Debugf("the node of podvolumebackup %q is %q, not %q, skip", pvb.GetName(), pvb.Spec.Node, s.nodeName) + s.logger.Debugf("the node of podvolumebackup %q is %q, not %q, skip", pvb.GetName(), pvb.Spec.Node, s.nodeName) continue } original := pvb.DeepCopy() @@ -309,22 +308,22 @@ func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvb.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) pvb.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} if err := client.Patch(s.ctx, &pvb, ctrlclient.MergeFrom(original)); err != nil { - log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) + s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) continue } - log.WithField("podvolumebackup", pvb.GetName()).Warn(pvb.Status.Message) + s.logger.WithField("podvolumebackup", pvb.GetName()).Warn(pvb.Status.Message) } } func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { pvrs := &velerov1api.PodVolumeRestoreList{} if err := client.List(s.ctx, pvrs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { - log.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") + s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") return } for _, pvr := range pvrs.Items { if pvr.Status.Phase != velerov1api.PodVolumeRestorePhaseInProgress { - log.Debugf("the status of podvolumerestore %q is %q, skip", pvr.GetName(), pvr.Status.Phase) + s.logger.Debugf("the status of podvolumerestore %q is %q, skip", pvr.GetName(), pvr.Status.Phase) continue } @@ -333,12 +332,12 @@ func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { Namespace: pvr.Spec.Pod.Namespace, Name: pvr.Spec.Pod.Name, }, pod); err != nil { - log.WithError(errors.WithStack(err)).Errorf("failed to get pod \"%s/%s\" of podvolumerestore %q", + s.logger.WithError(errors.WithStack(err)).Errorf("failed to get pod \"%s/%s\" of podvolumerestore %q", pvr.Spec.Pod.Namespace, pvr.Spec.Pod.Name, pvr.GetName()) continue } if pod.Spec.NodeName != s.nodeName { - log.Debugf("the node of pod referenced by podvolumebackup %q is %q, not %q, skip", pvr.GetName(), pod.Spec.NodeName, s.nodeName) + s.logger.Debugf("the node of pod referenced by podvolumebackup %q is %q, not %q, skip", pvr.GetName(), pod.Spec.NodeName, s.nodeName) continue } @@ -347,9 +346,9 @@ func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { pvr.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) pvr.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} if err := client.Patch(s.ctx, &pvr, ctrlclient.MergeFrom(original)); err != nil { - log.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) + s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) continue } - log.WithField("podvolumerestore", pvr.GetName()).Warn(pvr.Status.Message) + s.logger.WithField("podvolumerestore", pvr.GetName()).Warn(pvr.Status.Message) } } diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 33cd1ca3ed..49f2d1e320 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -28,7 +28,6 @@ import ( "sync" "time" - "github.com/apex/log" jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -913,14 +912,14 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo return false, errors.Wrapf(err, fmt.Sprintf("failed to get volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)) } if tmpVS.Status == nil || tmpVS.Status.BoundVolumeSnapshotContentName == nil || !boolptr.IsSetToTrue(tmpVS.Status.ReadyToUse) { - log.Infof("Waiting for CSI driver to reconcile volumesnapshot %s/%s. Retrying in %ds", volumeSnapshot.Namespace, volumeSnapshot.Name, interval/time.Second) + c.logger.Infof("Waiting for CSI driver to reconcile volumesnapshot %s/%s. Retrying in %ds", volumeSnapshot.Namespace, volumeSnapshot.Name, interval/time.Second) return false, nil } return true, nil }) if err == wait.ErrWaitTimeout { - log.Errorf("Timed out awaiting reconciliation of volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name) + c.logger.Errorf("Timed out awaiting reconciliation of volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name) } return err }) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index cc79bbfe81..f2d9aed806 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -20,7 +20,6 @@ import ( "context" "time" - "github.com/apex/log" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -418,19 +417,19 @@ func (b *backupSyncReconciler) locationFilterFunc(location *velerov1api.BackupSt if location.Spec.BackupSyncPeriod != nil { syncPeriod = location.Spec.BackupSyncPeriod.Duration if syncPeriod == 0 { - log.Debug("Backup sync period for this location is set to 0, skipping sync") + b.logger.Debug("Backup sync period for this location is set to 0, skipping sync") return false } if syncPeriod < 0 { - log.Debug("Backup sync period must be non-negative") + b.logger.Debug("Backup sync period must be non-negative") syncPeriod = b.defaultBackupSyncPeriod } } lastSync := location.Status.LastSyncedTime if lastSync != nil { - log.Debug("Checking if backups need to be synced at this time for this location") + b.logger.Debug("Checking if backups need to be synced at this time for this location") nextSync := lastSync.Add(syncPeriod) if time.Now().UTC().Before(nextSync) { return false From a90ba3db7c75e665d91740c7eb8005e9ad6c6e9a Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 5 Sep 2022 12:04:18 +0800 Subject: [PATCH 288/366] kopia pvbr smoking test changes Signed-off-by: Lyndon-Li --- changelogs/unreleased/5282-lyndon | 1 + .../pod_volume_backup_controller.go | 22 +-- .../pod_volume_backup_controller_test.go | 7 +- .../pod_volume_restore_controller.go | 19 +- .../restic_repository_controller.go | 20 +- .../restic_repository_controller_test.go | 8 +- pkg/persistence/object_store_layout.go | 1 + pkg/podvolume/restorer.go | 1 + pkg/podvolume/util.go | 10 + pkg/repository/backup_repo_op.go | 85 +++++++++ pkg/repository/manager.go | 27 ++- pkg/repository/mocks/Manager.go | 139 ++++++++++++++ pkg/repository/mocks/repository_manager.go | 171 ------------------ pkg/repository/provider/restic.go | 14 +- pkg/repository/provider/unified_repo.go | 11 +- pkg/repository/provider/unified_repo_test.go | 37 ++-- pkg/repository/udmrepo/kopialib/lib_repo.go | 30 +-- pkg/repository/util/backup_repo_op.go | 45 ----- pkg/uploader/kopia/snapshot.go | 11 +- pkg/uploader/provider/provider.go | 5 +- pkg/uploader/provider/restic.go | 5 +- pkg/util/logging/kopia_log.go | 12 +- 22 files changed, 359 insertions(+), 322 deletions(-) create mode 100644 changelogs/unreleased/5282-lyndon create mode 100644 pkg/repository/backup_repo_op.go create mode 100644 pkg/repository/mocks/Manager.go delete mode 100644 pkg/repository/mocks/repository_manager.go delete mode 100644 pkg/repository/util/backup_repo_op.go diff --git a/changelogs/unreleased/5282-lyndon b/changelogs/unreleased/5282-lyndon new file mode 100644 index 0000000000..ef34f89939 --- /dev/null +++ b/changelogs/unreleased/5282-lyndon @@ -0,0 +1 @@ +Add changes for problems/enhancements found during smoking test for Kopia pod volume backup/restore \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 4e07edb894..93c08ff645 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -26,7 +26,6 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" @@ -34,10 +33,10 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/metrics" + "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" - "github.com/vmware-tanzu/velero/pkg/repository/util" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -145,22 +144,19 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ }, backupLocation); err != nil { return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } - selector := labels.SelectorFromSet( - map[string]string{ - //TODO - //velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), - velerov1api.StorageLocationLabel: label.GetValidName(pvb.Spec.BackupStorageLocation), - //velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), - }, - ) - backupRepo, err := util.GetBackupRepositoryByLabel(ctx, r.Client, pvb.Namespace, selector) + + backupRepo, err := repository.GetBackupRepository(ctx, r.Client, pvb.Namespace, repository.BackupRepositoryKey{ + VolumeNamespace: pvb.Spec.Pod.Namespace, + BackupLocation: pvb.Spec.BackupStorageLocation, + RepositoryType: podvolume.GetPvbRepositoryType(&pvb), + }) if err != nil { return ctrl.Result{}, errors.Wrap(err, "error getting backup repository") } var uploaderProv provider.Provider uploaderProv, err = NewUploaderProviderFunc(ctx, r.Client, pvb.Spec.UploaderType, pvb.Spec.RepoIdentifier, - backupLocation, &backupRepo, r.CredentialGetter, repokey.RepoKeySelector(), log) + backupLocation, backupRepo, r.CredentialGetter, repokey.RepoKeySelector(), log) if err != nil { return r.updateStatusToFailed(ctx, &pvb, err, "error creating uploader", log) } diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index 03370f5382..f25b31a95f 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -80,11 +80,16 @@ func buildBackupRepo() *velerov1api.BackupRepository { }, ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, - Name: fmt.Sprintf("%s-bsl-loc-dn24h", velerov1api.DefaultNamespace), + Name: fmt.Sprintf("%s-bsl-loc-restic-dn24h", velerov1api.DefaultNamespace), Labels: map[string]string{ velerov1api.StorageLocationLabel: "bsl-loc", + velerov1api.VolumeNamespaceLabel: velerov1api.DefaultNamespace, + velerov1api.RepositoryTypeLabel: "restic", }, }, + Status: velerov1api.BackupRepositoryStatus{ + Phase: velerov1api.BackupRepositoryPhaseReady, + }, } } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 0302d1e628..df41b50f04 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -39,10 +39,9 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" - "github.com/vmware-tanzu/velero/pkg/repository/util" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -250,21 +249,17 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error getting backup storage location") } - selector := labels.SelectorFromSet( - map[string]string{ - //TODO - //velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), - velerov1api.StorageLocationLabel: label.GetValidName(req.Spec.BackupStorageLocation), - //velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), - }, - ) - backupRepo, err := util.GetBackupRepositoryByLabel(ctx, c.Client, req.Namespace, selector) + backupRepo, err := repository.GetBackupRepository(ctx, c.Client, req.Namespace, repository.BackupRepositoryKey{ + VolumeNamespace: req.Spec.Pod.Namespace, + BackupLocation: req.Spec.BackupStorageLocation, + RepositoryType: podvolume.GetPvrRepositoryType(req), + }) if err != nil { return errors.Wrap(err, "error getting backup repository") } uploaderProv, err := provider.NewUploaderProvider(ctx, c.Client, req.Spec.UploaderType, - req.Spec.RepoIdentifier, backupLocation, &backupRepo, c.credentialGetter, repokey.RepoKeySelector(), log) + req.Spec.RepoIdentifier, backupLocation, backupRepo, c.credentialGetter, repokey.RepoKeySelector(), log) if err != nil { return errors.Wrap(err, "error creating uploader") } diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 39ec50c37f..7328cf78b8 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -18,7 +18,6 @@ package controller import ( "context" - "strings" "time" "github.com/pkg/errors" @@ -173,23 +172,10 @@ func (r *ResticRepoReconciler) getRepositoryMaintenanceFrequency(req *velerov1ap } } -// ensureRepo checks to see if a repository exists, and attempts to initialize it if -// it does not exist. An error is returned if the repository can't be connected to -// or initialized. +// ensureRepo calls repo manager's PrepareRepo to ensure the repo is ready for use. +// An error is returned if the repository can't be connected to or initialized. func ensureRepo(repo *velerov1api.BackupRepository, repoManager repository.Manager) error { - if err := repoManager.ConnectToRepo(repo); err != nil { - // If the repository has not yet been initialized, the error message will always include - // the following string. This is the only scenario where we should try to initialize it. - // Other errors (e.g. "already locked") should be returned as-is since the repository - // does already exist, but it can't be connected to. - if strings.Contains(err.Error(), "Is there a repository at the following location?") { - return repoManager.InitRepo(repo) - } - - return err - } - - return nil + return repoManager.PrepareRepo(repo) } func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index 323c547100..16fa5f983f 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -34,7 +34,7 @@ import ( const testMaintenanceFrequency = 10 * time.Minute func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { - mgr := &repomokes.RepositoryManager{} + mgr := &repomokes.Manager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) } @@ -75,7 +75,7 @@ func TestPatchResticRepository(t *testing.T) { func TestCheckNotReadyRepo(t *testing.T) { rr := mockResticRepositoryCR() - reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) + reconciler := mockResticRepoReconciler(t, rr, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) @@ -107,7 +107,7 @@ func TestRunMaintenanceIfDue(t *testing.T) { func TestInitializeRepo(t *testing.T) { rr := mockResticRepositoryCR() rr.Spec.BackupStorageLocation = "default" - reconciler := mockResticRepoReconciler(t, rr, "ConnectToRepo", rr, nil) + reconciler := mockResticRepoReconciler(t, rr, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) locations := &velerov1api.BackupStorageLocation{ @@ -225,7 +225,7 @@ func TestGetRepositoryMaintenanceFrequency(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - mgr := repomokes.RepositoryManager{} + mgr := repomokes.Manager{} mgr.On("DefaultMaintenanceFrequency", mock.Anything).Return(test.freqReturn, test.freqError) reconciler := NewResticRepoReconciler( velerov1api.DefaultNamespace, diff --git a/pkg/persistence/object_store_layout.go b/pkg/persistence/object_store_layout.go index cad7479e0f..7042c40b37 100644 --- a/pkg/persistence/object_store_layout.go +++ b/pkg/persistence/object_store_layout.go @@ -40,6 +40,7 @@ func NewObjectStoreLayout(prefix string) *ObjectStoreLayout { "restic": path.Join(prefix, "restic") + "/", "metadata": path.Join(prefix, "metadata") + "/", "plugins": path.Join(prefix, "plugins") + "/", + "kopia": path.Join(prefix, "kopia") + "/", } return &ObjectStoreLayout{ diff --git a/pkg/podvolume/restorer.go b/pkg/podvolume/restorer.go index 4cc8770a2e..3b66a9be62 100644 --- a/pkg/podvolume/restorer.go +++ b/pkg/podvolume/restorer.go @@ -212,6 +212,7 @@ func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backu SnapshotID: snapshot, BackupStorageLocation: backupLocation, RepoIdentifier: repoIdentifier, + UploaderType: uploaderType, }, } if pvc != nil { diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index 7a73ed537f..a7ab78245a 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -73,6 +73,16 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod return volumes } +// GetPvbRepositoryType returns the repositoryType according to the PVB information +func GetPvbRepositoryType(pvb *velerov1api.PodVolumeBackup) string { + return getRepositoryType(pvb.Spec.UploaderType) +} + +// GetPvrRepositoryType returns the repositoryType according to the PVR information +func GetPvrRepositoryType(pvr *velerov1api.PodVolumeRestore) string { + return getRepositoryType(pvr.Spec.UploaderType) +} + // getVolumeBackupInfoForPod returns a map, of volume name -> VolumeBackupInfo, // of the PodVolumeBackups that exist for the provided pod. func getVolumeBackupInfoForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]volumeBackupInfo { diff --git a/pkg/repository/backup_repo_op.go b/pkg/repository/backup_repo_op.go new file mode 100644 index 0000000000..b60feb4fa5 --- /dev/null +++ b/pkg/repository/backup_repo_op.go @@ -0,0 +1,85 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package repository + +import ( + "context" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/label" +) + +// A BackupRepositoryKey uniquely identify a backup repository +type BackupRepositoryKey struct { + VolumeNamespace string + BackupLocation string + RepositoryType string +} + +var ( + backupRepoNotFoundError = errors.New("backup repository not found") +) + +func repoLabelsFromKey(key BackupRepositoryKey) labels.Set { + return map[string]string{ + velerov1api.VolumeNamespaceLabel: label.GetValidName(key.VolumeNamespace), + velerov1api.StorageLocationLabel: label.GetValidName(key.BackupLocation), + velerov1api.RepositoryTypeLabel: label.GetValidName(key.RepositoryType), + } +} + +// GetBackupRepository gets a backup repository through BackupRepositoryKey and ensure ready if required. +func GetBackupRepository(ctx context.Context, cli client.Client, namespace string, key BackupRepositoryKey, options ...bool) (*velerov1api.BackupRepository, error) { + var ensureReady = true + if len(options) > 0 { + ensureReady = options[0] + } + + selector := labels.SelectorFromSet(repoLabelsFromKey(key)) + + backupRepoList := &velerov1api.BackupRepositoryList{} + err := cli.List(ctx, backupRepoList, &client.ListOptions{ + Namespace: namespace, + LabelSelector: selector, + }) + + if err != nil { + return nil, errors.Wrap(err, "error getting backup repository list") + } + + if len(backupRepoList.Items) == 0 { + return nil, backupRepoNotFoundError + } + + if len(backupRepoList.Items) > 1 { + return nil, errors.Errorf("more than one BackupRepository found for workload namespace %q, backup storage location %q, repository type %q", key.VolumeNamespace, key.BackupLocation, key.RepositoryType) + } + + repo := &backupRepoList.Items[0] + + if ensureReady { + if repo.Status.Phase != velerov1api.BackupRepositoryPhaseReady { + return nil, errors.Errorf("backup repository is not ready: %s", repo.Status.Message) + } + } + + return repo, nil +} diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go index 8d9773a3b9..2ffa147805 100644 --- a/pkg/repository/manager.go +++ b/pkg/repository/manager.go @@ -55,12 +55,14 @@ type Manager interface { // InitRepo initializes a repo with the specified name and identifier. InitRepo(repo *velerov1api.BackupRepository) error - // ConnectToRepo runs the 'restic snapshots' command against the - // specified repo, and returns an error if it fails. This is - // intended to be used to ensure that the repo exists/can be - // authenticated to. + // ConnectToRepo tries to connect to the specified repo, and returns an error if it fails. + // This is intended to be used to ensure that the repo exists/can be authenticated to. ConnectToRepo(repo *velerov1api.BackupRepository) error + // PrepareRepo tries to connect to the specific repo first, if it fails because of the + // repo is not initialized, it turns to initialize the repo + PrepareRepo(repo *velerov1api.BackupRepository) error + // PruneRepo deletes unused data from a repo. PruneRepo(repo *velerov1api.BackupRepository) error @@ -108,7 +110,7 @@ func NewManager( mgr.providers[velerov1api.BackupRepositoryTypeKopia] = provider.NewUnifiedRepoProvider(credentials.CredentialGetter{ FromFile: credentialFileStore, FromSecret: credentialSecretStore, - }, mgr.log) + }, velerov1api.BackupRepositoryTypeKopia, mgr.log) return mgr } @@ -143,6 +145,21 @@ func (m *manager) ConnectToRepo(repo *velerov1api.BackupRepository) error { return prd.ConnectToRepo(context.Background(), param) } +func (m *manager) PrepareRepo(repo *velerov1api.BackupRepository) error { + m.repoLocker.Lock(repo.Name) + defer m.repoLocker.Unlock(repo.Name) + + prd, err := m.getRepositoryProvider(repo) + if err != nil { + return errors.WithStack(err) + } + param, err := m.assembleRepoParam(repo) + if err != nil { + return errors.WithStack(err) + } + return prd.PrepareRepo(context.Background(), param) +} + func (m *manager) PruneRepo(repo *velerov1api.BackupRepository) error { m.repoLocker.LockExclusive(repo.Name) defer m.repoLocker.UnlockExclusive(repo.Name) diff --git a/pkg/repository/mocks/Manager.go b/pkg/repository/mocks/Manager.go new file mode 100644 index 0000000000..5508ce9581 --- /dev/null +++ b/pkg/repository/mocks/Manager.go @@ -0,0 +1,139 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + repository "github.com/vmware-tanzu/velero/pkg/repository" + + time "time" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// Manager is an autogenerated mock type for the Manager type +type Manager struct { + mock.Mock +} + +// ConnectToRepo provides a mock function with given fields: repo +func (_m *Manager) ConnectToRepo(repo *v1.BackupRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DefaultMaintenanceFrequency provides a mock function with given fields: repo +func (_m *Manager) DefaultMaintenanceFrequency(repo *v1.BackupRepository) (time.Duration, error) { + ret := _m.Called(repo) + + var r0 time.Duration + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) time.Duration); ok { + r0 = rf(repo) + } else { + r0 = ret.Get(0).(time.Duration) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.BackupRepository) error); ok { + r1 = rf(repo) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Forget provides a mock function with given fields: _a0, _a1 +func (_m *Manager) Forget(_a0 context.Context, _a1 repository.SnapshotIdentifier) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, repository.SnapshotIdentifier) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// InitRepo provides a mock function with given fields: repo +func (_m *Manager) InitRepo(repo *v1.BackupRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PrepareRepo provides a mock function with given fields: repo +func (_m *Manager) PrepareRepo(repo *v1.BackupRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PruneRepo provides a mock function with given fields: repo +func (_m *Manager) PruneRepo(repo *v1.BackupRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UnlockRepo provides a mock function with given fields: repo +func (_m *Manager) UnlockRepo(repo *v1.BackupRepository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewManager interface { + mock.TestingT + Cleanup(func()) +} + +// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewManager(t mockConstructorTestingTNewManager) *Manager { + mock := &Manager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/mocks/repository_manager.go b/pkg/repository/mocks/repository_manager.go deleted file mode 100644 index b029556bab..0000000000 --- a/pkg/repository/mocks/repository_manager.go +++ /dev/null @@ -1,171 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License.*/ -// Code generated by mockery v2.10.2. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - - time "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/podvolume" - "github.com/vmware-tanzu/velero/pkg/repository" -) - -// RepositoryManager is an autogenerated mock type for the RepositoryManager type -type RepositoryManager struct { - mock.Mock -} - -// ConnectToRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) ConnectToRepo(repo *v1.BackupRepository) error { - ret := _m.Called(repo) - - var r0 error - if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { - r0 = rf(repo) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Forget provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 repository.SnapshotIdentifier) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, repository.SnapshotIdentifier) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DefaultMaintenanceFrequency provides a mock function with given fields: repo -func (_m *RepositoryManager) DefaultMaintenanceFrequency(repo *v1.BackupRepository) (time.Duration, error) { - ret := _m.Called(repo) - - var r0 time.Duration - if rf, ok := ret.Get(0).(func(*v1.BackupRepository) time.Duration); ok { - r0 = rf(repo) - } else { - r0 = ret.Get(0).(time.Duration) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*v1.BackupRepository) error); ok { - r1 = rf(repo) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// InitRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { - ret := _m.Called(repo) - - var r0 error - if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { - r0 = rf(repo) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NewBackupper provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (podvolume.Backupper, error) { - ret := _m.Called(_a0, _a1) - - var r0 podvolume.Backupper - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) podvolume.Backupper); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(podvolume.Backupper) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *v1.Backup) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewRestorer provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (podvolume.Restorer, error) { - ret := _m.Called(_a0, _a1) - - var r0 podvolume.Restorer - if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) podvolume.Restorer); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(podvolume.Restorer) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *v1.Restore) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// PruneRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) PruneRepo(repo *v1.BackupRepository) error { - ret := _m.Called(repo) - - var r0 error - if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { - r0 = rf(repo) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UnlockRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) UnlockRepo(repo *v1.BackupRepository) error { - ret := _m.Called(repo) - - var r0 error - if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { - r0 = rf(repo) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/pkg/repository/provider/restic.go b/pkg/repository/provider/restic.go index 4d9f63b4a6..65038f0f22 100644 --- a/pkg/repository/provider/restic.go +++ b/pkg/repository/provider/restic.go @@ -18,6 +18,7 @@ package provider import ( "context" + "strings" "time" "github.com/sirupsen/logrus" @@ -46,10 +47,19 @@ func (r *resticRepositoryProvider) ConnectToRepo(ctx context.Context, param Repo } func (r *resticRepositoryProvider) PrepareRepo(ctx context.Context, param RepoParam) error { - if err := r.InitRepo(ctx, param); err != nil { + if err := r.ConnectToRepo(ctx, param); err != nil { + // If the repository has not yet been initialized, the error message will always include + // the following string. This is the only scenario where we should try to initialize it. + // Other errors (e.g. "already locked") should be returned as-is since the repository + // does already exist, but it can't be connected to. + if strings.Contains(err.Error(), "Is there a repository at the following location?") { + return r.InitRepo(ctx, param) + } + return err } - return r.ConnectToRepo(ctx, param) + + return nil } func (r *resticRepositoryProvider) PruneRepo(ctx context.Context, param RepoParam) error { diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 18018bf155..ec71ae4c9b 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -38,6 +38,7 @@ type unifiedRepoProvider struct { credentialGetter credentials.CredentialGetter workPath string repoService udmrepo.BackupRepoService + repoBackend string log logrus.FieldLogger } @@ -50,7 +51,7 @@ var getS3BucketRegion = repoconfig.GetAWSBucketRegion var getAzureStorageDomain = repoconfig.GetAzureStorageDomain type localFuncTable struct { - getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) + getStorageVariables func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error) } @@ -69,10 +70,12 @@ const ( // NewUnifiedRepoProvider creates the service provider for Unified Repo func NewUnifiedRepoProvider( credentialGetter credentials.CredentialGetter, + repoBackend string, log logrus.FieldLogger, ) Provider { repo := unifiedRepoProvider{ credentialGetter: credentialGetter, + repoBackend: repoBackend, log: log, } @@ -303,7 +306,7 @@ func (urp *unifiedRepoProvider) GetStoreOptions(param interface{}) (map[string]s return map[string]string{}, errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param) } - storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, repoParam.BackupRepo.Spec.VolumeNamespace) + storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, urp.repoBackend, repoParam.BackupRepo.Spec.VolumeNamespace) if err != nil { return map[string]string{}, errors.Wrap(err, "error to get storage variables") } @@ -406,7 +409,7 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr return result, nil } -func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoName string) (map[string]string, error) { +func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoBackend string, repoName string) (map[string]string, error) { result := make(map[string]string) backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) @@ -426,7 +429,7 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo prefix = strings.Trim(backupLocation.Spec.ObjectStorage.Prefix, "/") } - prefix = path.Join(prefix, udmrepo.StoreOptionPrefixName, repoName) + "/" + prefix = path.Join(prefix, repoBackend, repoName) + "/" region := config["region"] diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index abcac14310..fb1c9d170f 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -233,6 +233,7 @@ func TestGetStorageVariables(t *testing.T) { name string backupLocation velerov1api.BackupStorageLocation repoName string + repoBackend string getS3BucketRegion func(string) (string, error) getAzureStorageDomain func(map[string]string) string expected map[string]string @@ -262,9 +263,10 @@ func TestGetStorageVariables(t *testing.T) { }, }, }, + repoBackend: "fake-repo-type", expected: map[string]string{ "bucket": "fake-bucket", - "prefix": "fake-prefix/unified-repo/", + "prefix": "fake-prefix/fake-repo-type/", "region": "fake-region", "fspath": "", "endpoint": "fake-url", @@ -286,9 +288,10 @@ func TestGetStorageVariables(t *testing.T) { getS3BucketRegion: func(bucket string) (string, error) { return "region from bucket: " + bucket, nil }, + repoBackend: "fake-repo-type", expected: map[string]string{ "bucket": "fake-bucket", - "prefix": "fake-prefix/unified-repo/", + "prefix": "fake-prefix/fake-repo-type/", "region": "region from bucket: fake-bucket", "fspath": "", "endpoint": "s3-region from bucket: fake-bucket.amazonaws.com", @@ -332,9 +335,10 @@ func TestGetStorageVariables(t *testing.T) { getS3BucketRegion: func(bucket string) (string, error) { return "region from bucket: " + bucket, nil }, + repoBackend: "fake-repo-type", expected: map[string]string{ "bucket": "fake-bucket-object-store", - "prefix": "fake-prefix-object-store/unified-repo/", + "prefix": "fake-prefix-object-store/fake-repo-type/", "region": "fake-region", "fspath": "", "endpoint": "fake-url", @@ -364,9 +368,10 @@ func TestGetStorageVariables(t *testing.T) { getAzureStorageDomain: func(config map[string]string) string { return config["storageDomain"] }, + repoBackend: "fake-repo-type", expected: map[string]string{ "bucket": "fake-bucket-object-store", - "prefix": "fake-prefix-object-store/unified-repo/", + "prefix": "fake-prefix-object-store/fake-repo-type/", "region": "fake-region", "fspath": "", "storageDomain": "fake-domain", @@ -386,13 +391,14 @@ func TestGetStorageVariables(t *testing.T) { }, }, }, - repoName: "//fake-name//", + repoName: "//fake-name//", + repoBackend: "fake-repo-type", getAzureStorageDomain: func(config map[string]string) string { return config["storageDomain"] }, expected: map[string]string{ "bucket": "fake-bucket", - "prefix": "fake-prefix/unified-repo/fake-name/", + "prefix": "fake-prefix/fake-repo-type/fake-name/", "region": "fake-region", "fspath": "", "storageDomain": "fake-domain", @@ -409,10 +415,11 @@ func TestGetStorageVariables(t *testing.T) { }, }, }, + repoBackend: "fake-repo-type", expected: map[string]string{ "fspath": "fake-path", "bucket": "", - "prefix": "fake-prefix/unified-repo/", + "prefix": "fake-prefix/fake-repo-type/", "region": "", }, }, @@ -423,7 +430,7 @@ func TestGetStorageVariables(t *testing.T) { getS3BucketRegion = tc.getS3BucketRegion getAzureStorageDomain = tc.getAzureStorageDomain - actual, err := getStorageVariables(&tc.backupLocation, tc.repoName) + actual, err := getStorageVariables(&tc.backupLocation, tc.repoBackend, tc.repoName) require.Equal(t, tc.expected, actual) @@ -512,7 +519,7 @@ func TestGetStoreOptions(t *testing.T) { BackupRepo: &velerov1api.BackupRepository{}, }, funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, errors.New("fake-error-2") }, }, @@ -526,7 +533,7 @@ func TestGetStoreOptions(t *testing.T) { BackupRepo: &velerov1api.BackupRepository{}, }, funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, nil }, getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { @@ -586,7 +593,7 @@ func TestPrepareRepo(t *testing.T) { repoService: new(reposervicenmocks.BackupRepoService), credStoreReturn: "fake-password", funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, errors.New("fake-store-option-error") }, }, @@ -597,7 +604,7 @@ func TestPrepareRepo(t *testing.T) { getter: new(credmock.SecretStore), credStoreReturn: "fake-password", funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, nil }, getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { @@ -618,7 +625,7 @@ func TestPrepareRepo(t *testing.T) { getter: new(credmock.SecretStore), credStoreReturn: "fake-password", funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, nil }, getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { @@ -695,7 +702,7 @@ func TestForget(t *testing.T) { getter: new(credmock.SecretStore), credStoreReturn: "fake-password", funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, nil }, getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { @@ -719,7 +726,7 @@ func TestForget(t *testing.T) { getter: new(credmock.SecretStore), credStoreReturn: "fake-password", funcTable: localFuncTable{ - getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) { return map[string]string{}, nil }, getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { diff --git a/pkg/repository/udmrepo/kopialib/lib_repo.go b/pkg/repository/udmrepo/kopialib/lib_repo.go index 45dc849276..1a32560f37 100644 --- a/pkg/repository/udmrepo/kopialib/lib_repo.go +++ b/pkg/repository/udmrepo/kopialib/lib_repo.go @@ -19,6 +19,7 @@ package kopialib import ( "context" "os" + "runtime" "strings" "sync/atomic" "time" @@ -382,12 +383,7 @@ func (kor *kopiaObjectReader) Read(p []byte) (int, error) { return 0, errors.New("object reader is closed or not open") } - n, err := kor.rawReader.Read(p) - if err != nil { - return 0, errors.Wrap(err, "error to read object") - } - - return n, nil + return kor.rawReader.Read(p) } func (kor *kopiaObjectReader) Seek(offset int64, whence int) (int64, error) { @@ -395,12 +391,7 @@ func (kor *kopiaObjectReader) Seek(offset int64, whence int) (int64, error) { return -1, errors.New("object reader is closed or not open") } - p, err := kor.rawReader.Seek(offset, whence) - if err != nil { - return -1, errors.Wrap(err, "error to seek object") - } - - return p, nil + return kor.rawReader.Seek(offset, whence) } func (kor *kopiaObjectReader) Close() error { @@ -410,7 +401,7 @@ func (kor *kopiaObjectReader) Close() error { err := kor.rawReader.Close() if err != nil { - return errors.Wrap(err, "error to close object reader") + return err } kor.rawReader = nil @@ -431,12 +422,7 @@ func (kow *kopiaObjectWriter) Write(p []byte) (int, error) { return 0, errors.New("object writer is closed or not open") } - n, err := kow.rawWriter.Write(p) - if err != nil { - return 0, errors.Wrap(err, "error to write object") - } - - return n, nil + return kow.rawWriter.Write(p) } func (kow *kopiaObjectWriter) Seek(offset int64, whence int) (int64, error) { @@ -476,7 +462,7 @@ func (kow *kopiaObjectWriter) Close() error { err := kow.rawWriter.Close() if err != nil { - return errors.Wrap(err, "error to close object writer") + return err } kow.rawWriter = nil @@ -484,9 +470,9 @@ func (kow *kopiaObjectWriter) Close() error { return nil } -// getAsyncWrites returns the number of async writes, at present, we don't support async writes +// getAsyncWrites returns the number of concurrent async writes func getAsyncWrites() int { - return 0 + return runtime.NumCPU() } // getCompressorForObject returns the compressor for an object, at present, we don't support compression diff --git a/pkg/repository/util/backup_repo_op.go b/pkg/repository/util/backup_repo_op.go deleted file mode 100644 index 0252464f48..0000000000 --- a/pkg/repository/util/backup_repo_op.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright The Velero Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import ( - "context" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/controller-runtime/pkg/client" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" -) - -// GetBackupRepositoryByLabel which find backup repository through pvbNamespace, label -// name of BackupRepository is generated with prefix volumeNamespace-backupLocation- and end with random characters -// it could not retrieve the BackupRepository CR with namespace + name. so first list all CRs with in the pvbNamespace -// then filtering the matched CR by label -func GetBackupRepositoryByLabel(ctx context.Context, cli client.Client, pvbNamespace string, selector labels.Selector) (velerov1api.BackupRepository, error) { - backupRepoList := &velerov1api.BackupRepositoryList{} - if err := cli.List(ctx, backupRepoList, &client.ListOptions{ - Namespace: pvbNamespace, - LabelSelector: selector, - }); err != nil { - return velerov1api.BackupRepository{}, errors.Wrap(err, "error getting backup repository list") - } else if len(backupRepoList.Items) == 1 { - return backupRepoList.Items[0], nil - } else { - return velerov1api.BackupRepository{}, errors.Errorf("unexpectedly find %d BackupRepository for workload namespace %s with label selector %v", len(backupRepoList.Items), pvbNamespace, selector) - } -} diff --git a/pkg/uploader/kopia/snapshot.go b/pkg/uploader/kopia/snapshot.go index fc5a71fa7f..904c235a1d 100644 --- a/pkg/uploader/kopia/snapshot.go +++ b/pkg/uploader/kopia/snapshot.go @@ -30,6 +30,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/kopia/kopia/fs" "github.com/kopia/kopia/fs/localfs" @@ -110,7 +111,9 @@ func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter rep if err != nil { return nil, false, errors.Wrap(err, "Unable to get local filesystem entry") } - snapID, snapshotSize, err := SnapshotSource(ctx, repoWriter, fsUploader, sourceInfo, rootDir, parentSnapshot, log, "Kopia Uploader") + + kopiaCtx := logging.SetupKopiaLog(ctx, log) + snapID, snapshotSize, err := SnapshotSource(kopiaCtx, repoWriter, fsUploader, sourceInfo, rootDir, parentSnapshot, log, "Kopia Uploader") if err != nil { return nil, false, err } @@ -261,7 +264,9 @@ func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sour func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *KopiaProgress, snapshotID, dest string, log logrus.FieldLogger, cancleCh chan struct{}) (int64, int32, error) { log.Info("Start to restore...") - rootEntry, err := snapshotfs.FilesystemEntryFromIDWithPath(ctx, rep, snapshotID, false) + kopiaCtx := logging.SetupKopiaLog(ctx, log) + + rootEntry, err := snapshotfs.FilesystemEntryFromIDWithPath(kopiaCtx, rep, snapshotID, false) if err != nil { return 0, 0, errors.Wrapf(err, "Unable to get filesystem entry for snapshot %v", snapshotID) } @@ -279,7 +284,7 @@ func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *KopiaProg IgnorePermissionErrors: true, } - stat, err := restore.Entry(ctx, rep, output, rootEntry, restore.Options{ + stat, err := restore.Entry(kopiaCtx, rep, output, rootEntry, restore.Options{ Parallel: runtime.NumCPU(), RestoreDirEntryAtDepth: math.MaxInt32, Cancel: cancleCh, diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index 1a74214f12..1a0a57682a 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -73,7 +73,10 @@ func NewUploaderProvider( return nil, errors.New("uninitialized FileStore credentail is not supported") } if uploaderType == uploader.KopiaType { - if err := provider.NewUnifiedRepoProvider(*credGetter, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { + // We use the hardcode repositoryType velerov1api.BackupRepositoryTypeKopia for now, because we have only one implementation of unified repo. + // TODO: post v1.10, replace the hardcode. In future, when we have multiple implementations of Unified Repo (besides Kopia), we will add the + // repositoryType to BSL, because by then, we are not able to hardcode the repositoryType to BackupRepositoryTypeKopia for Unified Repo. + if err := provider.NewUnifiedRepoProvider(*credGetter, velerov1api.BackupRepositoryTypeKopia, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { return nil, errors.Wrap(err, "failed to connect repository") } return NewKopiaUploaderProvider(ctx, credGetter, backupRepo, log) diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go index 549d395778..be438d0c84 100644 --- a/pkg/uploader/provider/restic.go +++ b/pkg/uploader/provider/restic.go @@ -120,7 +120,10 @@ func (rp *resticProvider) RunBackup( backupCmd := ResticBackupCMDFunc(rp.repoIdentifier, rp.credentialsFile, path, tags) backupCmd.Env = rp.cmdEnv backupCmd.CACertFile = rp.caCertFile - backupCmd.ExtraFlags = rp.extraFlags + if len(rp.extraFlags) != 0 { + backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, rp.extraFlags...) + } + if parentSnapshot != "" { backupCmd.ExtraFlags = append(backupCmd.ExtraFlags, fmt.Sprintf("--parent=%s", parentSnapshot)) } diff --git a/pkg/util/logging/kopia_log.go b/pkg/util/logging/kopia_log.go index 59fac6dc38..d70784d237 100644 --- a/pkg/util/logging/kopia_log.go +++ b/pkg/util/logging/kopia_log.go @@ -40,22 +40,22 @@ func SetupKopiaLog(ctx context.Context, logger logrus.FieldLogger) context.Conte } func (kl *kopiaLog) Debugf(msg string, args ...interface{}) { - logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger := kl.logger.WithField("logModule", kl.getLogModule()) logger.Debugf(msg, args...) } func (kl *kopiaLog) Debugw(msg string, keyValuePairs ...interface{}) { - logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger := kl.logger.WithField("logModule", kl.getLogModule()) logger.WithFields(getLogFields(keyValuePairs...)).Debug(msg) } func (kl *kopiaLog) Infof(msg string, args ...interface{}) { - logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger := kl.logger.WithField("logModule", kl.getLogModule()) logger.Infof(msg, args...) } func (kl *kopiaLog) Warnf(msg string, args ...interface{}) { - logger := kl.logger.WithField("logSource", kl.getLogSource()) + logger := kl.logger.WithField("logModule", kl.getLogModule()) logger.Warnf(msg, args...) } @@ -64,14 +64,14 @@ func (kl *kopiaLog) Warnf(msg string, args ...interface{}) { // affect Velero's workflow. func (kl *kopiaLog) Errorf(msg string, args ...interface{}) { logger := kl.logger.WithFields(logrus.Fields{ - "logSource": kl.getLogSource(), + "logModule": kl.getLogModule(), "sublevel": "error", }) logger.Warnf(msg, args...) } -func (kl *kopiaLog) getLogSource() string { +func (kl *kopiaLog) getLogModule() string { return "kopia/" + kl.module } From a12024887fe9af2de4fbb53ba4420fdbb8d86218 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 7 Sep 2022 21:44:45 +0800 Subject: [PATCH 289/366] fix issue 4874 Signed-off-by: Lyndon-Li --- pkg/cmd/server/server.go | 10 ++--- pkg/nodeagent/node_agent.go | 69 ++++++++++++++++++++++++++++++ pkg/podvolume/backupper.go | 19 +++++--- pkg/podvolume/backupper_factory.go | 5 ++- pkg/podvolume/util.go | 15 +++++++ 5 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 pkg/nodeagent/node_agent.go diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2253d9dbc6..eda50b6711 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -85,6 +85,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" + + "github.com/vmware-tanzu/velero/pkg/nodeagent" ) const ( @@ -112,9 +114,6 @@ const ( // defaultCredentialsDirectory is the path on disk where credential // files will be written to defaultCredentialsDirectory = "/tmp/credentials" - - // daemonSet is the name of the Velero restic daemonset. - daemonSet = "restic" ) type serverConfig struct { @@ -535,7 +534,7 @@ var defaultRestorePriorities = []string{ func (s *server) initRestic() error { // warn if restic daemonset does not exist - if _, err := s.kubeClient.AppsV1().DaemonSets(s.namespace).Get(s.ctx, daemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) { + if err := nodeagent.IsRunning(s.ctx, s.kubeClient, s.namespace); err == nodeagent.DaemonsetNotFound { s.logger.Warn("Velero restic daemonset not found; restic backups/restores will not work until it's created") } else if err != nil { s.logger.WithError(errors.WithStack(err)).Warn("Error checking for existence of velero restic daemonset") @@ -619,7 +618,8 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string client.NewDynamicFactory(s.dynamicClient), podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), podvolume.NewBackupperFactory(s.repoLocker, s.repoEnsurer, s.veleroClient, s.kubeClient.CoreV1(), - s.kubeClient.CoreV1(), s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), + s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), + s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), s.config.podVolumeOperationTimeout, s.config.defaultVolumesToRestic, s.config.clientPageSize, diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go new file mode 100644 index 0000000000..f4fcd92500 --- /dev/null +++ b/pkg/nodeagent/node_agent.go @@ -0,0 +1,69 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nodeagent + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "k8s.io/client-go/kubernetes" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +const ( + // daemonSet is the name of the Velero restic daemonset. + daemonSet = "restic" +) + +var ( + DaemonsetNotFound = errors.New("daemonset not found") +) + +// IsRunning checks if the node agent daemonset is running properly. If not, return the error found +func IsRunning(ctx context.Context, kubeClient kubernetes.Interface, namespace string) error { + if _, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, daemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) { + return DaemonsetNotFound + } else if err != nil { + return err + } else { + return nil + } +} + +// IsRunningInNode checks if the node agent daemonset pod is running properly in a specified node. If not, return the error found +func IsRunningInNode(ctx context.Context, namespace string, nodeName string, podClient corev1client.PodsGetter) error { + if nodeName == "" { + return errors.New("node name is empty") + } + + pods, err := podClient.Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: fmt.Sprintf("name=%s", daemonSet)}) + if err != nil { + return errors.Wrap(err, "failed to list daemonset pods") + } + + for _, pod := range pods.Items { + if pod.Spec.NodeName == nodeName { + return nil + } + } + + return errors.Errorf("daemonset pod not found in node %s", nodeName) +} diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index f4de4fb0d7..476b3e4f17 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -32,6 +32,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/nodeagent" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -49,6 +50,7 @@ type backupper struct { veleroClient clientset.Interface pvcClient corev1client.PersistentVolumeClaimsGetter pvClient corev1client.PersistentVolumesGetter + podClient corev1client.PodsGetter uploaderType string results map[string]chan *velerov1api.PodVolumeBackup @@ -63,6 +65,7 @@ func newBackupper( veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, + podClient corev1client.PodsGetter, uploaderType string, log logrus.FieldLogger, ) *backupper { @@ -73,6 +76,7 @@ func newBackupper( veleroClient: veleroClient, pvcClient: pvcClient, pvClient: pvClient, + podClient: podClient, uploaderType: uploaderType, results: make(map[string]chan *velerov1api.PodVolumeBackup), @@ -121,6 +125,16 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. return nil, []error{err} } + err = IsPodQualified(pod) + if err != nil { + return nil, []error{err} + } + + err = nodeagent.IsRunningInNode(b.ctx, backup.Namespace, pod.Spec.NodeName, b.podClient) + if err != nil { + return nil, []error{err} + } + // get a single non-exclusive lock since we'll wait for all individual // backups to be complete before releasing it. b.repoLocker.Lock(repo.Name) @@ -167,11 +181,6 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. } } - // ignore non-running pods - if pod.Status.Phase != corev1api.PodRunning { - log.Warnf("Skipping volume %s in pod %s/%s - pod not running", volumeName, pod.Namespace, pod.Name) - continue - } // hostPath volumes are not supported because they're not mounted into /var/lib/kubelet/pods, so our // daemonset pod has no way to access their data. isHostPath, err := isHostPathVolume(&volume, pvc, b.pvClient.PersistentVolumes()) diff --git a/pkg/podvolume/backupper_factory.go b/pkg/podvolume/backupper_factory.go index 5cbd823e24..7b87865e22 100644 --- a/pkg/podvolume/backupper_factory.go +++ b/pkg/podvolume/backupper_factory.go @@ -43,6 +43,7 @@ func NewBackupperFactory(repoLocker *repository.RepoLocker, veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, + podClient corev1client.PodsGetter, repoInformerSynced cache.InformerSynced, log logrus.FieldLogger) BackupperFactory { return &backupperFactory{ @@ -51,6 +52,7 @@ func NewBackupperFactory(repoLocker *repository.RepoLocker, veleroClient: veleroClient, pvcClient: pvcClient, pvClient: pvClient, + podClient: podClient, repoInformerSynced: repoInformerSynced, log: log, } @@ -62,6 +64,7 @@ type backupperFactory struct { veleroClient clientset.Interface pvcClient corev1client.PersistentVolumeClaimsGetter pvClient corev1client.PersistentVolumesGetter + podClient corev1client.PodsGetter repoInformerSynced cache.InformerSynced log logrus.FieldLogger } @@ -77,7 +80,7 @@ func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1ap }, ) - b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, uploaderType, bf.log) + b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, bf.podClient, uploaderType, bf.log) go informer.Run(ctx.Done()) if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, bf.repoInformerSynced) { diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index a7ab78245a..3652e50719 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -22,6 +22,7 @@ import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/pkg/errors" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/uploader" @@ -295,3 +296,17 @@ func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) [ } return podVolumes } + +// IsPodQualified checks if the pod's status is qualified for a PVB/PVR to backup/restore its volumes. +// If no, return the error found +func IsPodQualified(pod *corev1api.Pod) error { + if pod.Spec.NodeName == "" { + return errors.Errorf("pod is not scheduled, name=%s, namespace=%s, status=%s", pod.Name, pod.Namespace, pod.Status.Phase) + } + + if pod.Status.Phase != corev1api.PodRunning { + return errors.Errorf("pod is not running, name=%s, namespace=%s, status=%s", pod.Name, pod.Namespace, pod.Status.Phase) + } + + return nil +} From 8496b43e37fc0e97eac03ef61c2b162c4a78f7da Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 7 Sep 2022 17:57:52 +0800 Subject: [PATCH 290/366] refactor repo ensurer Signed-off-by: Lyndon-Li --- changelogs/unreleased/5308-lyndon | 1 + pkg/cmd/server/server.go | 2 +- pkg/repository/backup_repo_op.go | 34 ++++- pkg/repository/backup_repo_op_test.go | 152 +++++++++++++++++++++ pkg/repository/ensurer.go | 190 +++++++------------------- 5 files changed, 236 insertions(+), 143 deletions(-) create mode 100644 changelogs/unreleased/5308-lyndon create mode 100644 pkg/repository/backup_repo_op_test.go diff --git a/changelogs/unreleased/5308-lyndon b/changelogs/unreleased/5308-lyndon new file mode 100644 index 0000000000..5e9c4a544c --- /dev/null +++ b/changelogs/unreleased/5308-lyndon @@ -0,0 +1 @@ +Refactor the repoEnsurer code to use controller runtime client and wrap some common BackupRepository operations to share with other modules \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2253d9dbc6..932b2fa7d4 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -547,7 +547,7 @@ func (s *server) initRestic() error { } s.repoLocker = repository.NewRepoLocker() - s.repoEnsurer = repository.NewRepositoryEnsurer(s.sharedInformerFactory.Velero().V1().BackupRepositories(), s.veleroClient.VeleroV1(), s.logger) + s.repoEnsurer = repository.NewRepositoryEnsurer(s.mgr.GetClient(), s.logger) s.repoManager = repository.NewManager(s.namespace, s.mgr.GetClient(), s.repoLocker, s.repoEnsurer, s.credentialFileStore, s.credentialSecretStore, s.logger) diff --git a/pkg/repository/backup_repo_op.go b/pkg/repository/backup_repo_op.go index b60feb4fa5..237d8ddafb 100644 --- a/pkg/repository/backup_repo_op.go +++ b/pkg/repository/backup_repo_op.go @@ -18,8 +18,10 @@ package repository import ( "context" + "fmt" "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" @@ -35,7 +37,8 @@ type BackupRepositoryKey struct { } var ( - backupRepoNotFoundError = errors.New("backup repository not found") + backupRepoNotFoundError = errors.New("backup repository not found") + backupRepoNotProvisionedError = errors.New("backup repository not provisioned") ) func repoLabelsFromKey(key BackupRepositoryKey) labels.Set { @@ -76,10 +79,37 @@ func GetBackupRepository(ctx context.Context, cli client.Client, namespace strin repo := &backupRepoList.Items[0] if ensureReady { - if repo.Status.Phase != velerov1api.BackupRepositoryPhaseReady { + if repo.Status.Phase == velerov1api.BackupRepositoryPhaseNotReady { return nil, errors.Errorf("backup repository is not ready: %s", repo.Status.Message) } + + if repo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { + return nil, backupRepoNotProvisionedError + } } return repo, nil } + +func newBackupRepository(namespace string, key BackupRepositoryKey) *velerov1api.BackupRepository { + return &velerov1api.BackupRepository{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: fmt.Sprintf("%s-%s-%s-", key.VolumeNamespace, key.BackupLocation, key.RepositoryType), + Labels: repoLabelsFromKey(key), + }, + Spec: velerov1api.BackupRepositorySpec{ + VolumeNamespace: key.VolumeNamespace, + BackupStorageLocation: key.BackupLocation, + RepositoryType: key.RepositoryType, + }, + } +} + +func isBackupRepositoryNotFoundError(err error) bool { + return (err == backupRepoNotFoundError) +} + +func isBackupRepositoryNotProvisionedError(err error) bool { + return (err == backupRepoNotProvisionedError) +} diff --git a/pkg/repository/backup_repo_op_test.go b/pkg/repository/backup_repo_op_test.go new file mode 100644 index 0000000000..104c4d6163 --- /dev/null +++ b/pkg/repository/backup_repo_op_test.go @@ -0,0 +1,152 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package repository + +import ( + "context" + "fmt" + + "github.com/stretchr/testify/assert" + + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func buildBackupRepo(key BackupRepositoryKey, phase velerov1api.BackupRepositoryPhase, seqNum string) velerov1api.BackupRepository { + return velerov1api.BackupRepository{ + Spec: velerov1api.BackupRepositorySpec{ResticIdentifier: ""}, + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "BackupRepository", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: fmt.Sprintf("%s-%s-%s-%s", key.VolumeNamespace, key.BackupLocation, key.RepositoryType, seqNum), + Labels: map[string]string{ + velerov1api.StorageLocationLabel: key.BackupLocation, + velerov1api.VolumeNamespaceLabel: key.VolumeNamespace, + velerov1api.RepositoryTypeLabel: key.RepositoryType, + }, + }, + Status: velerov1api.BackupRepositoryStatus{ + Phase: phase, + }, + } +} + +func buildBackupRepoPointer(key BackupRepositoryKey, phase velerov1api.BackupRepositoryPhase, seqNum string) *velerov1api.BackupRepository { + value := buildBackupRepo(key, phase, seqNum) + return &value +} + +func TestGetBackupRepository(t *testing.T) { + testCases := []struct { + name string + backupRepositories []velerov1api.BackupRepository + ensureReady bool + backupRepositoryKey BackupRepositoryKey + expected *velerov1api.BackupRepository + expectedErr string + }{ + { + name: "repository not found", + expectedErr: "backup repository not found", + }, + { + name: "found more than one repository", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns", "fake-bsl", "fake-repository-type"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns", "fake-bsl", "fake-repository-type"}, velerov1api.BackupRepositoryPhaseReady, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns", "fake-bsl", "fake-repository-type"}, + expectedErr: "more than one BackupRepository found for workload namespace \"fake-volume-ns\", backup storage location \"fake-bsl\", repository type \"fake-repository-type\"", + }, + { + name: "repository not ready, not expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNotReady, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + expected: buildBackupRepoPointer(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNotReady, "02"), + }, + { + name: "repository is new, not expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNew, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + expected: buildBackupRepoPointer(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNew, "02"), + }, + { + name: "repository not ready, expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNotReady, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + ensureReady: true, + expectedErr: "backup repository is not ready: ", + }, + { + name: "repository is new, expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNew, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + ensureReady: true, + expectedErr: "backup repository not provisioned", + }, + { + name: "repository ready, expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseNotReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseReady, "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + ensureReady: true, + expected: buildBackupRepoPointer(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseReady, "02"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + clientBuilder := velerotest.NewFakeControllerRuntimeClientBuilder(t) + clientBuilder.WithLists(&velerov1api.BackupRepositoryList{ + Items: tc.backupRepositories, + }) + fakeClient := clientBuilder.Build() + + backupRepo, err := GetBackupRepository(context.Background(), fakeClient, velerov1api.DefaultNamespace, tc.backupRepositoryKey, tc.ensureReady) + + if backupRepo != nil { + backupRepo.ResourceVersion = tc.expected.ResourceVersion + require.Equal(t, *tc.expected, *backupRepo) + } else { + require.Equal(t, tc.expected, backupRepo) + } + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/ensurer.go b/pkg/repository/ensurer.go index 7a7e48d9a9..5527ac7422 100644 --- a/pkg/repository/ensurer.go +++ b/pkg/repository/ensurer.go @@ -18,92 +18,34 @@ package repository import ( "context" - "fmt" "sync" "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + "k8s.io/apimachinery/pkg/util/wait" + + "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - "github.com/vmware-tanzu/velero/pkg/label" ) // RepositoryEnsurer ensures that backup repositories are created and ready. type RepositoryEnsurer struct { log logrus.FieldLogger - repoLister velerov1listers.BackupRepositoryLister - repoClient velerov1client.BackupRepositoriesGetter - - repoChansLock sync.Mutex - repoChans map[string]chan *velerov1api.BackupRepository + repoClient client.Client // repoLocksMu synchronizes reads/writes to the repoLocks map itself // since maps are not threadsafe. repoLocksMu sync.Mutex - repoLocks map[repoKey]*sync.Mutex -} - -type repoKey struct { - volumeNamespace string - backupLocation string - repositoryType string + repoLocks map[BackupRepositoryKey]*sync.Mutex } -func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *RepositoryEnsurer { - r := &RepositoryEnsurer{ +func NewRepositoryEnsurer(repoClient client.Client, log logrus.FieldLogger) *RepositoryEnsurer { + return &RepositoryEnsurer{ log: log, - repoLister: repoInformer.Lister(), repoClient: repoClient, - repoChans: make(map[string]chan *velerov1api.BackupRepository), - repoLocks: make(map[repoKey]*sync.Mutex), - } - - repoInformer.Informer().AddEventHandler( - cache.ResourceEventHandlerFuncs{ - UpdateFunc: func(old, upd interface{}) { - oldObj := old.(*velerov1api.BackupRepository) - newObj := upd.(*velerov1api.BackupRepository) - - // we're only interested in phase-changing updates - if oldObj.Status.Phase == newObj.Status.Phase { - return - } - - // we're only interested in updates where the updated object is either Ready or NotReady - if newObj.Status.Phase != velerov1api.BackupRepositoryPhaseReady && newObj.Status.Phase != velerov1api.BackupRepositoryPhaseNotReady { - return - } - - r.repoChansLock.Lock() - defer r.repoChansLock.Unlock() - - key := repoLabels(newObj.Spec.VolumeNamespace, newObj.Spec.BackupStorageLocation, newObj.Spec.RepositoryType).String() - repoChan, ok := r.repoChans[key] - if !ok { - log.Debugf("No ready channel found for repository %s/%s", newObj.Namespace, newObj.Name) - return - } - - repoChan <- newObj - }, - }, - ) - - return r -} - -func repoLabels(volumeNamespace, backupLocation, repositoryType string) labels.Set { - return map[string]string{ - velerov1api.VolumeNamespaceLabel: label.GetValidName(volumeNamespace), - velerov1api.StorageLocationLabel: label.GetValidName(backupLocation), - velerov1api.RepositoryTypeLabel: label.GetValidName(repositoryType), + repoLocks: make(map[BackupRepositoryKey]*sync.Mutex), } } @@ -112,112 +54,80 @@ func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam return nil, errors.Errorf("wrong parameters, namespace %q, backup storage location %q, repository type %q", volumeNamespace, backupLocation, repositoryType) } + backupRepoKey := BackupRepositoryKey{volumeNamespace, backupLocation, repositoryType} + log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation).WithField("repositoryType", repositoryType) // It's only safe to have one instance of this method executing concurrently for a - // given volumeNamespace + backupLocation + repositoryType, so synchronize based on that. It's fine - // to run concurrently for *different* namespaces/locations. If you had 2 goroutines + // given BackupRepositoryKey, so synchronize based on that. It's fine + // to run concurrently for *different* BackupRepositoryKey. If you had 2 goroutines // running this for the same inputs, both might find no BackupRepository exists, then - // both would create new ones for the same namespace/location. + // both would create new ones for the same BackupRepositoryKey. // // This issue could probably be avoided if we had a deterministic name for - // each restic repository, and we just tried to create it, checked for an + // each BackupRepository and we just tried to create it, checked for an // AlreadyExists err, and then waited for it to be ready. However, there are // already repositories in the wild with non-deterministic names (i.e. using // GenerateName) which poses a backwards compatibility problem. log.Debug("Acquiring lock") - repoMu := r.repoLock(volumeNamespace, backupLocation, repositoryType) + repoMu := r.repoLock(backupRepoKey) repoMu.Lock() defer func() { repoMu.Unlock() log.Debug("Released lock") }() - log.Debug("Acquired lock") - - selector := labels.SelectorFromSet(repoLabels(volumeNamespace, backupLocation, repositoryType)) - - repos, err := r.repoLister.BackupRepositories(namespace).List(selector) - if err != nil { - return nil, errors.WithStack(err) - } - if len(repos) > 1 { - return nil, errors.Errorf("more than one BackupRepository found for workload namespace %q, backup storage location %q, repository type %q", volumeNamespace, backupLocation, repositoryType) + repo, err := GetBackupRepository(ctx, r.repoClient, namespace, backupRepoKey, true) + if err == nil { + log.Debug("Ready repository found") + return repo, nil } - if len(repos) == 1 { - if repos[0].Status.Phase != velerov1api.BackupRepositoryPhaseReady { - return nil, errors.Errorf("restic repository is not ready: %s", repos[0].Status.Message) - } - log.Debug("Ready repository found") - return repos[0], nil + if !isBackupRepositoryNotFoundError(err) { + return nil, errors.WithStack(err) } log.Debug("No repository found, creating one") // no repo found: create one and wait for it to be ready - repo := &velerov1api.BackupRepository{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - GenerateName: fmt.Sprintf("%s-%s-%s-", volumeNamespace, backupLocation, repositoryType), - Labels: repoLabels(volumeNamespace, backupLocation, repositoryType), - }, - Spec: velerov1api.BackupRepositorySpec{ - VolumeNamespace: volumeNamespace, - BackupStorageLocation: backupLocation, - RepositoryType: repositoryType, - }, - } - - repoChan := r.getRepoChan(selector.String()) - defer func() { - delete(r.repoChans, selector.String()) - close(repoChan) - }() - - if _, err := r.repoClient.BackupRepositories(namespace).Create(context.TODO(), repo, metav1.CreateOptions{}); err != nil { - return nil, errors.Wrapf(err, "unable to create restic repository resource") - } - - select { - // repositories should become either ready or not ready quickly if they're - // newly created. - case <-time.After(time.Minute): - return nil, errors.New("timed out waiting for restic repository to become ready") - case <-ctx.Done(): - return nil, errors.New("timed out waiting for restic repository to become ready") - case res := <-repoChan: - - if res.Status.Phase == velerov1api.BackupRepositoryPhaseNotReady { - return nil, errors.Errorf("restic repository is not ready: %s", res.Status.Message) - } - - return res, nil - } + return r.createBackupRepositoryAndWait(ctx, namespace, backupRepoKey) } -func (r *RepositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { - r.repoChansLock.Lock() - defer r.repoChansLock.Unlock() - - r.repoChans[name] = make(chan *velerov1api.BackupRepository) - return r.repoChans[name] -} - -func (r *RepositoryEnsurer) repoLock(volumeNamespace, backupLocation, repositoryType string) *sync.Mutex { +func (r *RepositoryEnsurer) repoLock(key BackupRepositoryKey) *sync.Mutex { r.repoLocksMu.Lock() defer r.repoLocksMu.Unlock() - key := repoKey{ - volumeNamespace: volumeNamespace, - backupLocation: backupLocation, - repositoryType: repositoryType, - } - if r.repoLocks[key] == nil { r.repoLocks[key] = new(sync.Mutex) } return r.repoLocks[key] } + +func (r *RepositoryEnsurer) createBackupRepositoryAndWait(ctx context.Context, namespace string, backupRepoKey BackupRepositoryKey) (*velerov1api.BackupRepository, error) { + toCreate := newBackupRepository(namespace, backupRepoKey) + if err := r.repoClient.Create(ctx, toCreate, &client.CreateOptions{}); err != nil { + return nil, errors.Wrap(err, "unable to create backup repository resource") + } + + var repo *velerov1api.BackupRepository + checkFunc := func(ctx context.Context) (bool, error) { + found, err := GetBackupRepository(ctx, r.repoClient, namespace, backupRepoKey, true) + if err == nil { + repo = found + return true, nil + } else if isBackupRepositoryNotFoundError(err) || isBackupRepositoryNotProvisionedError(err) { + return false, nil + } else { + return false, err + } + } + + err := wait.PollWithContext(ctx, time.Millisecond*500, time.Minute, checkFunc) + if err != nil { + return nil, errors.Wrap(err, "failed to wait BackupRepository") + } else { + return repo, nil + } +} From dc704719098bda06ae86025556ac6324c044d4f4 Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Wed, 7 Sep 2022 11:48:34 +0800 Subject: [PATCH 291/366] Change the status of restore to completed from partially failed when restore empty backup Signed-off-by: allenxu404 --- changelogs/unreleased/5314-allenxu404 | 1 + pkg/restore/restore.go | 6 +++++ pkg/restore/restore_test.go | 36 +++++++++++++-------------- 3 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/5314-allenxu404 diff --git a/changelogs/unreleased/5314-allenxu404 b/changelogs/unreleased/5314-allenxu404 new file mode 100644 index 0000000000..9125ad1310 --- /dev/null +++ b/changelogs/unreleased/5314-allenxu404 @@ -0,0 +1 @@ +Change the status of restore to completed from partially failed when restore empty backup \ No newline at end of file diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index c3a022c566..044fb50f43 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -403,6 +403,12 @@ func (ctx *restoreContext) execute() (Result, Result) { ctx.restoreDir = dir backupResources, err := archive.NewParser(ctx.log, ctx.fileSystem).Parse(ctx.restoreDir) + // If ErrNotExist occurs, it implies that the backup to be restored includes zero items. + // Need to add a warning about it and jump out of the function. + if errors.Cause(err) == archive.ErrNotExist { + warnings.AddVeleroError(errors.Wrap(err, "zero items to be restored")) + return warnings, errs + } if err != nil { errs.AddVeleroError(errors.Wrap(err, "error parsing backup contents")) return warnings, errs diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 404d45e1a5..41b0743eb4 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -762,14 +762,15 @@ func TestInvalidTarballContents(t *testing.T) { tarball io.Reader want map[*test.APIResource][]string wantErrs Result + wantWarnings Result }{ { - name: "empty tarball returns an error", + name: "empty tarball returns a warning", restore: defaultRestore().Result(), backup: defaultBackup().Result(), tarball: test.NewTarWriter(t). Done(), - wantErrs: Result{ + wantWarnings: Result{ Velero: []string{archive.ErrNotExist.Error()}, }, }, @@ -820,33 +821,32 @@ func TestInvalidTarballContents(t *testing.T) { nil, // snapshot location lister nil, // volume snapshotter getter ) - - assertEmptyResults(t, warnings) - assertWantErrs(t, tc.wantErrs, errs) + assertWantErrsOrWarnings(t, tc.wantWarnings, warnings) + assertWantErrsOrWarnings(t, tc.wantErrs, errs) assertAPIContents(t, h, tc.want) }) } } -func assertWantErrs(t *testing.T, wantErrRes Result, errRes Result) { +func assertWantErrsOrWarnings(t *testing.T, wantRes Result, res Result) { t.Helper() - if wantErrRes.Velero != nil { - assert.Equal(t, len(wantErrRes.Velero), len(errRes.Velero)) - for i := range errRes.Velero { - assert.Contains(t, errRes.Velero[i], wantErrRes.Velero[i]) + if wantRes.Velero != nil { + assert.Equal(t, len(wantRes.Velero), len(res.Velero)) + for i := range res.Velero { + assert.Contains(t, res.Velero[i], wantRes.Velero[i]) } } - if wantErrRes.Namespaces != nil { - assert.Equal(t, len(wantErrRes.Namespaces), len(errRes.Namespaces)) - for ns := range errRes.Namespaces { - assert.Equal(t, len(wantErrRes.Namespaces[ns]), len(errRes.Namespaces[ns])) - for i := range errRes.Namespaces[ns] { - assert.Contains(t, errRes.Namespaces[ns][i], wantErrRes.Namespaces[ns][i]) + if wantRes.Namespaces != nil { + assert.Equal(t, len(wantRes.Namespaces), len(res.Namespaces)) + for ns := range res.Namespaces { + assert.Equal(t, len(wantRes.Namespaces[ns]), len(res.Namespaces[ns])) + for i := range res.Namespaces[ns] { + assert.Contains(t, res.Namespaces[ns][i], wantRes.Namespaces[ns][i]) } } } - if wantErrRes.Cluster != nil { - assert.Equal(t, wantErrRes.Cluster, errRes.Cluster) + if wantRes.Cluster != nil { + assert.Equal(t, wantRes.Cluster, res.Cluster) } } From 901bec30ddc03e21898bc256a542083635ab47c8 Mon Sep 17 00:00:00 2001 From: Aaron Arias <33655005+aaronariasperez@users.noreply.github.com> Date: Thu, 8 Sep 2022 12:40:51 +0200 Subject: [PATCH 292/366] A little note about TTL expiration I think is necessary this little comment about TTL expiration, because it can be confusing when the expiration time has passed and the data allocated and the snapshots are not erased at that time. Signed-off-by: Aaron Arias <33655005+aaronariasperez@users.noreply.github.com> --- site/content/docs/v1.9/how-velero-works.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/content/docs/v1.9/how-velero-works.md b/site/content/docs/v1.9/how-velero-works.md index 19fc89a94f..227c927c1a 100644 --- a/site/content/docs/v1.9/how-velero-works.md +++ b/site/content/docs/v1.9/how-velero-works.md @@ -88,6 +88,8 @@ When you create a backup, you can specify a TTL (time to live) by adding the fla The TTL flag allows the user to specify the backup retention period with the value specified in hours, minutes and seconds in the form `--ttl 24h0m0s`. If not specified, a default TTL value of 30 days will be applied. +The effects of expiration are not applied immediately, they are applied when the gc-controller runs its reconciliation loop every hour. + If backup fails to delete, a label `velero.io/gc-failure=` will be added to the backup custom resource. You can use this label to filter and select backups that failed to delete. From 596114b4279d8b8472d9f3bc1f8967df17186efe Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Fri, 1 Apr 2022 13:29:52 -0400 Subject: [PATCH 293/366] Add credentials to volume snapshot locations. This is analogous to the BSL creds work that was done previously, but for VSLs instead. Signed-off-by: Scott Seago --- changelogs/unreleased/4864-sseago | 1 + .../velero.io_volumesnapshotlocations.yaml | 18 ++++++++ config/crd/v1/crds/crds.go | 2 +- .../v1/volume_snapshot_location_type.go | 9 +++- pkg/apis/velero/v1/zz_generated.deepcopy.go | 5 +++ .../volume_snapshot_location_builder.go | 10 ++++- pkg/cmd/cli/snapshotlocation/create.go | 35 +++++++++++---- pkg/cmd/cli/snapshotlocation/set.go | 24 +++++++--- pkg/cmd/server/server.go | 2 + pkg/controller/backup_controller.go | 13 ++++++ pkg/controller/backup_controller_test.go | 3 ++ pkg/restore/pv_restorer.go | 11 ++++- pkg/restore/restore.go | 11 +++-- pkg/volume/snapshotlocation.go | 44 +++++++++++++++++++ .../main/api-types/volumesnapshotlocation.md | 6 +++ site/content/docs/main/locations.md | 6 ++- site/content/docs/main/troubleshooting.md | 18 ++++++-- 17 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 changelogs/unreleased/4864-sseago create mode 100644 pkg/volume/snapshotlocation.go diff --git a/changelogs/unreleased/4864-sseago b/changelogs/unreleased/4864-sseago new file mode 100644 index 0000000000..fdbe784915 --- /dev/null +++ b/changelogs/unreleased/4864-sseago @@ -0,0 +1 @@ +Add credentials to volume snapshot locations diff --git a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml index b47713497a..3db023bff9 100644 --- a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml @@ -45,6 +45,24 @@ spec: type: string description: Config is for provider-specific configuration fields. type: object + credential: + description: Credential contains the credential information intended + to be used with this location + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object provider: description: Provider is the provider of the volume storage. type: string diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index b7199e7421..efdbadcf47 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -39,7 +39,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb8N\x1e\x137\x8b;\xdf\xdfV\xe5\\\x9b\x1f;\xd5\x1f\xb86\xf6S\x99W\x8a\xe5\xad\xf1l\xad\xe6bW\xe5L5\xf5g\x00:\x91%^\xc1G\x1a\xaad\t\xa6g\x00~bv\xe8\x15\xb04\xb5\xa4b\xf9gŅAu-\xf3\xaa\b$ZA\x8a:Q\xbc4\x96\x14w\x86\x99J\x83܂ɰ=\x0e\x95\x9f\xb5\x14\x9f\x99ɮ`\xadm\xbbu\x991\x1d\xbe:\x129\x00\xbe\xca\x1c\b7m\x14\x17\xbb\xa1\xd1\xde\xc1\xb5\x92\x02\xf0k\xa9P\x13ʐZΊ\x1dCș6`x\x81\xc0\xfc\x80\xf0Ĵ\xc5a+\x15\x98\x8c\xeby\x9a\x10\x90\x0e\xb6\x0e\x9d\x0f\xfdj\x87P\xca\fztZ\xa0\x82T\xaf\x8f$\xb2\x03\xf3\xdd\x0e\x87\x81\xb9\xcf\xfb\xb7Nn\x92\f\vv\xe5[\xca\x12ŻϷ\x0f\u007f\xbd\xebTCO\x0e\xfc,\x81k`\xf0`\x85\x1a\x94_~`2f@!q\r\x85\xa1\x16\xa5\xc2U\xa0LZ\x83\x04\x90\nJT\\\xa6<\t\x14\xb5\x9du&\xab<\x85\r\x12q\xd7u\x87R\xc9\x12\x95\xe1aٸ\xd2R\x13\xad\xda\x1e\xc6ohR\xae\x95\x93\"\xd4Vp\xfcb\xc0\xd4\xd3\xc1\xc96\xd7\r\xfe\x96\xc0\x1d\xc0@\x8d\x98\x00\xb9\xf9\x19\x13\xb3\x86;T\x04&`\x9dH\xb1GE\x14H\xe4N\xf0\xff\u05305I\xac\xb1\x82dЯ\xe5\xa6\xd8\xc5'X\x0e{\x96Wx\x01L\xa4P\xb0\x03(\xa4Q\xa0\x12-x\xb6\x89^\xc3?\xa5B\xe0b+\xaf 3\xa6\xd4W\x97\x97;n\x82zLdQT\x82\x9bå\xd5t|S\x19\xa9\xf4e\x8a{\xcc/5߭\x98J2n01\x95\xc2KV\xf2\x95E]X\x15\xb9.\xd2?\x05\x8e\xea7\x1d\\\x8f֊+V\x89Mp\x80\xb4\x99\x13\x18\xd7\xd5͢!4U\x11u\xbe\xdc\xdcݷ\x85\x89\xeb>\xf5-\xdd[\x12ְ\x80\b\xc6\xc5\x16\xfdj\xdc*YX\x98(\xd2Rra\xec\x8f$\xe7(\xfa\xe4\xd7զ\xe0\x86\xf8\xfeK\x85\xda\x10\xaf\xd6pmm\x06\xc9aU\xd2\xeaI\xd7p+\xe0\x9a\x15\x98_3\x8d/\xce\x00\xa2\xb4^\x11a\xe3X\xd06w\xfdƎj\xad\x0f\xc14\x8d\xf0+\xac\xf1\xbb\x12\x93Β\xa1~|\xcb\x13\xbb0\xac\xe6\xabU@O\xfb\xb92\xbcj\xc1\xab\x1ejޯ\x9f\xd46\xb16\xe1\b&x\x15\xb3>\xfa2BM\xfb\t\x8b\x92\x96\xeb\f\x8a\xf7\xbe\x19\xa1H4Jk\x17$\x18ˠޤ\xd7jp\xa4T\xecp\x19\x12\xbd\xf6<\xf5Z㈚\xd3\x14\xa5\x92h~'X\xa93i\xc8.\xc8\xca\f\xb5\xeaM\xe0\xfa\xee\xb6\xd7)\xf0\xd9s\xddڽJcJSxb\xbc\xbf~B!y\xb8\xbe\xbb\x85\ar#0\xc0\x04g\xfd\xc0TJX5\xf8\x05Yz\xb8\x97?i\x84\xb4\xb2\xda ز\x8b\x11\xc0\x1b\xdc\xd2bSH0\xa8\x03*E\xb2\xa7-j\xb22kk\xa4Sܲ*7^\xb9p\ro\xbf\x83\x82\x8b\xca\xe01\xdfa\x9a\xf7\x8eH\x16\x9c\x9b\x8d\xbe\x97_P\x1b\x9eD\x10\xf4\xfd`\xc7\x16Q\x9f24\x19*\xd2t\xf6\x835\x1e\xa3s\xafIo\xd8#\xf9\x1f\x1b'Nd\x88\xf2\x1cJ\x99\xc2ލ\x04\x9bC@zj\xc2\x1b)sdC\"\x88_\x93\xbcJ1\xad}\xc6A!\xeb\xcd\xf6樓\xf5\xae\x19\x17\xb4dɗ%TE\xfdud\x9e\xd6\xf83\x85@Z\x97\v\a\x13\xb8\xf3\xf16#\xab\x97\n7X\x8c\xe09\xcbb\xb0^<\xdb\xe4x\x05FUC\x8a#\xc0`J\xb1\xc3\x04\xcdB\x04\xb2\x84du\x1fo\x1bs\x9e \x11\xab\xb6\x80\x96j\x964#\xf3\xfb?$X&\xe5c\f\x91\xfeA\xed\x1aK\x0f\x89\r\xf4`\x83\x19\xdbs\xa9t\xdf]į\x98T\x06\xc7\xd6\x113\x90\xf2\xed\x16\x15\xc1\xb2\xd1I\x1d\xccL\x11kZ\xdfRQӌ?\x9aW\xc3tb\x9e\xa5\xc6\xd8T\xac]\x1b\x85\n\x16qR\x87U\t\\\xa4|\xcfӊ\xe5\xc0\x856L$n~\xac\xc6ox~0'\x10G\xf8;k\x16fA\\\xea\xb8\tR \xf9\xf6\x85T\xc3\xc2\x11\xca1\x98q2l\x18i@9fۛ\xa2($\xf6\xa8\xa4\xd6\x1e5z\xe7\xa2\xe1\x94\xf3\xb0s\xb6\xc1\x1c4\xe6\x98\x18\xa9\xc6\xc9\x13#\x04\xae\xc4\xea\xcf\x11\xca\x0ehҮ!\x9eU\xa2M!K\x9d\xf1$s\xce0I\x99\x85\x05\xa9Dm5\x06+\xcb\xfc05i\x88\x91\f?\u061c\xd2hJ\x84\xfa\xe8\xc3\x1dS$M\x89\xd4\xc1M\x99\xd1\xc6]\xaa\xd7b\xf3J\xf4\x0e\x9a⛄\xfd\xf6\xa8\xfb\xf3\v;\x91\x9bS\xb0|\xbb\x05,Js\xb8\x00nBm\fTr\xb0\x1a<\xfe`\x8c;m\xb5\xdc\xf6{?\xfbjy\x16\xae\xd5h\xfcA\x98f\x8d՝\xb7U\x8b\x18\xf6\xa1\xdd\xf3\x02\xf8\xb6fXz\x01[\x9e\x1b\xb4\xbe\xd4\x1c\xa2-Gg\x96s\xcfI\xa0X\xdbK\xa5`&\xc9n\xea\xfd\x81\x88\x1e=Z\xf5\x018\xbf<\xc40\x96\a\x11 \xa1v*\xec\x96\x12WX\xb8\xad\xaa{\xbb>\x9a\x1a\xeb\x01\xbe\xfb\xf8\x1e\xd39\x92A\xbc\xa4\x1eM\xea]\xcf\xd3i\xa3`'\x18\x05\xb25)\xeb\xa6\xd51\x9eې\xbc\x00\x06\x8fxp\x9e\xd5`p9T\x88\xb5\xac\x06\xa9\xd0\xee\x8eZ5\xf2\x88\a\v\xcaowF\xc1[\"*\xae<\xe2!\xb6i\x8f\xa8\x84\x9f\xdf\xf0qԥ\n;\x8b\x98\xa5Ԕ\x9a\xa8~퀑q\x93\x85eJ)\x94@\xf1\x13\xa7]3\xac\xb3\xc7\xff\x88\x877ڱ\x8fVM\xc6\xcb\x05\x14 \x85\r\x1a\xed\n\v\x9b\xdb\x0f,\xe7i=\x98]'\v ފ\v\xf8(\r\xfds\xf3\x95kBQ\xa4\xf0^\xa2\xfe(\x8d\xadyQ\x12\xbbI\x9cH`\xd7\xd9.K\xe1\xcc\x02\xd1e\xd1\xf8\r\x0eք\x92\x88\xd6l\xe3\x1an\x05\xc5g\x8e>Kؔa@ΡUT\xdan\x8f\v)V\xd6L\x87\xd1\x16\x00m\xe3\xe5Y%U\x87S\x17\v!\x0e\xa2\xe8ѻ'k\xe5\xbe\x1c\x1d,L\x15\x85e\xce\x12L\xc3v\xa5=\xc5`\x06w<\x81\x02\xd5\x0e\xa1$\xbb\x11/T\v4\xb9+'Ha\xbck\x11\x8a7\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1cYL7\x8f\x9b\xa55\xef\xd6\x1f\x8a\xa2~\xfb||\x99eYȯc\x1f\xc4!\xe9\u070f\x82\xd9\xcd\xde_ɼZ\xf1\xfe-\xce\x1a2\xae\xf4\x1a\xde\xd9\xec\x80\x1c\xdb\xfd\xc3.ak\xa8(\x90\x84\t\xd7@r\xb2g9\xb9\x0f\xa4\xbc\x05`\xee\x9c\t\xb9=\xf2\xa0\xe2T\xccS&\xb5\xb3\xf9[\x8e\xb9=*<\u007f\xc4\xc3\xf9ő\xf6:\xbf\x15\xe7q0I\xe7\x1f)\xad\xdak\x91\"?\xc0\xb9\xfdvn\x1d\xb3%K\xe4\x04\xe7m\x81TG7\xb5g\xf9KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x13or\xe1\xe7f\x11-ӥ\xd4#\xc7N#h}\x96ڸ\r\xc0\x8e\xbb=\xb0C\x18\x13\xfd\xf9]C`[\x83\n\xb4\x91*\x9c.\x93\xda\xedm\x90\x13\xe7\xf5<\xef\x89\xd5\xf5n\xa4\x03LA\xe6y\xa3!\x9cN?w\xc7\xce\xf4\xf7<\xcc\xc4:K\x16v\xa9d\x82ZϋR\xa4\xe5\x98ٰ\xad7k\x99\v\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6kkߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\x9b5V\x14\xac\x9f\xe9\x10\x8d\xee\xb5\xeb\x1d\x16\xa0\a\xe6\x02&\xb5\xab\xacRY\xe67{\x91\xfc\xbd9\x1e\x05\x17\xb7v x\xfbb\xce\n\x04U\x8e\xa7\x862ס\u007fÐ\xba\"6~\x85pn/\xedY\x8d\xc2\x0eg\x8fO2\xe29\x05\xe4L\viڛ5~\xa47\x1a\xb6\\i\xd3 \xbc\x00*\xd7\xf68\xf9ecLq\xa3\xd4\xc9!\xe6'\u05fb\xb5\xad\x98\xc9'\x9fe\xb2$\xb0\x0e\xc4\xcf\xd8\x1e\x81o\x81\x1b@\x91\xc8J\xd8\r/R\x174\xcc\x02\x88\x8e\x89ΘD\xda\xccVgQ\x15\xf1\x04YY\xe9\xe4bvw\xac\xdd\xe5\a\xc6\xe3v\xa7\xe04\xb6\x9a\xa9\x8c\x92\xa1\xd2M\x93\xf1\xa9%\xedt\xa2\x82}\xe5EU\x00+\x88-K\xe2ƭKJ\t\xb9G\x8e\xd7O\x8c\x1b\x9f\x8e\xe9\x0eV\x97i\xd3D\x16e\x8e\x06C\xbaI\"\x85\xe6)\xd6\xee\x83\xe7\xff`\xf2\xceXa\xb0e<\xaf\xd4\x02\x1d\xbd\x983K\xe36\xaf\x9e\x9e?\x18\x8bGde\x89\x19\xb9\xe9\xbe\xc0i\x9e\xb7\x1f\xa5Z\xe62\u007fV\xf8\xfc\xaei\xa98I\xa9\x9c\xf3NgaZ\xef\xb5\xeb\x9dz\xe1e\xe20\xe6\x9e\xceB\xb5\x98\xbc\xba\xa7uyuO_\xdd\xd3W\xf7\xb4W^\xdd\xd3W\xf7\xf4\xd5=\x1d.\xaf\xeei\xab\xbc\xba\xa7\xd1\xf6#\x06Õݹ\x9dh\x10\x85Ud\n\xc6\x1c\xda3c\xf9L\xa3\xeb\xbc\xd2\x06Ւ\f\xe9\xdb\xe1\x9e\x039\xf4\x89k\xb2\xb2\xd7\x1dǤ\xa6I]i\x8c^\x9d2MK2,&w\xb1%\xc2\v\x8fN\x83\x1e϶\x8fM\xa0\x9bK\x9b\xeb\xe6\x8e\xd7\xe9j\xee\xaf\x11\x82\x18\x19\x86\xf7\xdcs\x17\xa6\xda9W\xdd\xdc7\x1b\a\x04\x8c\u007f\x97y\xe5\x91im3\xc9lӉ\xf8c\x16>вw\xb8\xd0%\xa6\xea$~\xff\xaei\x19\x91m6\x9ec\xe6O-Ѱ\xfd\xdbu\xf7\x8b\x91>\xe3ldfO\xdcd\xee2\x17\x85\xaeb\xd7Nk\x0fr\xea/R\xf6i<\x02Q*\x10\x11\x90*E5{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xc8\xfa\x16|\x02?r\x91:ސ\x10\xb6\xfc\v\xfbj\xa1\xbd'S;>\xe3b\xd4x\x9b\xbdC#\x8d%#5j\x83\"{\xb8\xad\xd7pÒ\xacn8\x02ю\x9c1\r[\xa9\nf\xe0\xbc>\x8d\xbb\f=\xa9\xe6|\r\xf0\x83\xac\x0fB[\xaf܌\xc0ռ(\xf3\x03E?p\xde\x05\xf4m\xa23*~ڿ\x04\xe7\x1fD\x8b\x88\x80\xef\xba=\x06\x8e}\xc3shI.\xab\xb4\x1ea\x82\xddL\x1c\xe0\xf3\x83\xf5\xa6\xec#PI\xf3X\x96\xf7\x95B$\xdc{Kk\x04\xe4\u0603\x82\x8bH6~8\xac\x8dTl\x87\x1f\xa4{k1\x86f\xdd\x1e\x9d\xe76\xbd\xae\n\xa9\"\xfe\xeeר$\xbb\xb9\xf5\x016\x19d~\xb55g\xe9\x84\xed\x98\x12\x9bY\xe7\xc6\xe4\x11\x93\xbb\xbf\xff\xe0&dx\x81\xeb\xf7\x95;\xa8_\x95Li$J\x87\x89\xbaN\x9bq;\x97\xc9'ȥ\xa7\xc3\xf7\xfdy(\xb4\x19k6'\xe0\xa4\xd9\xec;O\x1f\x06\xd2ň\xfc\xc3p\xcfV \xdbb\xe2\xd4Ѿ\u070e\xc2bZ˄[]d\xb7\x84l\xa2\xd8\xcb=\x157eQ&TF\xa5\xf1ӓ@\xf5%,T}+\x1c\xa7f^\xe2\xfc\xe9\xa8c`\xf0\x90\xfa \xfd\xd7k>d\xf7\x84'\x90v\xafT\x86\xbd-\xae\xeb\xb7I\x8fI7\xb3\xfe\xc7\xd7\xfe\xb0\xef\xba\x1a~\x0etU\xbfPz\x16AY\xf7\ng̣\xaf\xee\xb9΄\x95\xa6R\u07bc&\x95\xb2\xef\xe6\x11\x10t\xcfʝ\xf6\xeck\xf3\x04\xf6\f/\x9bG\xb1\x9bh\u007f\xf6\t\xee\x01\xfe\xd5\x0fȎ\xbe\xa8ꬫ{\"{E\xf0Oc\xe7\xe0:\xb0\xef\f\xce\xcc\xf43\xb5\xa9\x93|=\xa1m\xc7\xf0>\xe1\xdd\x18\xea\xc3Y\x9b+\xf8\x88O\x03\xb57\x82&q|\xa6\xe6R31\xb5{\x04CO^ONq_\xf7\xb2y\xb1\x03ڢ\xab\xe6z\xcd{\t7,\xcf[\x10]\x0e\xec\x10[\xff̷n\x03'\xa19\xfd\xe5\xa8Ũ\xe2\x9aTZc\nkpI\x1dUjT{L[B\xe2mx\xbb\xa6\xda4\xcfE¯\xbf\x9d5\xab\x92%\t\x96\xc6'v\xb5\xffk\x80\xf3s\xfb#\xbc\xfco\u007f&R8G[_\xc1\xbf\xfe}\x06\xde\x00?\x84\xe7\xfd\xa9\xf2\xbf\x01\x00\x00\xff\xff\x9dJq\x1dHa\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\u007f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\u007f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\u007f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\u007f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), } var CRDs = crds() diff --git a/pkg/apis/velero/v1/volume_snapshot_location_type.go b/pkg/apis/velero/v1/volume_snapshot_location_type.go index 505e1d994a..836701b774 100644 --- a/pkg/apis/velero/v1/volume_snapshot_location_type.go +++ b/pkg/apis/velero/v1/volume_snapshot_location_type.go @@ -16,7 +16,10 @@ limitations under the License. package v1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -61,6 +64,10 @@ type VolumeSnapshotLocationSpec struct { // Config is for provider-specific configuration fields. // +optional Config map[string]string `json:"config,omitempty"` + + // Credential contains the credential information intended to be used with this location + // +optional + Credential *corev1api.SecretKeySelector `json:"credential,omitempty"` } // VolumeSnapshotLocationPhase is the lifecycle phase of a Velero VolumeSnapshotLocation. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 7cf271e8f8..adfb9c2526 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1656,6 +1656,11 @@ func (in *VolumeSnapshotLocationSpec) DeepCopyInto(out *VolumeSnapshotLocationSp (*out)[key] = val } } + if in.Credential != nil { + in, out := &in.Credential, &out.Credential + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationSpec. diff --git a/pkg/builder/volume_snapshot_location_builder.go b/pkg/builder/volume_snapshot_location_builder.go index 1862045e0b..af94471e36 100644 --- a/pkg/builder/volume_snapshot_location_builder.go +++ b/pkg/builder/volume_snapshot_location_builder.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1api "k8s.io/api/core/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -62,3 +64,9 @@ func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLoc b.object.Spec.Provider = name return b } + +// Credential sets the VolumeSnapshotLocation's credential selector. +func (b *VolumeSnapshotLocationBuilder) Credential(selector *corev1api.SecretKeySelector) *VolumeSnapshotLocationBuilder { + b.object.Spec.Credential = selector + return b +} diff --git a/pkg/cmd/cli/snapshotlocation/create.go b/pkg/cmd/cli/snapshotlocation/create.go index 2de6b28277..b0e5e2f09e 100644 --- a/pkg/cmd/cli/snapshotlocation/create.go +++ b/pkg/cmd/cli/snapshotlocation/create.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" @@ -54,16 +55,18 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command { } type CreateOptions struct { - Name string - Provider string - Config flag.Map - Labels flag.Map + Name string + Provider string + Config flag.Map + Labels flag.Map + Credential flag.Map } func NewCreateOptions() *CreateOptions { return &CreateOptions{ - Config: flag.NewMap(), - Labels: flag.NewMap(), + Config: flag.NewMap(), + Labels: flag.NewMap(), + Credential: flag.NewMap(), } } @@ -71,6 +74,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.Provider, "provider", o.Provider, "Name of the volume snapshot provider (e.g. aws, azure, gcp).") flags.Var(&o.Config, "config", "Configuration key-value pairs.") flags.Var(&o.Labels, "labels", "Labels to apply to the volume snapshot location.") + flags.Var(&o.Credential, "credential", "The credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.") } func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -82,6 +86,10 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return errors.New("--provider is required") } + if len(o.Credential.Data()) > 1 { + return errors.New("--credential can only contain 1 key/value pair") + } + return nil } @@ -90,10 +98,10 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error { return nil } -func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { +func (o *CreateOptions) BuildVolumeSnapshotLocation(namespace string) *api.VolumeSnapshotLocation { volumeSnapshotLocation := &api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace(), + Namespace: namespace, Name: o.Name, Labels: o.Labels.Data(), }, @@ -102,6 +110,15 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { Config: o.Config.Data(), }, } + for secretName, secretKey := range o.Credential.Data() { + volumeSnapshotLocation.Spec.Credential = builder.ForSecretKeySelector(secretName, secretKey).Result() + break + } + return volumeSnapshotLocation +} + +func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { + volumeSnapshotLocation := o.BuildVolumeSnapshotLocation(f.Namespace()) if printed, err := output.PrintWithFormat(c, volumeSnapshotLocation); printed || err != nil { return err diff --git a/pkg/cmd/cli/snapshotlocation/set.go b/pkg/cmd/cli/snapshotlocation/set.go index 8515426610..f6b8ac3688 100644 --- a/pkg/cmd/cli/snapshotlocation/set.go +++ b/pkg/cmd/cli/snapshotlocation/set.go @@ -27,8 +27,10 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" + "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" ) @@ -39,9 +41,6 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command { Use: use + " NAME", Short: "Set specific features for a snapshot location", Args: cobra.ExactArgs(1), - // Mark this command as hidden until more functionality is added - // as part of https://github.com/vmware-tanzu/velero/issues/2426 - Hidden: true, Run: func(c *cobra.Command, args []string) { cmd.CheckError(o.Complete(args, f)) cmd.CheckError(o.Validate(c, args, f)) @@ -54,14 +53,18 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command { } type SetOptions struct { - Name string + Name string + Credential flag.Map } func NewSetOptions() *SetOptions { - return &SetOptions{} + return &SetOptions{ + Credential: flag.NewMap(), + } } -func (o *SetOptions) BindFlags(*pflag.FlagSet) { +func (o *SetOptions) BindFlags(flags *pflag.FlagSet) { + flags.Var(&o.Credential, "credential", "Sets the credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.") } func (o *SetOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -69,6 +72,10 @@ func (o *SetOptions) Validate(c *cobra.Command, args []string, f client.Factory) return err } + if len(o.Credential.Data()) > 1 { + return errors.New("--credential can only contain 1 key/value pair") + } + return nil } @@ -92,6 +99,11 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error { return errors.WithStack(err) } + for name, key := range o.Credential.Data() { + location.Spec.Credential = builder.ForSecretKeySelector(name, key).Result() + break + } + if err := kbClient.Update(context.Background(), location, &kbclient.UpdateOptions{}); err != nil { return errors.WithStack(err) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2253d9dbc6..4e39c7d77b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -650,6 +650,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string csiVSCLister, csiVSClassLister, backupStoreGetter, + s.credentialFileStore, ) return controllerRunInfo{ @@ -672,6 +673,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger, podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), s.kubeClient.CoreV1().RESTClient(), + s.credentialFileStore, ) cmd.CheckError(err) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 33cd1ca3ed..462205bc2e 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -50,6 +50,7 @@ import ( snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -98,6 +99,7 @@ type backupController struct { volumeSnapshotClient *snapshotterClientSet.Clientset volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister + credentialFileStore credentials.FileStore } func NewBackupController( @@ -123,6 +125,7 @@ func NewBackupController( volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister, backupStoreGetter persistence.ObjectBackupStoreGetter, + credentialStore credentials.FileStore, ) Interface { c := &backupController{ genericController: newGenericController(Backup, logger), @@ -148,6 +151,7 @@ func NewBackupController( volumeSnapshotContentLister: volumeSnapshotContentLister, volumeSnapshotClassLister: volumesnapshotClassLister, backupStoreGetter: backupStoreGetter, + credentialFileStore: credentialStore, } c.syncHandler = c.processBackup @@ -566,6 +570,15 @@ func (c *backupController) validateAndGetSnapshotLocations(backup *velerov1api.B return nil, errors } + // add credential to config for each location + for _, location := range providerLocations { + err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(location, c.credentialFileStore, c.logger) + if err != nil { + errors = append(errors, fmt.Sprintf("error adding credentials to volume snapshot location named %s: %v", location.Name, err)) + continue + } + } + return providerLocations, nil } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 14dcc2349d..20e340df06 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -1007,12 +1007,15 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + formatFlag := logging.FormatText var ( client = fake.NewSimpleClientset() sharedInformers = informers.NewSharedInformerFactory(client, 0) + logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) ) c := &backupController{ + genericController: newGenericController("backup-test", logger), snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultSnapshotLocations: test.defaultLocations, } diff --git a/pkg/restore/pv_restorer.go b/pkg/restore/pv_restorer.go index ff8d36b347..ce3ab1e1d9 100644 --- a/pkg/restore/pv_restorer.go +++ b/pkg/restore/pv_restorer.go @@ -21,6 +21,7 @@ import ( "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/internal/credentials" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -39,6 +40,7 @@ type pvRestorer struct { volumeSnapshots []*volume.Snapshot volumeSnapshotterGetter VolumeSnapshotterGetter snapshotLocationLister listers.VolumeSnapshotLocationLister + credentialFileStore credentials.FileStore } func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { @@ -59,7 +61,7 @@ func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructu log := r.logger.WithFields(logrus.Fields{"persistentVolume": pvName}) - snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister) + snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister, r.credentialFileStore, r.logger) if err != nil { return nil, err } @@ -103,7 +105,7 @@ type snapshotInfo struct { location *api.VolumeSnapshotLocation } -func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister) (*snapshotInfo, error) { +func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister, credentialStore credentials.FileStore, logger logrus.FieldLogger) (*snapshotInfo, error) { var pvSnapshot *volume.Snapshot for _, snapshot := range volumeSnapshots { if snapshot.Spec.PersistentVolumeName == pvName { @@ -120,6 +122,11 @@ func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volum if err != nil { return nil, errors.WithStack(err) } + // add credential to config + err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(loc, credentialStore, logger) + if err != nil { + return nil, errors.WithStack(err) + } return &snapshotInfo{ providerSnapshotID: pvSnapshot.Status.ProviderSnapshotID, diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index c3a022c566..2228627bc3 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -46,6 +46,7 @@ import ( corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/archive" @@ -113,6 +114,7 @@ type kubernetesRestorer struct { logger logrus.FieldLogger podCommandExecutor podexec.PodCommandExecutor podGetter cache.Getter + credentialFileStore credentials.FileStore } // NewKubernetesRestorer creates a new kubernetesRestorer. @@ -128,6 +130,7 @@ func NewKubernetesRestorer( logger logrus.FieldLogger, podCommandExecutor podexec.PodCommandExecutor, podGetter cache.Getter, + credentialStore credentials.FileStore, ) (Restorer, error) { return &kubernetesRestorer{ restoreClient: restoreClient, @@ -147,9 +150,10 @@ func NewKubernetesRestorer( veleroCloneName := "velero-clone-" + veleroCloneUuid.String() return veleroCloneName, nil }, - fileSystem: filesystem.NewFileSystem(), - podCommandExecutor: podCommandExecutor, - podGetter: podGetter, + fileSystem: filesystem.NewFileSystem(), + podCommandExecutor: podCommandExecutor, + podGetter: podGetter, + credentialFileStore: credentialStore, }, nil } @@ -276,6 +280,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( volumeSnapshots: req.VolumeSnapshots, volumeSnapshotterGetter: volumeSnapshotterGetter, snapshotLocationLister: snapshotLocationLister, + credentialFileStore: kr.credentialFileStore, } restoreCtx := &restoreContext{ diff --git a/pkg/volume/snapshotlocation.go b/pkg/volume/snapshotlocation.go new file mode 100644 index 0000000000..62675dd444 --- /dev/null +++ b/pkg/volume/snapshotlocation.go @@ -0,0 +1,44 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package volume + +import ( + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// UpdateVolumeSnapshotLocationWithCredentialConfig adds the credentials file path to the config +// if the VSL specifies a credential +func UpdateVolumeSnapshotLocationWithCredentialConfig(location *velerov1api.VolumeSnapshotLocation, credentialStore credentials.FileStore, logger logrus.FieldLogger) error { + if location.Spec.Config == nil { + location.Spec.Config = make(map[string]string) + } + // If the VSL specifies a credential, fetch its path on disk and pass to + // plugin via the config. + if location.Spec.Credential != nil && credentialStore != nil { + credsFile, err := credentialStore.Path(location.Spec.Credential) + if err != nil { + return errors.Wrap(err, "unable to get credentials") + } + + location.Spec.Config["credentialsFile"] = credsFile + } + return nil +} diff --git a/site/content/docs/main/api-types/volumesnapshotlocation.md b/site/content/docs/main/api-types/volumesnapshotlocation.md index 28ac332222..e6758f8faf 100644 --- a/site/content/docs/main/api-types/volumesnapshotlocation.md +++ b/site/content/docs/main/api-types/volumesnapshotlocation.md @@ -21,6 +21,9 @@ metadata: namespace: velero spec: provider: aws + credential: + name: secret-name + key: key-in-secret config: region: us-west-2 profile: "default" @@ -37,4 +40,7 @@ The configurable parameters are as follows: | --- | --- | --- | --- | | `provider` | String | Required Field | The name for whichever storage provider will be used to create/store the volume snapshots. See [your volume snapshot provider's plugin documentation](../supported-providers) for the appropriate value to use. | | `config` | map string string | None (Optional) | Provider-specific configuration keys/values to be passed to the volume snapshotter plugin. See [your volume snapshot provider's plugin documentation](../supported-providers) for details. | +| `credential` | [corev1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#secretkeyselector-v1-core) | Optional Field | The credential information to be used with this location. | +| `credential/name` | String | Optional Field | The name of the secret within the Velero namespace which contains the credential information. | +| `credential/key` | String | Optional Field | The key to use within the secret. | {{< /table >}} diff --git a/site/content/docs/main/locations.md b/site/content/docs/main/locations.md index d5dbc710a6..fe74391f9e 100644 --- a/site/content/docs/main/locations.md +++ b/site/content/docs/main/locations.md @@ -26,8 +26,10 @@ This configuration design enables a number of different use cases, including: All [plugins maintained by the Velero team][5] support this feature. If you are using a plugin from another provider, please check their documentation to determine if this feature is supported. -- Velero only supports a single set of credentials for `VolumeSnapshotLocations`. - Velero will always use the credentials provided at install time (stored in the `cloud-credentials` secret) for volume snapshots. +- Velero supports multiple credentials for `VolumeSnapshotLocations`, allowing you to specify the credentials to use with any `VolumeSnapshotLocation`. + However, use of this feature requires support within the plugin for the object storage provider you wish to use. + All [plugins maintained by the Velero team][5] support this feature. + If you are using a plugin from another provider, please check their documentation to determine if this feature is supported. - Volume snapshots are still limited by where your provider allows you to create snapshots. For example, AWS and Azure do not allow you to create a volume snapshot in a different region than where the volume is. If you try to take a Velero backup using a volume snapshot location with a different region than where your cluster's volumes are, the backup will fail. diff --git a/site/content/docs/main/troubleshooting.md b/site/content/docs/main/troubleshooting.md index cdfd253059..dd1a331493 100644 --- a/site/content/docs/main/troubleshooting.md +++ b/site/content/docs/main/troubleshooting.md @@ -175,9 +175,9 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre ``` -### Troubleshooting `BackupStorageLocation` credentials +### Troubleshooting `BackupStorageLocation` and `VolumeSnapshotLocation` credentials -Follow the below troubleshooting steps to confirm that Velero is using the correct credentials if using credentials specific to a [`BackupStorageLocation`][10]: +Follow the below troubleshooting steps to confirm that Velero is using the correct credentials if using credentials specific to a [`BackupStorageLocation` or `VolumeSnapshotLocation`][10]: 1. Confirm that the object storage provider plugin being used supports multiple credentials. If the logs from the Velero deployment contain the error message `"config has invalid keys credentialsFile"`, the version of your object storage plugin does not yet support multiple credentials. @@ -186,7 +186,7 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre If you are using a plugin from a different provider, please contact them for further advice. -1. Confirm that the secret and key referenced by the `BackupStorageLocation` exists in the Velero namespace and has the correct content: +1. Confirm that the secret and key referenced by the `BackupStorageLocation` or `VolumeSnapshotLocation` exists in the Velero namespace and has the correct content: ```bash # Determine which secret and key the BackupStorageLocation is using BSL_SECRET=$(kubectl get backupstoragelocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.name}) @@ -197,11 +197,21 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre # Print the content of the secret and ensure it is correct kubectl -n velero get secret $BSL_SECRET -ojsonpath={.data.$BSL_SECRET_KEY} | base64 --decode + + # Determine which secret and key the VolumeSnapshotLocation is using + VSL_SECRET=$(kubectl get volumesnapshotlocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.name}) + VSL_SECRET_KEY=$(kubectl get volumesnapshotlocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.key}) + + # Confirm that the secret exists + kubectl -n velero get secret $VSL_SECRET + + # Print the content of the secret and ensure it is correct + kubectl -n velero get secret $VSL_SECRET -ojsonpath={.data.$VSL_SECRET_KEY} | base64 --decode ``` If the secret can't be found, the secret does not exist within the Velero namespace and must be created. If no output is produced when printing the contents of the secret, the key within the secret may not exist or may have no content. - Ensure that the key exists within the secret's data by checking the output from `kubectl -n velero describe secret $BSL_SECRET`. + Ensure that the key exists within the secret's data by checking the output from `kubectl -n velero describe secret $BSL_SECRET` or `kubectl -n velero describe secret $VSL_SECRET`. If it does not exist, follow the instructions for [editing a Kubernetes secret][12] to add the base64 encoded credentials data. From c612853bd5ca12ee2e1173b7394697af6821337f Mon Sep 17 00:00:00 2001 From: Ming Date: Fri, 9 Sep 2022 08:25:13 +0000 Subject: [PATCH 294/366] Fix PVB finds wrong parent snapshot Signed-off-by: Ming --- changelogs/unreleased/5322-qiuming-best | 1 + pkg/controller/pod_volume_backup_controller.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/5322-qiuming-best diff --git a/changelogs/unreleased/5322-qiuming-best b/changelogs/unreleased/5322-qiuming-best new file mode 100644 index 0000000000..b44ed0b5f2 --- /dev/null +++ b/changelogs/unreleased/5322-qiuming-best @@ -0,0 +1 @@ +Fix PVB finds wrong parent snapshot diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 93c08ff645..6b4cf22268 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -168,7 +168,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ // parent snapshot to use, and will have to do a full rescan of the contents of the PVC. var parentSnapshotID string if pvcUID, ok := pvb.Labels[velerov1api.PVCUIDLabel]; ok { - parentSnapshotID = r.getParentSnapshot(ctx, log, pvb.Namespace, pvcUID, pvb.Spec.BackupStorageLocation) + parentSnapshotID = r.getParentSnapshot(ctx, log, pvcUID, &pvb) if parentSnapshotID == "" { log.Info("No parent snapshot found for PVC, not based on parent snapshot for this backup") } else { @@ -221,14 +221,14 @@ func (r *PodVolumeBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { } // getParentSnapshot finds the most recent completed PodVolumeBackup for the -// specified PVC and returns its Restic snapshot ID. Any errors encountered are +// specified PVC and returns its snapshot ID. Any errors encountered are // logged but not returned since they do not prevent a backup from proceeding. -func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log logrus.FieldLogger, pvbNamespace, pvcUID, bsl string) string { +func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log logrus.FieldLogger, pvcUID string, podVolumeBackup *velerov1api.PodVolumeBackup) string { log = log.WithField("pvcUID", pvcUID) log.Infof("Looking for most recent completed PodVolumeBackup for this PVC") listOpts := &client.ListOptions{ - Namespace: pvbNamespace, + Namespace: podVolumeBackup.Namespace, } matchingLabels := client.MatchingLabels(map[string]string{velerov1api.PVCUIDLabel: pvcUID}) matchingLabels.ApplyToList(listOpts) @@ -242,11 +242,14 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l // recent completed one to use as the parent. var mostRecentPVB velerov1api.PodVolumeBackup for _, pvb := range pvbList.Items { + if pvb.Spec.UploaderType != podVolumeBackup.Spec.UploaderType { + continue + } if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseCompleted { continue } - if bsl != pvb.Spec.BackupStorageLocation { + if podVolumeBackup.Spec.BackupStorageLocation != pvb.Spec.BackupStorageLocation { // Check the backup storage location is the same as spec in order to // support backup to multiple backup-locations. Otherwise, there exists // a case that backup volume snapshot to the second location would From fbb2606102f235c9f8981ec1e80443e07654d3eb Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 9 Sep 2022 09:38:29 +0800 Subject: [PATCH 295/366] issue fix 4874 update Signed-off-by: Lyndon-Li --- changelogs/unreleased/5319-lyndon | 1 + pkg/cmd/server/server.go | 6 ++--- pkg/nodeagent/node_agent.go | 12 +++++++--- pkg/podvolume/backupper.go | 3 ++- pkg/podvolume/util.go | 15 ------------ pkg/util/kube/pod.go | 39 +++++++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/5319-lyndon create mode 100644 pkg/util/kube/pod.go diff --git a/changelogs/unreleased/5319-lyndon b/changelogs/unreleased/5319-lyndon new file mode 100644 index 0000000000..5c87431c56 --- /dev/null +++ b/changelogs/unreleased/5319-lyndon @@ -0,0 +1 @@ +Fix issue 4874 and 4752: check the daemonset pod is running in the node where the workload pod resides before running the PVB for the pod \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 06ad6c2a32..54cfe62034 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -533,11 +533,11 @@ var defaultRestorePriorities = []string{ } func (s *server) initRestic() error { - // warn if restic daemonset does not exist + // warn if node agent does not exist if err := nodeagent.IsRunning(s.ctx, s.kubeClient, s.namespace); err == nodeagent.DaemonsetNotFound { - s.logger.Warn("Velero restic daemonset not found; restic backups/restores will not work until it's created") + s.logger.Warn("Velero node agent not found; pod volume backups/restores will not work until it's created") } else if err != nil { - s.logger.WithError(errors.WithStack(err)).Warn("Error checking for existence of velero restic daemonset") + s.logger.WithError(errors.WithStack(err)).Warn("Error checking for existence of velero node agent") } // ensure the repo key secret is set up diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index f4fcd92500..fcfd10931f 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -23,13 +23,15 @@ import ( "github.com/pkg/errors" "k8s.io/client-go/kubernetes" + "github.com/vmware-tanzu/velero/pkg/util/kube" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" ) const ( - // daemonSet is the name of the Velero restic daemonset. + // daemonSet is the name of the Velero node agent daemonset. daemonSet = "restic" ) @@ -48,7 +50,7 @@ func IsRunning(ctx context.Context, kubeClient kubernetes.Interface, namespace s } } -// IsRunningInNode checks if the node agent daemonset pod is running properly in a specified node. If not, return the error found +// IsRunningInNode checks if the node agent pod is running properly in a specified node. If not, return the error found func IsRunningInNode(ctx context.Context, namespace string, nodeName string, podClient corev1client.PodsGetter) error { if nodeName == "" { return errors.New("node name is empty") @@ -60,10 +62,14 @@ func IsRunningInNode(ctx context.Context, namespace string, nodeName string, pod } for _, pod := range pods.Items { + if kube.IsPodRunning(&pod) != nil { + continue + } + if pod.Spec.NodeName == nodeName { return nil } } - return errors.Errorf("daemonset pod not found in node %s", nodeName) + return errors.Errorf("daemonset pod not found in running state in node %s", nodeName) } diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index 476b3e4f17..3a5db6ceb6 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -35,6 +35,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/nodeagent" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" + "github.com/vmware-tanzu/velero/pkg/util/kube" ) // Backupper can execute restic backups of volumes in a pod. @@ -125,7 +126,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. return nil, []error{err} } - err = IsPodQualified(pod) + err = kube.IsPodRunning(pod) if err != nil { return nil, []error{err} } diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index 3652e50719..a7ab78245a 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -22,7 +22,6 @@ import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/pkg/errors" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/uploader" @@ -296,17 +295,3 @@ func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) [ } return podVolumes } - -// IsPodQualified checks if the pod's status is qualified for a PVB/PVR to backup/restore its volumes. -// If no, return the error found -func IsPodQualified(pod *corev1api.Pod) error { - if pod.Spec.NodeName == "" { - return errors.Errorf("pod is not scheduled, name=%s, namespace=%s, status=%s", pod.Name, pod.Namespace, pod.Status.Phase) - } - - if pod.Status.Phase != corev1api.PodRunning { - return errors.Errorf("pod is not running, name=%s, namespace=%s, status=%s", pod.Name, pod.Namespace, pod.Status.Phase) - } - - return nil -} diff --git a/pkg/util/kube/pod.go b/pkg/util/kube/pod.go new file mode 100644 index 0000000000..4e589c4f04 --- /dev/null +++ b/pkg/util/kube/pod.go @@ -0,0 +1,39 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package kube + +import ( + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" +) + +// IsPodRunning does a well-rounded check to make sure the specified pod is running stably. +// If not, return the error found +func IsPodRunning(pod *corev1api.Pod) error { + if pod.Spec.NodeName == "" { + return errors.Errorf("pod is not scheduled, name=%s, namespace=%s, phase=%s", pod.Name, pod.Namespace, pod.Status.Phase) + } + + if pod.Status.Phase != corev1api.PodRunning { + return errors.Errorf("pod is not running, name=%s, namespace=%s, phase=%s", pod.Name, pod.Namespace, pod.Status.Phase) + } + + if pod.DeletionTimestamp != nil { + return errors.Errorf("pod is being terminated, name=%s, namespace=%s, phase=%s", pod.Name, pod.Namespace, pod.Status.Phase) + } + + return nil +} From 876238e33d0fbf263d00893d18caaa251787fac4 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 14 Sep 2022 10:46:35 -0400 Subject: [PATCH 296/366] Resolve gopkg.in/yaml.v3 vulnerabilities as shown from https://security.snyk.io/package/golang/gopkg.in%2Fyaml.v3 Signed-off-by: Tiger Kaovilai --- changelogs/unreleased/5344-kaovilai | 1 + go.mod | 2 +- go.sum | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5344-kaovilai diff --git a/changelogs/unreleased/5344-kaovilai b/changelogs/unreleased/5344-kaovilai new file mode 100644 index 0000000000..574844a8a1 --- /dev/null +++ b/changelogs/unreleased/5344-kaovilai @@ -0,0 +1 @@ +Resolve gopkg.in/yaml.v3 vulnerabilities by upgrading gopkg.in/yaml.v3 to v3.0.1 \ No newline at end of file diff --git a/go.mod b/go.mod index 03733cfe55..8d6b270432 100644 --- a/go.mod +++ b/go.mod @@ -138,7 +138,7 @@ require ( gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/component-base v0.22.2 // indirect k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect diff --git a/go.sum b/go.sum index 75a473f1c3..d40fe374cf 100644 --- a/go.sum +++ b/go.sum @@ -1472,8 +1472,9 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= From 2c759f395ad77b342337575fa2e67d79f50ff8e4 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Mon, 12 Sep 2022 10:40:28 -0400 Subject: [PATCH 297/366] cancel downloadRequest checkFunc if timeout passed Signed-off-by: Tiger Kaovilai --- changelogs/unreleased/5329-kaovilai | 1 + pkg/cmd/util/downloadrequest/downloadrequest.go | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/5329-kaovilai diff --git a/changelogs/unreleased/5329-kaovilai b/changelogs/unreleased/5329-kaovilai new file mode 100644 index 0000000000..81615413cd --- /dev/null +++ b/changelogs/unreleased/5329-kaovilai @@ -0,0 +1 @@ +Cancel downloadRequest when timeout without downloadURL \ No newline at end of file diff --git a/pkg/cmd/util/downloadrequest/downloadrequest.go b/pkg/cmd/util/downloadrequest/downloadrequest.go index 22861245f3..f131b32e0c 100644 --- a/pkg/cmd/util/downloadrequest/downloadrequest.go +++ b/pkg/cmd/util/downloadrequest/downloadrequest.go @@ -40,6 +40,7 @@ import ( // ErrNotFound is exported for external packages to check for when a file is // not found var ErrNotFound = errors.New("file not found") +var ErrDownloadRequestDownloadURLTimeout = errors.New("download request download url timeout, check velero server logs for errors. backup storage location may not be available") func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name string, kind velerov1api.DownloadTargetKind, w io.Writer, timeout time.Duration, insecureSkipTLSVerify bool, caCertFile string) error { uuid, err := uuid.NewRandom() @@ -58,7 +59,14 @@ func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name strin defer cancel() key := kbclient.ObjectKey{Name: created.Name, Namespace: namespace} + timeStreamFirstCheck := time.Now() + downloadUrlTimeout := false checkFunc := func() { + // if timeout has been reached, cancel request + if time.Now().After(timeStreamFirstCheck.Add(timeout)) { + downloadUrlTimeout = true + cancel() + } updated := &velerov1api.DownloadRequest{} if err := kbClient.Get(ctx, key, updated); err != nil { return @@ -77,9 +85,8 @@ func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name strin } wait.Until(checkFunc, 25*time.Millisecond, ctx.Done()) - - if created.Status.DownloadURL == "" { - return ErrNotFound + if downloadUrlTimeout { + return ErrDownloadRequestDownloadURLTimeout } var caPool *x509.CertPool From 4b9dbfa416bbcddc17064e0fec3300da34e614e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 2 Sep 2022 10:59:09 +0800 Subject: [PATCH 298/366] Support pause/unpause schedules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support pause/unpause schedule Fixes #2363 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/5279-ywk253100 | 1 + config/crd/v1/bases/velero.io_schedules.yaml | 6 + config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/schedule_types.go | 5 + pkg/cmd/cli/delete_options.go | 30 ++--- pkg/cmd/cli/schedule/create.go | 3 + pkg/cmd/cli/schedule/pause.go | 122 ++++++++++++++++++ pkg/cmd/cli/schedule/schedule.go | 2 + pkg/cmd/cli/schedule/unpause.go | 55 ++++++++ pkg/cmd/cli/select_option.go | 69 ++++++++++ pkg/cmd/cli/select_option_test.go | 45 +++++++ pkg/cmd/util/output/schedule_describer.go | 3 + pkg/cmd/util/output/schedule_printer.go | 2 + .../backup_storage_location_controller.go | 15 +-- pkg/controller/backup_sync_controller.go | 35 ++--- pkg/controller/gc_controller.go | 6 +- pkg/controller/schedule_controller.go | 22 ++-- pkg/util/kube/periodical_enqueue_source.go | 22 ++-- .../kube/periodical_enqueue_source_test.go | 14 +- pkg/util/kube/predicate.go | 51 ++++++++ pkg/util/kube/predicate_test.go | 19 +++ 21 files changed, 440 insertions(+), 89 deletions(-) create mode 100644 changelogs/unreleased/5279-ywk253100 create mode 100644 pkg/cmd/cli/schedule/pause.go create mode 100644 pkg/cmd/cli/schedule/unpause.go create mode 100644 pkg/cmd/cli/select_option.go create mode 100644 pkg/cmd/cli/select_option_test.go diff --git a/changelogs/unreleased/5279-ywk253100 b/changelogs/unreleased/5279-ywk253100 new file mode 100644 index 0000000000..51dd99deaf --- /dev/null +++ b/changelogs/unreleased/5279-ywk253100 @@ -0,0 +1 @@ +Support pause/unpause schedules \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index c3e0a69fa4..246bafb3f7 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -32,6 +32,9 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date + - jsonPath: .spec.paused + name: Paused + type: boolean name: v1 schema: openAPIV3Schema: @@ -53,6 +56,9 @@ spec: spec: description: ScheduleSpec defines the specification for a Velero schedule properties: + paused: + description: Paused specifies whether the schedule is paused or not + type: boolean schedule: description: Schedule is a Cron expression defining when to run the Backup. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index efdbadcf47..14cd3c51ba 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z\xdds\x1b\xb7\x11\u007f\xe7_\xb1\xe3<\xa8\x991\x8f\x89\xdbi;|s\xa4\xa6\xa36\x915\x96\xad\x17\x8f\x1f\xc0\xc3\xf2\x0e\xd1\x1d\x80\x028\xcal&\xff{g\xf1q\xbc\x0f\x90\x944ur/6\xf1\xb1\xf8a\xbfw\xa1\xc5r\xb9\\0-\xee\xd1X\xa1\xe4\x1a\x98\x16\xf8š\xa4_\xb6x\xf8\xbb-\x84Z\xed\xbe_<\b\xc9\xd7p\xd9Y\xa7\xda\xf7hUgJ\xbc\u00ad\x90\xc2\t%\x17-:ƙc\xeb\x05\x00\x93R9FÖ~\x02\x94J:\xa3\x9a\x06ͲBY\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb8N\x1e\x137\x8b;\xdf\xdfV\xe5\\\x9b\x1f;\xd5\x1f\xb86\xf6S\x99W\x8a\xe5\xad\xf1l\xad\xe6bW\xe5L5\xf5g\x00:\x91%^\xc1G\x1a\xaad\t\xa6g\x00~bv\xe8\x15\xb04\xb5\xa4b\xf9gŅAu-\xf3\xaa\b$ZA\x8a:Q\xbc4\x96\x14w\x86\x99J\x83܂ɰ=\x0e\x95\x9f\xb5\x14\x9f\x99ɮ`\xadm\xbbu\x991\x1d\xbe:\x129\x00\xbe\xca\x1c\b7m\x14\x17\xbb\xa1\xd1\xde\xc1\xb5\x92\x02\xf0k\xa9P\x13ʐZΊ\x1dCș6`x\x81\xc0\xfc\x80\xf0Ĵ\xc5a+\x15\x98\x8c\xeby\x9a\x10\x90\x0e\xb6\x0e\x9d\x0f\xfdj\x87P\xca\fztZ\xa0\x82T\xaf\x8f$\xb2\x03\xf3\xdd\x0e\x87\x81\xb9\xcf\xfb\xb7Nn\x92\f\vv\xe5[\xca\x12ŻϷ\x0f\u007f\xbd\xebTCO\x0e\xfc,\x81k`\xf0`\x85\x1a\x94_~`2f@!q\r\x85\xa1\x16\xa5\xc2U\xa0LZ\x83\x04\x90\nJT\\\xa6<\t\x14\xb5\x9du&\xab<\x85\r\x12q\xd7u\x87R\xc9\x12\x95\xe1aٸ\xd2R\x13\xad\xda\x1e\xc6ohR\xae\x95\x93\"\xd4Vp\xfcb\xc0\xd4\xd3\xc1\xc96\xd7\r\xfe\x96\xc0\x1d\xc0@\x8d\x98\x00\xb9\xf9\x19\x13\xb3\x86;T\x04&`\x9dH\xb1GE\x14H\xe4N\xf0\xff\u05305I\xac\xb1\x82dЯ\xe5\xa6\xd8\xc5'X\x0e{\x96Wx\x01L\xa4P\xb0\x03(\xa4Q\xa0\x12-x\xb6\x89^\xc3?\xa5B\xe0b+\xaf 3\xa6\xd4W\x97\x97;n\x82zLdQT\x82\x9bå\xd5t|S\x19\xa9\xf4e\x8a{\xcc/5߭\x98J2n01\x95\xc2KV\xf2\x95E]X\x15\xb9.\xd2?\x05\x8e\xea7\x1d\\\x8f֊+V\x89Mp\x80\xb4\x99\x13\x18\xd7\xd5͢!4U\x11u\xbe\xdc\xdcݷ\x85\x89\xeb>\xf5-\xdd[\x12ְ\x80\b\xc6\xc5\x16\xfdj\xdc*YX\x98(\xd2Rra\xec\x8f$\xe7(\xfa\xe4\xd7զ\xe0\x86\xf8\xfeK\x85\xda\x10\xaf\xd6pmm\x06\xc9aU\xd2\xeaI\xd7p+\xe0\x9a\x15\x98_3\x8d/\xce\x00\xa2\xb4^\x11a\xe3X\xd06w\xfdƎj\xad\x0f\xc14\x8d\xf0+\xac\xf1\xbb\x12\x93Β\xa1~|\xcb\x13\xbb0\xac\xe6\xabU@O\xfb\xb92\xbcj\xc1\xab\x1ejޯ\x9f\xd46\xb16\xe1\b&x\x15\xb3>\xfa2BM\xfb\t\x8b\x92\x96\xeb\f\x8a\xf7\xbe\x19\xa1H4Jk\x17$\x18ˠޤ\xd7jp\xa4T\xecp\x19\x12\xbd\xf6<\xf5Z㈚\xd3\x14\xa5\x92h~'X\xa93i\xc8.\xc8\xca\f\xb5\xeaM\xe0\xfa\xee\xb6\xd7)\xf0\xd9s\xddڽJcJSxb\xbc\xbf~B!y\xb8\xbe\xbb\x85\ar#0\xc0\x04g\xfd\xc0TJX5\xf8\x05Yz\xb8\x97?i\x84\xb4\xb2\xda ز\x8b\x11\xc0\x1b\xdc\xd2bSH0\xa8\x03*E\xb2\xa7-j\xb22kk\xa4Sܲ*7^\xb9p\ro\xbf\x83\x82\x8b\xca\xe01\xdfa\x9a\xf7\x8eH\x16\x9c\x9b\x8d\xbe\x97_P\x1b\x9eD\x10\xf4\xfd`\xc7\x16Q\x9f24\x19*\xd2t\xf6\x835\x1e\xa3s\xafIo\xd8#\xf9\x1f\x1b'Nd\x88\xf2\x1cJ\x99\xc2ލ\x04\x9bC@zj\xc2\x1b)sdC\"\x88_\x93\xbcJ1\xad}\xc6A!\xeb\xcd\xf6樓\xf5\xae\x19\x17\xb4dɗ%TE\xfdud\x9e\xd6\xf83\x85@Z\x97\v\a\x13\xb8\xf3\xf16#\xab\x97\n7X\x8c\xe09\xcbb\xb0^<\xdb\xe4x\x05FUC\x8a#\xc0`J\xb1\xc3\x04\xcdB\x04\xb2\x84du\x1fo\x1bs\x9e \x11\xab\xb6\x80\x96j\x964#\xf3\xfb?$X&\xe5c\f\x91\xfeA\xed\x1aK\x0f\x89\r\xf4`\x83\x19\xdbs\xa9t\xdf]į\x98T\x06\xc7\xd6\x113\x90\xf2\xed\x16\x15\xc1\xb2\xd1I\x1d\xccL\x11kZ\xdfRQӌ?\x9aW\xc3tb\x9e\xa5\xc6\xd8T\xac]\x1b\x85\n\x16qR\x87U\t\\\xa4|\xcfӊ\xe5\xc0\x856L$n~\xac\xc6ox~0'\x10G\xf8;k\x16fA\\\xea\xb8\tR \xf9\xf6\x85T\xc3\xc2\x11\xca1\x98q2l\x18i@9fۛ\xa2($\xf6\xa8\xa4\xd6\x1e5z\xe7\xa2\xe1\x94\xf3\xb0s\xb6\xc1\x1c4\xe6\x98\x18\xa9\xc6\xc9\x13#\x04\xae\xc4\xea\xcf\x11\xca\x0ehҮ!\x9eU\xa2M!K\x9d\xf1$s\xce0I\x99\x85\x05\xa9Dm5\x06+\xcb\xfc05i\x88\x91\f?\u061c\xd2hJ\x84\xfa\xe8\xc3\x1dS$M\x89\xd4\xc1M\x99\xd1\xc6]\xaa\xd7b\xf3J\xf4\x0e\x9a⛄\xfd\xf6\xa8\xfb\xf3\v;\x91\x9bS\xb0|\xbb\x05,Js\xb8\x00nBm\fTr\xb0\x1a<\xfe`\x8c;m\xb5\xdc\xf6{?\xfbjy\x16\xae\xd5h\xfcA\x98f\x8d՝\xb7U\x8b\x18\xf6\xa1\xdd\xf3\x02\xf8\xb6fXz\x01[\x9e\x1b\xb4\xbe\xd4\x1c\xa2-Gg\x96s\xcfI\xa0X\xdbK\xa5`&\xc9n\xea\xfd\x81\x88\x1e=Z\xf5\x018\xbf<\xc40\x96\a\x11 \xa1v*\xec\x96\x12WX\xb8\xad\xaa{\xbb>\x9a\x1a\xeb\x01\xbe\xfb\xf8\x1e\xd39\x92A\xbc\xa4\x1eM\xea]\xcf\xd3i\xa3`'\x18\x05\xb25)\xeb\xa6\xd51\x9eې\xbc\x00\x06\x8fxp\x9e\xd5`p9T\x88\xb5\xac\x06\xa9\xd0\xee\x8eZ5\xf2\x88\a\v\xcaowF\xc1[\"*\xae<\xe2!\xb6i\x8f\xa8\x84\x9f\xdf\xf0qԥ\n;\x8b\x98\xa5Ԕ\x9a\xa8~퀑q\x93\x85eJ)\x94@\xf1\x13\xa7]3\xac\xb3\xc7\xff\x88\x877ڱ\x8fVM\xc6\xcb\x05\x14 \x85\r\x1a\xed\n\v\x9b\xdb\x0f,\xe7i=\x98]'\v ފ\v\xf8(\r\xfds\xf3\x95kBQ\xa4\xf0^\xa2\xfe(\x8d\xadyQ\x12\xbbI\x9cH`\xd7\xd9.K\xe1\xcc\x02\xd1e\xd1\xf8\r\x0eք\x92\x88\xd6l\xe3\x1an\x05\xc5g\x8e>Kؔa@ΡUT\xdan\x8f\v)V\xd6L\x87\xd1\x16\x00m\xe3\xe5Y%U\x87S\x17\v!\x0e\xa2\xe8ѻ'k\xe5\xbe\x1c\x1d,L\x15\x85e\xce\x12L\xc3v\xa5=\xc5`\x06w<\x81\x02\xd5\x0e\xa1$\xbb\x11/T\v4\xb9+'Ha\xbck\x11\x8a7\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1cYL7\x8f\x9b\xa55\xef\xd6\x1f\x8a\xa2~\xfb||\x99eYȯc\x1f\xc4!\xe9\u070f\x82\xd9\xcd\xde_ɼZ\xf1\xfe-\xce\x1a2\xae\xf4\x1a\xde\xd9\xec\x80\x1c\xdb\xfd\xc3.ak\xa8(\x90\x84\t\xd7@r\xb2g9\xb9\x0f\xa4\xbc\x05`\xee\x9c\t\xb9=\xf2\xa0\xe2T\xccS&\xb5\xb3\xf9[\x8e\xb9=*<\u007f\xc4\xc3\xf9ő\xf6:\xbf\x15\xe7q0I\xe7\x1f)\xad\xdak\x91\"?\xc0\xb9\xfdvn\x1d\xb3%K\xe4\x04\xe7m\x81TG7\xb5g\xf9KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x13or\xe1\xe7f\x11-ӥ\xd4#\xc7N#h}\x96ڸ\r\xc0\x8e\xbb=\xb0C\x18\x13\xfd\xf9]C`[\x83\n\xb4\x91*\x9c.\x93\xda\xedm\x90\x13\xe7\xf5<\xef\x89\xd5\xf5n\xa4\x03LA\xe6y\xa3!\x9cN?w\xc7\xce\xf4\xf7<\xcc\xc4:K\x16v\xa9d\x82ZϋR\xa4\xe5\x98ٰ\xad7k\x99\v\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6kkߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\x9b5V\x14\xac\x9f\xe9\x10\x8d\xee\xb5\xeb\x1d\x16\xa0\a\xe6\x02&\xb5\xab\xacRY\xe67{\x91\xfc\xbd9\x1e\x05\x17\xb7v x\xfbb\xce\n\x04U\x8e\xa7\x862ס\u007fÐ\xba\"6~\x85pn/\xedY\x8d\xc2\x0eg\x8fO2\xe29\x05\xe4L\viڛ5~\xa47\x1a\xb6\\i\xd3 \xbc\x00*\xd7\xf68\xf9ecLq\xa3\xd4\xc9!\xe6'\u05fb\xb5\xad\x98\xc9'\x9fe\xb2$\xb0\x0e\xc4\xcf\xd8\x1e\x81o\x81\x1b@\x91\xc8J\xd8\r/R\x174\xcc\x02\x88\x8e\x89ΘD\xda\xccVgQ\x15\xf1\x04YY\xe9\xe4bvw\xac\xdd\xe5\a\xc6\xe3v\xa7\xe04\xb6\x9a\xa9\x8c\x92\xa1\xd2M\x93\xf1\xa9%\xedt\xa2\x82}\xe5EU\x00+\x88-K\xe2ƭKJ\t\xb9G\x8e\xd7O\x8c\x1b\x9f\x8e\xe9\x0eV\x97i\xd3D\x16e\x8e\x06C\xbaI\"\x85\xe6)\xd6\xee\x83\xe7\xff`\xf2\xceXa\xb0e<\xaf\xd4\x02\x1d\xbd\x983K\xe36\xaf\x9e\x9e?\x18\x8bGde\x89\x19\xb9\xe9\xbe\xc0i\x9e\xb7\x1f\xa5Z\xe62\u007fV\xf8\xfc\xaei\xa98I\xa9\x9c\xf3NgaZ\xef\xb5\xeb\x9dz\xe1e\xe20\xe6\x9e\xceB\xb5\x98\xbc\xba\xa7uyuO_\xdd\xd3W\xf7\xb4W^\xdd\xd3W\xf7\xf4\xd5=\x1d.\xaf\xeei\xab\xbc\xba\xa7\xd1\xf6#\x06Õݹ\x9dh\x10\x85Ud\n\xc6\x1c\xda3c\xf9L\xa3\xeb\xbc\xd2\x06Ւ\f\xe9\xdb\xe1\x9e\x039\xf4\x89k\xb2\xb2\xd7\x1dǤ\xa6I]i\x8c^\x9d2MK2,&w\xb1%\xc2\v\x8fN\x83\x1e϶\x8fM\xa0\x9bK\x9b\xeb\xe6\x8e\xd7\xe9j\xee\xaf\x11\x82\x18\x19\x86\xf7\xdcs\x17\xa6\xda9W\xdd\xdc7\x1b\a\x04\x8c\u007f\x97y\xe5\x91im3\xc9lӉ\xf8c\x16>вw\xb8\xd0%\xa6\xea$~\xff\xaei\x19\x91m6\x9ec\xe6O-Ѱ\xfd\xdbu\xf7\x8b\x91>\xe3ldfO\xdcd\xee2\x17\x85\xaeb\xd7Nk\x0fr\xea/R\xf6i<\x02Q*\x10\x11\x90*E5{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xc8\xfa\x16|\x02?r\x91:ސ\x10\xb6\xfc\v\xfbj\xa1\xbd'S;>\xe3b\xd4x\x9b\xbdC#\x8d%#5j\x83\"{\xb8\xad\xd7pÒ\xacn8\x02ю\x9c1\r[\xa9\nf\xe0\xbc>\x8d\xbb\f=\xa9\xe6|\r\xf0\x83\xac\x0fB[\xaf܌\xc0ռ(\xf3\x03E?p\xde\x05\xf4m\xa23*~ڿ\x04\xe7\x1fD\x8b\x88\x80\xef\xba=\x06\x8e}\xc3shI.\xab\xb4\x1ea\x82\xddL\x1c\xe0\xf3\x83\xf5\xa6\xec#PI\xf3X\x96\xf7\x95B$\xdc{Kk\x04\xe4\u0603\x82\x8bH6~8\xac\x8dTl\x87\x1f\xa4{k1\x86f\xdd\x1e\x9d\xe76\xbd\xae\n\xa9\"\xfe\xeeר$\xbb\xb9\xf5\x016\x19d~\xb55g\xe9\x84\xed\x98\x12\x9bY\xe7\xc6\xe4\x11\x93\xbb\xbf\xff\xe0&dx\x81\xeb\xf7\x95;\xa8_\x95Li$J\x87\x89\xbaN\x9bq;\x97\xc9'ȥ\xa7\xc3\xf7\xfdy(\xb4\x19k6'\xe0\xa4\xd9\xec;O\x1f\x06\xd2ň\xfc\xc3p\xcfV \xdbb\xe2\xd4Ѿ\u070e\xc2bZ˄[]d\xb7\x84l\xa2\xd8\xcb=\x157eQ&TF\xa5\xf1ӓ@\xf5%,T}+\x1c\xa7f^\xe2\xfc\xe9\xa8c`\xf0\x90\xfa \xfd\xd7k>d\xf7\x84'\x90v\xafT\x86\xbd-\xae\xeb\xb7I\x8fI7\xb3\xfe\xc7\xd7\xfe\xb0\xef\xba\x1a~\x0etU\xbfPz\x16AY\xf7\ng̣\xaf\xee\xb9΄\x95\xa6R\u07bc&\x95\xb2\xef\xe6\x11\x10t\xcfʝ\xf6\xeck\xf3\x04\xf6\f/\x9bG\xb1\x9bh\u007f\xf6\t\xee\x01\xfe\xd5\x0fȎ\xbe\xa8ꬫ{\"{E\xf0Oc\xe7\xe0:\xb0\xef\f\xce\xcc\xf43\xb5\xa9\x93|=\xa1m\xc7\xf0>\xe1\xdd\x18\xea\xc3Y\x9b+\xf8\x88O\x03\xb57\x82&q|\xa6\xe6R31\xb5{\x04CO^ONq_\xf7\xb2y\xb1\x03ڢ\xab\xe6z\xcd{\t7,\xcf[\x10]\x0e\xec\x10[\xff̷n\x03'\xa19\xfd\xe5\xa8Ũ\xe2\x9aTZc\nkpI\x1dUjT{L[B\xe2mx\xbb\xa6\xda4\xcfE¯\xbf\x9d5\xab\x92%\t\x96\xc6'v\xb5\xffk\x80\xf3s\xfb#\xbc\xfco\u007f&R8G[_\xc1\xbf\xfe}\x06\xde\x00?\x84\xe7\xfd\xa9\xf2\xbf\x01\x00\x00\xff\xff\x9dJq\x1dHa\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Ko\x1c9sw\xfd\x8a\x82rp\x02hFk\xe4\x90@7\u007f\xb2\x16\x11ֱ\rK\xabK\x90\x03\xa7\xbbF\xc3U7\xd9K\xb2G\x9e,\xf6\xbf\aUl\xf6k\xfa\xc1\x1eK\xc8f!^\xeca\x93\xc5bU\xb1\x1ed\x91:[\xadVg\xa2\x90\x0fh\xac\xd4\xea\nD!\xf1\xbbCE\xbf\xec\xfa\xe9\xdf\xedZ\xea\xcb\xfd\xfb\xb3'\xa9\xd2+\xb8.\xad\xd3\xf97\xb4\xba4\t~ĭT\xd2I\xad\xcert\"\x15N\\\x9d\x01\b\xa5\xb4\x13Tm\xe9'@\xa2\x953:\xcbЬ\x1eQ\xad\x9f\xca\rnJ\x99\xa5h\x18x\x18z\xff\xd3\xfa\xdf\xd6?\x9d\x01$\x06\xb9\xfb\xbd\xcc\xd1:\x91\x17W\xa0\xca,;\x03P\"\xc7+\xb0\xc9\x0e\xd32C\xbb\xdec\x86F\xaf\xa5>\xb3\x05&4ڣ\xd1eq\x05\xcd\aߩ\xc2\xc4\xcf\xe2\xae\xea\xcfU\x99\xb4\xee\x97N\xf5'i\x1d\u007f*\xb2҈\xac5\x1e\xd7Z\xa9\x1e\xcbL\x98\xa6\xfe\f\xc0&\xba\xc0+\xf8LC\x15\"\xc1\xf4\f\xa0\x9a\x18\x0f\xbd\x02\x91\xa6L*\x91}5R94\xd7:+\xf3@\xa2\x15\xa4h\x13#\vǤ\xb8s\u0095\x16\xf4\x16\xdc\x0e\xdb\xe3P\xf9\xcdj\xf5U\xb8\xdd\x15\xac-\xb7[\x17;a\xc3WO\"\x0f\xa0\xaar\a\xc2\xcd:#\xd5\xe3\xd0h\x1f\xe0\xdah\x05\xf8\xbd0h\teH\x99\xb3\xea\x11\x9ew\xa8\xc0i0\xa5bT\xfe!\x92\xa7\xb2\x18@\xa4\xc0d\xddó¤[9\x87\xcb\xfd\x0e!\x13ց\x939\x82\xa8\x06\x84ga\x19\x87\xad6\xe0v\xd2\xceӄ\x80t\xb0\xf5\xe8|\xeaW{\x84R\xe1\xb0B\xa7\x05*H\xf5\xfaH\";0?\x9a\x1a\xf6\x00?|\xfe\x88\xe9\x1c\xc9 ^R\x8f&\xf5\xa1\xe7\xe9\xb4Q\xe0\tF\x81lM\x8aݴ:\xc6\xf3\x1b\xa3\x17 \xe0\t\x0f\u07b3\x1a\f.\x87\n\xb1V\xd4 \r\xf2.-\xab\x91'<0\xa8j\xdb5\n\xde\x12Q\xf1\xe5\t\x0f\xb1M{D%\xfc\xaa\r\x1fO]\xaa\xe0Y\xc4,\xa5\xa6\xd4D\xad\xd6\x0e8\x1d7YX\xa6\x94B\t\x14?q\xda5\xc3:g\rOxxg=\xfbh\xd5\xecd\xb1\x80\x02\xa4\xb0\xc1\"\xaf\xb0\xb0\xc9\xfe 2\x99փ\xf1:Y\x00\xf1V]\xc0g\xed蟛\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xb8\xe6UI\xec'q\"\x81}g^\x96ʛ\x05\xa2ˢ\xf1\x1b\x1c\u0604\x92\x88\xd6l\x93\x16n\x15\xc5g\x9e>KشÀ\x9cG+/-o\xd3+\xadVl\xa6\xc3h\v\x80\xb6\xf1\xaaX\xa5M\x87S\x17\v!\x0e\xa2X\xa1wO\xd6\xca\u007f9:\xe0\x98*\x06\x8bL$\x98\x86\xedJ>M\x11\x0e\x1fe\x029\x9aG\x84\x82\xecF\xbcP-\xd0侜 \x85\xf1\xaeE(\x95Y\x188\x1c\x18*+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1c\x9dL7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xeb_fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03g:d\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\x91\xe5\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca9\x05KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x93wr\xe1\xe7f\x11-Ӆ\xb6#\xc7N#h}\xd5\xd6\xf9\r\xc0\x8e\xbb=\xb0C\x18\x13\xfdU\xbb\x86 \xb6\x0e\rX\xa7M8\xe5&\xb5\xdb\xdb '\xce\xdby\xde\x13\xab\xeb\xddH\x0f\x98\x82\xcc\xf3FCx\x9d~\ue3ff\xe9\xff\xf30\x13v\x96\x18vat\x82\xd6\u038bR\xa4\xe5\x98ٰ\xad7k\x85\x0f\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6{kߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\u0380\xcbs\xd1ϸ\x88F\xf7\xda\xf7\x0e\v\xb0\x02\xe6\x03&\xf3X\xb2RY\xe67W\"\xf9Ws/mi6Z\x9d?4oU~ \am\xd9\x05\x81\xd9|\xb3\x18\xa4!&\xcbl8\u007fl\x06\xea\x92ܲ\xd8\x18<\"\x8f,>{,\x8e<\xc0װcsƢ\xbd\xb6\xd8\xfc\xb0\xd7\xc9\n\x8b\xcc\x05kex͂<1\x03,\x9a`q\xd9^\xd19^\xaḓyjMdv\r\xe7k͂\x1c\xca\xe7\x8a\xc9Ҋ\xc25:7\xabθ\x9a\xdfI\xfc\xa1\x8c\xac\x97\xcf\xfd~I?\u007f:\xbf**\xab**\x16\x98\xc79*oji\xb6T\x14U\x97fF\xd5YO\x13\x03G\xe5C\x1d\xe7:MMe6\vj<\xc3i\n\xecP\xeeSD^\xd3\x04\xc8v\xc6\xd3b7`V\x9af\x1a\f?\x95\x10ʼ\xad\xcd\xfe/$\xf0G'\xadM\xc7\x05\x8e\x89J\xbe\xf4\xba\x10\xef\x83\xd77\xe4V\x8f\xc7x\xde\xd9>\xc1\xad\x1e\x01y\xbb\x85\xbc̜,\xb2\xd6[\x05n\x87\ax\x96YF\xea\xfc7\xcdW/7\a\x86\xf6\xe5[-\xc0c \xbb\x01\x82\xb0\xf0\x8cYF\xff\x1eQ!\xf1/\x83$z\x85dsƷ\xc1\xab\v\xf2ճ\"\x17>\u05cf沈=\xcb\tR\xb8W\u007fB\xf85\xed\xecz\x1f\x9d\xeb~/\xd1\x1c@\xef\xd1\xd4^ͨ\x985\xf7\x95\xaa\xa5iˬQ%\x95N\xf2\xef\xd3tU\xcb\xf8j\xa8\x174|P\xde\xcc\xf6qeX\xa4C\x9a\xe0hJuR,4\x06B\xe9\x1a\xc2H\xff\x18_z\xc9\x05\x9e\xd7\b\x95^\"X\x8ar+^#`z\xad\x90iiд\xe4\xe82\xea\x02\xcek\x84NK\x82\xa7E\x1e`\xfc\x05\x9b\u05faX\xf3\nA\xd4\xc9a\xd4\"\xd2\xc5^\x9cY\x1cLE\xcco\xe6\xa2̑\xc7\x15\x01r\xf4\x82\xccp@\x15\x01\xf1\xe8b\xcclH\x15\xb3\x0e\xfaA\xd7\x0f_s\x89>\xc6_t\x96\x14{\x04\x1fw\xcc3\u007f}%\xf2\xdaJ\xe4!P\f\xf6\x91\xd7S\x96_K\x89\xa4\xf3\x89\xc1\xd6\xe4Б\xd7O\x16\x85['\x06\\\x93\x10\xa7\xae\x9bL\x87\\\xd3\xdbi\xfdk&'\xb8\x13\x11\x126\xdb\xe4\x87O\x04\xb4I\xd1\xcc\x1e\xae,\x11\xcdY\xa1\xec\xc5D\xdd\xf1{\xaf\x0e\x847\xb1\xa8U\xfb\xe0f\x8c;\xba\xbe\x05\x9f\xc0/R\xa5\x9e7$\x84-\xff\x82_O\xe4{2\xb5\xe33.F\x8d\xb7\xd9;4\xb2X\bR\xa3\x1c\x14\xf1\xe1\xb6]ÍHvu\xc3\x11\x88<\xf2NX\xd8j\x93\v\a\xe7\xf5i\xdce\xe8I5\xe7k\x80\x9fu}\x10\xdaz\xe5f\x04\xae\x95y\x91\x1d(\xfa\x81\xf3.\xa0\x1f\x13\x9dQ\xf1\xb3\xd5KpՃh\x11\x11\xf0]\xb7\xc7\xd0K\x84\xd5shI\xa6˴\x1ea\x82\xddB\x1d\xe0\xeb\x03{S\xfc\bT\xd2<\x96U\xf9J!\x12\uef655\x02r\xecA\xc1E$\x1b?\x1c\xb6N\x1b\U000487f4\u007f\xf31\x86f\xdd\x1e\x9dg?+]\x15RE\xaa\xbb_\xa3\x92\xec\xe7\xd6\a\xd8d\x90U\xab\xad9K'lǔ\xd8\xcc:w.\x8b\x98\xdc\xfd\xfd'?!'s\\\u007f,\xfdA\xfd\xaa\x10\xc6\"Q:L\xd4wڌ۹\x9d~\x86LWt\xf8G\u007f\x1e\x069c\x8ds\x02N\x9a;\xf3\xf4a ]\x8c\xc8?\f\xf7l\x05\xb2-&N\x1d\xed\xeb\xed(,a\xadN$\xeb\"\xde\x12\xe2D\xb1\xd7{*nʢL\xa8\x8c\xd2\xe2\x97g\x85\xe6[X\xa8\xf6VyNͼ\xc4\xf9\xebQ\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@ֿR\x19\xf6\xb6\xa4\xad\xdf&=&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf89\xd0U\xfdB\xe9Y\x04e\xfd+\x9c1\x8f\xcf\xfa\xe7:\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xda\xf3\xb3\xcds\xde3\xbcl\x1e\xf8n\xa2\xfd\xd9\xe7\xc4\a\xf8W? ;\xfa\xa2\xaa\xb7\xae\xfe\xb9\xef\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c{l\x97\xda\xd4I\xbe\x15\xa1\xb9cx\x9f\xf0n\f\xf5\xe1\xac\xcd\x15|\xc6\xe7\x81\xda\x1bE\x938>S\U000e9658\xf2\x1e\xc1\xd0\xd3ۓS\xdc\u05fd8/v@[t\xd5\\\xafy/\xe1FdY\v\xa2ρ\x1db\xeb?˭\xdf\xc0IhN\xffr\xd4bTqM*\xad1\x855\xb8\xa4\x8e*-\x9a=\xa6-!\xa9lx\xbb\xa6\xdc4\xcfE\xc2\x1f\u007f\x9e5\xabR$\t\x16\xaeJ\xecj\xff\x99\x83\xf3s\xfe\x11\xfe\x8a\x01\xffL\xb4\U0008edbd\x82\xff\xfa\xef3\xa8\f\xf0C\xf8S\x05T\xf9\xbf\x01\x00\x00\xff\xff\x16'-~\x14b\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\u007f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\u007f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\u007f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\u007f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), } diff --git a/pkg/apis/velero/v1/schedule_types.go b/pkg/apis/velero/v1/schedule_types.go index 077a375ecf..6cb553b9a8 100644 --- a/pkg/apis/velero/v1/schedule_types.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -38,6 +38,10 @@ type ScheduleSpec struct { // +optional // +nullable UseOwnerReferencesInBackup *bool `json:"useOwnerReferencesInBackup,omitempty"` + + // Paused specifies whether the schedule is paused or not + // +optional + Paused bool `json:"paused,omitempty"` } // SchedulePhase is a string representation of the lifecycle phase @@ -87,6 +91,7 @@ type ScheduleStatus struct { // +kubebuilder:printcolumn:name="Schedule",type="string",JSONPath=".spec.schedule",description="A Cron expression defining when to run the Backup" // +kubebuilder:printcolumn:name="LastBackup",type="date",JSONPath=".status.lastBackup",description="The last time a Backup was run for this schedule" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Paused",type="boolean",JSONPath=".spec.paused" // Schedule is a Velero resource that represents a pre-scheduled or // periodic Backup that should be run. diff --git a/pkg/cmd/cli/delete_options.go b/pkg/cmd/cli/delete_options.go index fbdcb0d0a9..80fd292228 100644 --- a/pkg/cmd/cli/delete_options.go +++ b/pkg/cmd/cli/delete_options.go @@ -27,24 +27,20 @@ import ( "github.com/spf13/pflag" "github.com/vmware-tanzu/velero/pkg/client" - "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" ) // DeleteOptions contains parameters used for deleting a restore. type DeleteOptions struct { - Names []string - all bool - Selector flag.LabelSelector - Confirm bool - Client clientset.Interface - Namespace string - singularTypeName string + *SelectOptions + Confirm bool + Client clientset.Interface + Namespace string } func NewDeleteOptions(singularTypeName string) *DeleteOptions { o := &DeleteOptions{} - o.singularTypeName = singularTypeName + o.SelectOptions = NewSelectOptions("delete", singularTypeName) return o } @@ -56,8 +52,7 @@ func (o *DeleteOptions) Complete(f client.Factory, args []string) error { return err } o.Client = client - o.Names = args - return nil + return o.SelectOptions.Complete(args) } // Validate validates the fields of the DeleteOptions struct. @@ -65,23 +60,14 @@ func (o *DeleteOptions) Validate(c *cobra.Command, f client.Factory, args []stri if o.Client == nil { return errors.New("Velero client is not set; unable to proceed") } - var ( - hasNames = len(o.Names) > 0 - hasAll = o.all - hasSelector = o.Selector.LabelSelector != nil - ) - if !xor(hasNames, hasAll, hasSelector) { - return errors.New("you must specify exactly one of: specific " + o.singularTypeName + " name(s), the --all flag, or the --selector flag") - } - return nil + return o.SelectOptions.Validate() } // BindFlags binds options for this command to flags. func (o *DeleteOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.Confirm, "confirm", o.Confirm, "Confirm deletion") - flags.BoolVar(&o.all, "all", o.all, "Delete all "+o.singularTypeName+"s") - flags.VarP(&o.Selector, "selector", "l", "Delete all "+o.singularTypeName+"s matching this label selector.") + o.SelectOptions.BindFlags(flags) } // GetConfirmation ensures that the user confirms the action before proceeding. diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index e32a71c0cd..00d84721c4 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -82,6 +82,7 @@ type CreateOptions struct { BackupOptions *backup.CreateOptions Schedule string UseOwnerReferencesInBackup bool + Paused bool labelSelector *metav1.LabelSelector } @@ -96,6 +97,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { o.BackupOptions.BindFlags(flags) flags.StringVar(&o.Schedule, "schedule", o.Schedule, "A cron expression specifying a recurring schedule for this backup to run") flags.BoolVar(&o.UseOwnerReferencesInBackup, "use-owner-references-in-backup", o.UseOwnerReferencesInBackup, "Specifies whether to use OwnerReferences on backups created by this Schedule. Notice: if set to true, when schedule is deleted, backups will be deleted too.") + flags.BoolVar(&o.Paused, "paused", o.Paused, "Specifies whether the newly created schedule is paused or not.") } func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -149,6 +151,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { }, Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, + Paused: o.Paused, }, } diff --git a/pkg/cmd/cli/schedule/pause.go b/pkg/cmd/cli/schedule/pause.go new file mode 100644 index 0000000000..e8aa3fe82e --- /dev/null +++ b/pkg/cmd/cli/schedule/pause.go @@ -0,0 +1,122 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package schedule + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + kubeerrs "k8s.io/apimachinery/pkg/util/errors" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/cmd" + "github.com/vmware-tanzu/velero/pkg/cmd/cli" +) + +// NewPauseCommand creates the command for pause +func NewPauseCommand(f client.Factory, use string) *cobra.Command { + o := cli.NewSelectOptions("pause", "schedule") + + c := &cobra.Command{ + Use: use, + Short: "Pause schedules", + Example: ` # Pause a schedule named "schedule-1". + velero schedule pause schedule-1 + + # Pause schedules named "schedule-1" and "schedule-2". + velero schedule pause schedule-1 schedule-2 + + # Pause all schedules labelled with "foo=bar". + velero schedule pause --selector foo=bar + + # Pause all schedules. + velero schedule pause --all`, + Run: func(c *cobra.Command, args []string) { + cmd.CheckError(o.Complete(args)) + cmd.CheckError(o.Validate()) + cmd.CheckError(runPause(f, o, true)) + }, + } + + o.BindFlags(c.Flags()) + + return c +} + +func runPause(f client.Factory, o *cli.SelectOptions, paused bool) error { + client, err := f.Client() + if err != nil { + return err + } + + var ( + schedules []*velerov1api.Schedule + errs []error + ) + switch { + case len(o.Names) > 0: + for _, name := range o.Names { + schedule, err := client.VeleroV1().Schedules(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + errs = append(errs, errors.WithStack(err)) + continue + } + schedules = append(schedules, schedule) + } + default: + selector := labels.Everything().String() + if o.Selector.LabelSelector != nil { + selector = o.Selector.String() + } + res, err := client.VeleroV1().Schedules(f.Namespace()).List(context.TODO(), metav1.ListOptions{ + LabelSelector: selector, + }) + if err != nil { + errs = append(errs, errors.WithStack(err)) + } + + for i := range res.Items { + schedules = append(schedules, &res.Items[i]) + } + } + if len(schedules) == 0 { + fmt.Println("No schedules found") + return nil + } + + msg := "paused" + if !paused { + msg = "unpaused" + } + for _, schedule := range schedules { + if schedule.Spec.Paused == paused { + fmt.Printf("Schedule %s is already %s, skip\n", schedule.Name, msg) + continue + } + schedule.Spec.Paused = paused + if _, err := client.VeleroV1().Schedules(schedule.Namespace).Update(context.TODO(), schedule, metav1.UpdateOptions{}); err != nil { + return errors.Wrapf(err, "failed to update schedule %s", schedule.Name) + } + fmt.Printf("Schedule %s %s successfully\n", schedule.Name, msg) + } + return kubeerrs.NewAggregate(errs) +} diff --git a/pkg/cmd/cli/schedule/schedule.go b/pkg/cmd/cli/schedule/schedule.go index 274a2ad799..85c51c3999 100644 --- a/pkg/cmd/cli/schedule/schedule.go +++ b/pkg/cmd/cli/schedule/schedule.go @@ -34,6 +34,8 @@ func NewCommand(f client.Factory) *cobra.Command { NewGetCommand(f, "get"), NewDescribeCommand(f, "describe"), NewDeleteCommand(f, "delete"), + NewPauseCommand(f, "pause"), + NewUnpauseCommand(f, "unpause"), ) return c diff --git a/pkg/cmd/cli/schedule/unpause.go b/pkg/cmd/cli/schedule/unpause.go new file mode 100644 index 0000000000..8a9e467e3d --- /dev/null +++ b/pkg/cmd/cli/schedule/unpause.go @@ -0,0 +1,55 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package schedule + +import ( + "github.com/spf13/cobra" + + "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/cmd" + "github.com/vmware-tanzu/velero/pkg/cmd/cli" +) + +// NewUnpauseCommand creates the command for unpause +func NewUnpauseCommand(f client.Factory, use string) *cobra.Command { + o := cli.NewSelectOptions("pause", "schedule") + + c := &cobra.Command{ + Use: use, + Short: "Unpause schedules", + Example: ` # Unpause a schedule named "schedule-1". + velero schedule unpause schedule-1 + + # Unpause schedules named "schedule-1" and "schedule-2". + velero schedule unpause schedule-1 schedule-2 + + # Unpause all schedules labelled with "foo=bar". + velero schedule unpause --selector foo=bar + + # Unpause all schedules. + velero schedule unpause --all`, + Run: func(c *cobra.Command, args []string) { + cmd.CheckError(o.Complete(args)) + cmd.CheckError(o.Validate()) + cmd.CheckError(runPause(f, o, false)) + }, + } + + o.BindFlags(c.Flags()) + + return c +} diff --git a/pkg/cmd/cli/select_option.go b/pkg/cmd/cli/select_option.go new file mode 100644 index 0000000000..45ed547168 --- /dev/null +++ b/pkg/cmd/cli/select_option.go @@ -0,0 +1,69 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cli + +import ( + "errors" + "strings" + + "github.com/spf13/pflag" + + "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" +) + +// SelectOptions defines the options for selecting resources +type SelectOptions struct { + Names []string + All bool + Selector flag.LabelSelector + CMD string + SingularTypeName string +} + +// NewSelectOptions creates a new option for selector +func NewSelectOptions(cmd, singularTypeName string) *SelectOptions { + return &SelectOptions{ + CMD: cmd, + SingularTypeName: singularTypeName, + } +} + +// Complete fills in the correct values for all the options. +func (o *SelectOptions) Complete(args []string) error { + o.Names = args + return nil +} + +// Validate validates the fields of the SelectOptions struct. +func (o *SelectOptions) Validate() error { + var ( + hasNames = len(o.Names) > 0 + hasAll = o.All + hasSelector = o.Selector.LabelSelector != nil + ) + if !xor(hasNames, hasAll, hasSelector) { + return errors.New("you must specify exactly one of: specific " + o.SingularTypeName + " name(s), the --all flag, or the --selector flag") + } + + return nil +} + +// BindFlags binds options for this command to flags. +func (o *SelectOptions) BindFlags(flags *pflag.FlagSet) { + flags.BoolVar(&o.All, "all", o.All, strings.Title(o.CMD)+" all "+o.SingularTypeName+"s") + flags.VarP(&o.Selector, "selector", "l", strings.Title(o.CMD)+" all "+o.SingularTypeName+"s matching this label selector.") +} diff --git a/pkg/cmd/cli/select_option_test.go b/pkg/cmd/cli/select_option_test.go new file mode 100644 index 0000000000..b555f2bebb --- /dev/null +++ b/pkg/cmd/cli/select_option_test.go @@ -0,0 +1,45 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cli + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" +) + +func TestCompleteOfSelectOption(t *testing.T) { + option := &SelectOptions{} + args := []string{"arg1", "arg2"} + require.Nil(t, option.Complete(args)) + assert.Equal(t, args, option.Names) +} + +func TestValidateOfSelectOption(t *testing.T) { + option := &SelectOptions{ + Names: nil, + Selector: flag.LabelSelector{}, + All: false, + } + assert.NotNil(t, option.Validate()) + + option.All = true + assert.Nil(t, option.Validate()) +} diff --git a/pkg/cmd/util/output/schedule_describer.go b/pkg/cmd/util/output/schedule_describer.go index 46a65ba182..48f3669dbd 100644 --- a/pkg/cmd/util/output/schedule_describer.go +++ b/pkg/cmd/util/output/schedule_describer.go @@ -52,6 +52,9 @@ func DescribeSchedule(schedule *v1.Schedule) string { } } + d.Println() + d.Printf("Paused:\t%t\n", schedule.Spec.Paused) + d.Println() DescribeScheduleSpec(d, schedule.Spec) diff --git a/pkg/cmd/util/output/schedule_printer.go b/pkg/cmd/util/output/schedule_printer.go index 4c7f7821e4..e39ee90692 100644 --- a/pkg/cmd/util/output/schedule_printer.go +++ b/pkg/cmd/util/output/schedule_printer.go @@ -36,6 +36,7 @@ var ( {Name: "Backup TTL"}, {Name: "Last Backup"}, {Name: "Selector"}, + {Name: "Paused"}, } ) @@ -71,6 +72,7 @@ func printSchedule(schedule *v1.Schedule) []metav1.TableRow { schedule.Spec.Template.TTL.Duration, humanReadableTimeFromNow(lastBackupTime), metav1.FormatLabelSelector(schedule.Spec.Template.LabelSelector), + schedule.Spec.Paused, ) return []metav1.TableRow{row} diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index 3791e01e68..793347dec4 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -207,18 +207,15 @@ func (r *backupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err mgr.GetClient(), &velerov1api.BackupStorageLocationList{}, bslValidationEnqueuePeriod, - kube.PeriodicalEnqueueSourceOption{ - FilterFuncs: []func(object client.Object) bool{ - func(object client.Object) bool { - location := object.(*velerov1api.BackupStorageLocation) - return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.defaultBackupLocationInfo.ServerValidationFrequency, r.log.WithField("controller", BackupStorageLocation)) - }, - }, - }, + kube.PeriodicalEnqueueSourceOption{}, ) + gp := kube.NewGenericEventPredicate(func(object client.Object) bool { + location := object.(*velerov1api.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, r.defaultBackupLocationInfo.ServerValidationFrequency, r.log.WithField("controller", BackupStorageLocation)) + }) return ctrl.NewControllerManagedBy(mgr). // As the "status.LastValidationTime" field is always updated, this triggers new reconciling process, skip the update event that include no spec change to avoid the reconcile loop For(&velerov1api.BackupStorageLocation{}, builder.WithPredicates(kube.SpecChangePredicate{})). - Watches(g, nil). + Watches(g, nil, builder.WithPredicates(gp)). Complete(r) } diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index cc79bbfe81..196ddeb266 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -31,19 +31,17 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" - - "github.com/vmware-tanzu/velero/pkg/util/kube" + "sigs.k8s.io/controller-runtime/pkg/builder" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + "github.com/vmware-tanzu/velero/pkg/util/kube" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" ) const ( @@ -286,33 +284,18 @@ func (b *backupSyncReconciler) SetupWithManager(mgr ctrl.Manager) error { backupSyncReconcilePeriod, kube.PeriodicalEnqueueSourceOption{ OrderFunc: backupSyncSourceOrderFunc, - FilterFuncs: []func(object client.Object) bool{ - func(object client.Object) bool { - location := object.(*velerov1api.BackupStorageLocation) - return b.locationFilterFunc(location) - }, - }, }, ) + gp := kube.NewGenericEventPredicate(func(object client.Object) bool { + location := object.(*velerov1api.BackupStorageLocation) + return b.locationFilterFunc(location) + }) + return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.BackupStorageLocation{}). // Filter all BSL events, because this controller is supposed to run periodically, not by event. - WithEventFilter(predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(ue event.UpdateEvent) bool { - return false - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - GenericFunc: func(ge event.GenericEvent) bool { - return false - }, - }). - Watches(backupSyncSource, nil). + For(&velerov1api.BackupStorageLocation{}, builder.WithPredicates(kube.FalsePredicate{})). + Watches(backupSyncSource, nil, builder.WithPredicates(gp)). Complete(b) } diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 4e61cd25d3..61b22996a6 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -25,6 +25,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -75,8 +76,7 @@ func NewGCReconciler( func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error { s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, c.frequency, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.Backup{}). - WithEventFilter(predicate.Funcs{ + For(&velerov1api.Backup{}, builder.WithPredicates(predicate.Funcs{ UpdateFunc: func(ue event.UpdateEvent) bool { return false }, @@ -86,7 +86,7 @@ func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error { GenericFunc: func(ge event.GenericEvent) bool { return false }, - }). + })). Watches(s, nil). Complete(c) } diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index 9ca9de1edb..3f3df4a6f7 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -28,6 +28,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" + bld "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -67,7 +68,16 @@ func NewScheduleReconciler( func (c *scheduleReconciler) SetupWithManager(mgr ctrl.Manager) error { s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1.ScheduleList{}, scheduleSyncPeriod, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1.Schedule{}). + // global predicate, works for both For and Watch + WithEventFilter(kube.NewAllEventPredicate(func(obj client.Object) bool { + schedule := obj.(*velerov1.Schedule) + if pause := schedule.Spec.Paused; pause { + c.logger.Infof("schedule %s is paused, skip", schedule.Name) + return false + } + return true + })). + For(&velerov1.Schedule{}, bld.WithPredicates(kube.SpecChangePredicate{})). Watches(s, nil). Complete(c) } @@ -89,13 +99,6 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, errors.Wrapf(err, "error getting schedule %s", req.String()) } - if schedule.Status.Phase != "" && - schedule.Status.Phase != velerov1.SchedulePhaseNew && - schedule.Status.Phase != velerov1.SchedulePhaseEnabled { - log.Debugf("the schedule phase is %s, isn't %s or %s, skip", schedule.Status.Phase, velerov1.SchedulePhaseNew, velerov1.SchedulePhaseEnabled) - return ctrl.Result{}, nil - } - c.metrics.InitSchedule(schedule.Name) original := schedule.DeepCopy() @@ -124,7 +127,8 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } - // check for the schedule being due to run, and submit a Backup if so + // check for the schedule being due to run, and submit a Backup if so. + // As the schedule must be validated before checking whether it's due, we cannot put the checking log in Predicate if err := c.submitBackupIfDue(ctx, schedule, cronSchedule); err != nil { return ctrl.Result{}, errors.Wrapf(err, "error running submitBackupIfDue for schedule %s", req.String()) } diff --git a/pkg/util/kube/periodical_enqueue_source.go b/pkg/util/kube/periodical_enqueue_source.go index 16c4db0ce0..1b0ec1a31f 100644 --- a/pkg/util/kube/periodical_enqueue_source.go +++ b/pkg/util/kube/periodical_enqueue_source.go @@ -23,13 +23,13 @@ import ( "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" ) @@ -61,11 +61,11 @@ type PeriodicalEnqueueSource struct { } type PeriodicalEnqueueSourceOption struct { - FilterFuncs []func(object client.Object) bool - OrderFunc func(objList client.ObjectList) client.ObjectList + OrderFunc func(objList client.ObjectList) client.ObjectList } -func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHandler, q workqueue.RateLimitingInterface, pre ...predicate.Predicate) error { +// Start enqueue items periodically. The predicates only apply to the GenericEvent +func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHandler, q workqueue.RateLimitingInterface, predicates ...predicate.Predicate) error { go wait.Until(func() { p.logger.Debug("enqueueing resources ...") if err := p.List(ctx, p.objList); err != nil { @@ -80,19 +80,19 @@ func (p *PeriodicalEnqueueSource) Start(ctx context.Context, h handler.EventHand p.objList = p.option.OrderFunc(p.objList) } if err := meta.EachListItem(p.objList, func(object runtime.Object) error { - obj, ok := object.(metav1.Object) + obj, ok := object.(client.Object) if !ok { p.logger.Error("%s's type isn't metav1.Object", object.GetObjectKind().GroupVersionKind().String()) return nil } - for _, filter := range p.option.FilterFuncs { - if filter != nil { - if enqueueObj := filter(object.(client.Object)); !enqueueObj { - p.logger.Debugf("skip enqueue object %s/%s due to filter function.", obj.GetNamespace(), obj.GetName()) - return nil - } + event := event.GenericEvent{Object: obj} + for _, predicate := range predicates { + if !predicate.Generic(event) { + p.logger.Debugf("skip enqueue object %s/%s due to the predicate.", obj.GetNamespace(), obj.GetName()) + return nil } } + q.Add(ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: obj.GetNamespace(), diff --git a/pkg/util/kube/periodical_enqueue_source_test.go b/pkg/util/kube/periodical_enqueue_source_test.go index e209d6d9eb..8d5e142dd3 100644 --- a/pkg/util/kube/periodical_enqueue_source_test.go +++ b/pkg/util/kube/periodical_enqueue_source_test.go @@ -68,7 +68,7 @@ func TestStart(t *testing.T) { require.Equal(t, 0, queue.Len()) } -func TestFilter(t *testing.T) { +func TestPredicate(t *testing.T) { require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) ctx, cancelFunc := context.WithCancel(context.TODO()) @@ -79,15 +79,13 @@ func TestFilter(t *testing.T) { client, &velerov1.BackupStorageLocationList{}, 1*time.Second, - PeriodicalEnqueueSourceOption{ - FilterFuncs: []func(object crclient.Object) bool{func(object crclient.Object) bool { - location := object.(*velerov1.BackupStorageLocation) - return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, 1*time.Minute, logrus.WithContext(ctx).WithField("BackupStorageLocation", location.Name)) - }}, - }, + PeriodicalEnqueueSourceOption{}, ) - require.Nil(t, source.Start(ctx, nil, queue)) + require.Nil(t, source.Start(ctx, nil, queue, NewGenericEventPredicate(func(object crclient.Object) bool { + location := object.(*velerov1.BackupStorageLocation) + return storage.IsReadyToValidate(location.Spec.ValidationFrequency, location.Status.LastValidationTime, 1*time.Minute, logrus.WithContext(ctx).WithField("BackupStorageLocation", location.Name)) + }))) // Should not patch a backup storage location object status phase // if the location's validation frequency is specifically set to zero diff --git a/pkg/util/kube/predicate.go b/pkg/util/kube/predicate.go index 3073ef8816..a9660b34e7 100644 --- a/pkg/util/kube/predicate.go +++ b/pkg/util/kube/predicate.go @@ -19,6 +19,7 @@ package kube import ( "reflect" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" ) @@ -45,3 +46,53 @@ func (SpecChangePredicate) Update(e event.UpdateEvent) bool { newSpec := reflect.ValueOf(e.ObjectNew).Elem().FieldByName("Spec") return !reflect.DeepEqual(oldSpec.Interface(), newSpec.Interface()) } + +// NewGenericEventPredicate creates a new Predicate that checks the Generic event with the provided func +func NewGenericEventPredicate(f func(object client.Object) bool) predicate.Predicate { + return predicate.Funcs{ + GenericFunc: func(event event.GenericEvent) bool { + return f(event.Object) + }, + } +} + +// NewAllEventPredicate creates a new Predicate that checks all the events with the provided func +func NewAllEventPredicate(f func(object client.Object) bool) predicate.Predicate { + return predicate.Funcs{ + CreateFunc: func(event event.CreateEvent) bool { + return f(event.Object) + }, + DeleteFunc: func(event event.DeleteEvent) bool { + return f(event.Object) + }, + UpdateFunc: func(event event.UpdateEvent) bool { + return f(event.ObjectNew) + }, + GenericFunc: func(event event.GenericEvent) bool { + return f(event.Object) + }, + } +} + +// FalsePredicate always returns false for all kinds of events +type FalsePredicate struct{} + +// Create always returns false +func (f FalsePredicate) Create(event.CreateEvent) bool { + return false +} + +// Delete always returns false +func (f FalsePredicate) Delete(event.DeleteEvent) bool { + return false +} + +// Update always returns false +func (f FalsePredicate) Update(event.UpdateEvent) bool { + return false +} + +// Generic always returns false +func (f FalsePredicate) Generic(event.GenericEvent) bool { + return false +} diff --git a/pkg/util/kube/predicate_test.go b/pkg/util/kube/predicate_test.go index d1c3be8df5..40d1b8c9c0 100644 --- a/pkg/util/kube/predicate_test.go +++ b/pkg/util/kube/predicate_test.go @@ -178,3 +178,22 @@ func TestSpecChangePredicate(t *testing.T) { }) } } + +func TestNewGenericEventPredicate(t *testing.T) { + predicate := NewGenericEventPredicate(func(object client.Object) bool { + return false + }) + + assert.False(t, predicate.Generic(event.GenericEvent{})) +} + +func TestNewAllEventPredicate(t *testing.T) { + predicate := NewAllEventPredicate(func(object client.Object) bool { + return false + }) + + assert.False(t, predicate.Create(event.CreateEvent{})) + assert.False(t, predicate.Update(event.UpdateEvent{})) + assert.False(t, predicate.Delete(event.DeleteEvent{})) + assert.False(t, predicate.Generic(event.GenericEvent{})) +} From 4022020d5fe06609e61f417894a36efdaa07e7a9 Mon Sep 17 00:00:00 2001 From: Ming Date: Thu, 15 Sep 2022 04:02:53 +0000 Subject: [PATCH 299/366] Fix restore cmd extraflag overwrite bug Signed-off-by: Ming --- changelogs/unreleased/5347-qiuming-best | 1 + pkg/uploader/provider/restic.go | 26 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/5347-qiuming-best diff --git a/changelogs/unreleased/5347-qiuming-best b/changelogs/unreleased/5347-qiuming-best new file mode 100644 index 0000000000..c9f946e228 --- /dev/null +++ b/changelogs/unreleased/5347-qiuming-best @@ -0,0 +1 @@ +Fix restore cmd extraflag overwrite bug diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go index be438d0c84..2715b9e9d2 100644 --- a/pkg/uploader/provider/restic.go +++ b/pkg/uploader/provider/restic.go @@ -91,11 +91,18 @@ func NewResticUploaderProvider( } func (rp *resticProvider) Close(ctx context.Context) error { - if err := os.Remove(rp.credentialsFile); err != nil { - rp.log.Warnf("Failed to remove file %s with err %v", rp.credentialsFile, err) + _, err := os.Stat(rp.credentialsFile) + if err == nil { + return os.Remove(rp.credentialsFile) + } else if !os.IsNotExist(err) { + return errors.Errorf("failed to get file %s info with error %v", rp.credentialsFile, err) } - if err := os.Remove(rp.caCertFile); err != nil { - rp.log.Warnf("Failed to remove file %s with err %v", rp.caCertFile, err) + + _, err = os.Stat(rp.caCertFile) + if err == nil { + return os.Remove(rp.caCertFile) + } else if !os.IsNotExist(err) { + return errors.Errorf("failed to get file %s info with error %v", rp.caCertFile, err) } return nil } @@ -134,7 +141,7 @@ func (rp *resticProvider) RunBackup( log.Debugf("Restic backup got empty dir with %s path", path) return "", true, nil } - return "", false, errors.WithStack(fmt.Errorf("error running restic backup with error: %v", err)) + return "", false, errors.WithStack(fmt.Errorf("error running restic backup command %s with error: %v stderr: %v", backupCmd.String(), err, stderrBuf)) } // GetSnapshotID snapshotIdCmd := restic.GetSnapshotCommand(rp.repoIdentifier, rp.credentialsFile, tags) @@ -145,7 +152,7 @@ func (rp *resticProvider) RunBackup( if err != nil { return "", false, errors.WithStack(fmt.Errorf("error getting snapshot id with error: %v", err)) } - log.Debugf("Run command=%s, stdout=%s, stderr=%s", backupCmd.String(), summary, stderrBuf) + log.Infof("Run command=%s, stdout=%s, stderr=%s", backupCmd.String(), summary, stderrBuf) return snapshotID, false, nil } @@ -167,10 +174,11 @@ func (rp *resticProvider) RunRestore( restoreCmd := ResticRestoreCMDFunc(rp.repoIdentifier, rp.credentialsFile, snapshotID, volumePath) restoreCmd.Env = rp.cmdEnv restoreCmd.CACertFile = rp.caCertFile - restoreCmd.ExtraFlags = rp.extraFlags - + if len(rp.extraFlags) != 0 { + restoreCmd.ExtraFlags = append(restoreCmd.ExtraFlags, rp.extraFlags...) + } stdout, stderr, err := restic.RunRestore(restoreCmd, log, updater) - log.Debugf("Run command=%s, stdout=%s, stderr=%s", restoreCmd.Command, stdout, stderr) + log.Infof("Run command=%s, stdout=%s, stderr=%s", restoreCmd.Command, stdout, stderr) return err } From 4262b475360419eb6679e92f7207ad5b56ea7125 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 31 Aug 2022 13:48:53 -0400 Subject: [PATCH 300/366] plugin/clientmgmt refactoring for BackupItemAction v1 Refactors the clientmgmt package to implement the plugin versioning changes needed for BIA v1 and overall package refactoring to support plugin versions in different packages. This should be all that's needed to move on to v2 for BackupItemAction. The remaining plugin types still need similar refactoring to what's being done here for BIA before attempting a v2 implementation. Signed-off-by: Scott Seago --- changelogs/unreleased/5271-sseago | 1 + pkg/cmd/server/server.go | 5 +- .../v1}/restartable_backup_item_action.go | 52 ++++-- .../restartable_backup_item_action_test.go | 27 +-- .../backupitemaction/v1/shared_test.go | 156 ++++++++++++++++++ pkg/plugin/clientmgmt/manager.go | 51 +++--- pkg/plugin/clientmgmt/manager_test.go | 96 +++++------ .../{ => process}/client_builder.go | 2 +- .../{ => process}/client_builder_test.go | 2 +- .../{ => process}/logrus_adapter.go | 2 +- .../{ => process}/logrus_adapter_test.go | 2 +- .../clientmgmt/{ => process}/process.go | 14 +- .../clientmgmt/{ => process}/process_test.go | 16 +- .../clientmgmt/{ => process}/registry.go | 28 ++-- .../clientmgmt/{ => process}/registry_test.go | 2 +- .../{ => process}/restartable_process.go | 56 +++---- .../clientmgmt/restartable_delegate_test.go | 11 +- .../restartable_delete_item_action.go | 15 +- .../restartable_delete_item_action_test.go | 19 ++- .../restartable_item_snapshotter.go | 18 +- .../restartable_item_snapshotter_test.go | 22 +-- .../clientmgmt/restartable_object_store.go | 19 ++- .../restartable_object_store_test.go | 31 ++-- .../restartable_restore_item_action.go | 17 +- .../restartable_restore_item_action_test.go | 19 ++- .../restartable_volume_snapshotter.go | 21 +-- .../restartable_volume_snapshotter_test.go | 31 ++-- pkg/plugin/mocks/process_factory.go | 20 ++- 28 files changed, 479 insertions(+), 276 deletions(-) create mode 100644 changelogs/unreleased/5271-sseago rename pkg/plugin/clientmgmt/{ => backupitemaction/v1}/restartable_backup_item_action.go (57%) rename pkg/plugin/clientmgmt/{ => backupitemaction/v1}/restartable_backup_item_action_test.go (82%) create mode 100644 pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go rename pkg/plugin/clientmgmt/{ => process}/client_builder.go (99%) rename pkg/plugin/clientmgmt/{ => process}/client_builder_test.go (99%) rename pkg/plugin/clientmgmt/{ => process}/logrus_adapter.go (99%) rename pkg/plugin/clientmgmt/{ => process}/logrus_adapter_test.go (99%) rename pkg/plugin/clientmgmt/{ => process}/process.go (93%) rename pkg/plugin/clientmgmt/{ => process}/process_test.go (92%) rename pkg/plugin/clientmgmt/{ => process}/registry.go (91%) rename pkg/plugin/clientmgmt/{ => process}/registry_test.go (99%) rename pkg/plugin/clientmgmt/{ => process}/restartable_process.go (75%) diff --git a/changelogs/unreleased/5271-sseago b/changelogs/unreleased/5271-sseago new file mode 100644 index 0000000000..339b81b696 --- /dev/null +++ b/changelogs/unreleased/5271-sseago @@ -0,0 +1 @@ + plugin/clientmgmt refactoring for BackupItemAction v1 diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 482e917312..3d09f72132 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -69,6 +69,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/restore" @@ -250,7 +251,7 @@ type server struct { cancelFunc context.CancelFunc logger logrus.FieldLogger logLevel logrus.Level - pluginRegistry clientmgmt.Registry + pluginRegistry process.Registry repoManager repository.Manager repoLocker *repository.RepoLocker repoEnsurer *repository.RepositoryEnsurer @@ -295,7 +296,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s return nil, err } - pluginRegistry := clientmgmt.NewRegistry(config.pluginDir, logger, logger.Level) + pluginRegistry := process.NewRegistry(config.pluginDir, logger, logger.Level) if err := pluginRegistry.DiscoverPlugins(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_backup_item_action.go b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go similarity index 57% rename from pkg/plugin/clientmgmt/restartable_backup_item_action.go rename to pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go index f9ff2faa35..3317be280b 100644 --- a/pkg/plugin/clientmgmt/restartable_backup_item_action.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go @@ -14,40 +14,60 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" ) -// restartableBackupItemAction is a backup item action for a given implementation (such as "pod"). It is associated with +// AdaptedBackupItemAction is a backup item action adapted to the v1 BackupItemAction API +type AdaptedBackupItemAction struct { + Kind framework.PluginKind + + // Get returns a restartable BackupItemAction for the given name and process, wrapping if necessary + GetRestartable func(name string, restartableProcess process.RestartableProcess) biav1.BackupItemAction +} + +func AdaptedBackupItemActions() []AdaptedBackupItemAction { + return []AdaptedBackupItemAction{ + { + Kind: framework.PluginKindBackupItemAction, + GetRestartable: func(name string, restartableProcess process.RestartableProcess) biav1.BackupItemAction { + return NewRestartableBackupItemAction(name, restartableProcess) + }, + }, + } +} + +// RestartableBackupItemAction is a backup item action for a given implementation (such as "pod"). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method // call, the restartableBackupItemAction asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. -type restartableBackupItemAction struct { - key kindAndName - sharedPluginProcess RestartableProcess +type RestartableBackupItemAction struct { + Key process.KindAndName + SharedPluginProcess process.RestartableProcess } -// newRestartableBackupItemAction returns a new restartableBackupItemAction. -func newRestartableBackupItemAction(name string, sharedPluginProcess RestartableProcess) *restartableBackupItemAction { - r := &restartableBackupItemAction{ - key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name}, - sharedPluginProcess: sharedPluginProcess, +// NewRestartableBackupItemAction returns a new RestartableBackupItemAction. +func NewRestartableBackupItemAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableBackupItemAction { + r := &RestartableBackupItemAction{ + Key: process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, } return r } // getBackupItemAction returns the backup item action for this restartableBackupItemAction. It does *not* restart the // plugin process. -func (r *restartableBackupItemAction) getBackupItemAction() (biav1.BackupItemAction, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) +func (r *RestartableBackupItemAction) getBackupItemAction() (biav1.BackupItemAction, error) { + plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key) if err != nil { return nil, err } @@ -61,8 +81,8 @@ func (r *restartableBackupItemAction) getBackupItemAction() (biav1.BackupItemAct } // getDelegate restarts the plugin process (if needed) and returns the backup item action for this restartableBackupItemAction. -func (r *restartableBackupItemAction) getDelegate() (biav1.BackupItemAction, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { +func (r *RestartableBackupItemAction) getDelegate() (biav1.BackupItemAction, error) { + if err := r.SharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } @@ -70,7 +90,7 @@ func (r *restartableBackupItemAction) getDelegate() (biav1.BackupItemAction, err } // AppliesTo restarts the plugin's process if needed, then delegates the call. -func (r *restartableBackupItemAction) AppliesTo() (velero.ResourceSelector, error) { +func (r *RestartableBackupItemAction) AppliesTo() (velero.ResourceSelector, error) { delegate, err := r.getDelegate() if err != nil { return velero.ResourceSelector{}, err @@ -80,7 +100,7 @@ func (r *restartableBackupItemAction) AppliesTo() (velero.ResourceSelector, erro } // Execute restarts the plugin's process if needed, then delegates the call. -func (r *restartableBackupItemAction) Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { +func (r *RestartableBackupItemAction) Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { delegate, err := r.getDelegate() if err != nil { return nil, nil, err diff --git a/pkg/plugin/clientmgmt/restartable_backup_item_action_test.go b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go similarity index 82% rename from pkg/plugin/clientmgmt/restartable_backup_item_action_test.go rename to pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go index 7f528e73a7..947a0f1505 100644 --- a/pkg/plugin/clientmgmt/restartable_backup_item_action_test.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "testing" @@ -27,6 +27,7 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/backup/mocks" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -60,10 +61,10 @@ func TestRestartableGetBackupItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := kindAndName{kind: framework.PluginKindBackupItemAction, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) - r := newRestartableBackupItemAction(name, p) + r := NewRestartableBackupItemAction(name, p) a, err := r.getBackupItemAction() if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) @@ -81,18 +82,18 @@ func TestRestartableBackupItemActionGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "pod" - r := newRestartableBackupItemAction(name, p) + r := NewRestartableBackupItemAction(name, p) a, err := r.getDelegate() assert.Nil(t, a) assert.EqualError(t, err, "reset error") // Happy path - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) expected := new(mocks.ItemAction) - key := kindAndName{kind: framework.PluginKindBackupItemAction, name: name} - p.On("getByKindAndName", key).Return(expected, nil) + key := process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name} + p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -123,10 +124,10 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindBackupItemAction, - func(key kindAndName, p RestartableProcess) interface{} { - return &restartableBackupItemAction{ - key: key, - sharedPluginProcess: p, + func(key process.KindAndName, p process.RestartableProcess) interface{} { + return &RestartableBackupItemAction{ + Key: key, + SharedPluginProcess: p, } }, func() mockable { diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go b/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go new file mode 100644 index 0000000000..a612c764cb --- /dev/null +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2018 the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1 + +import ( + "reflect" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" + "github.com/vmware-tanzu/velero/pkg/plugin/framework" +) + +type mockRestartableProcess struct { + mock.Mock +} + +func (rp *mockRestartableProcess) AddReinitializer(key process.KindAndName, r process.Reinitializer) { + rp.Called(key, r) +} + +func (rp *mockRestartableProcess) Reset() error { + args := rp.Called() + return args.Error(0) +} + +func (rp *mockRestartableProcess) ResetIfNeeded() error { + args := rp.Called() + return args.Error(0) +} + +func (rp *mockRestartableProcess) GetByKindAndName(key process.KindAndName) (interface{}, error) { + args := rp.Called(key) + return args.Get(0), args.Error(1) +} + +func (rp *mockRestartableProcess) Stop() { + rp.Called() +} + +type restartableDelegateTest struct { + function string + inputs []interface{} + expectedErrorOutputs []interface{} + expectedDelegateOutputs []interface{} +} + +type mockable interface { + Test(t mock.TestingT) + On(method string, args ...interface{}) *mock.Call + AssertExpectations(t mock.TestingT) bool +} + +func runRestartableDelegateTests( + t *testing.T, + kind framework.PluginKind, + newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, + newMock func() mockable, + tests ...restartableDelegateTest, +) { + for _, tc := range tests { + t.Run(tc.function, func(t *testing.T) { + p := new(mockRestartableProcess) + p.Test(t) + defer p.AssertExpectations(t) + + // getDelegate error + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() + name := "delegateName" + key := process.KindAndName{Kind: kind, Name: name} + r := newRestartable(key, p) + + // Get the method we're going to call using reflection + method := reflect.ValueOf(r).MethodByName(tc.function) + require.NotEmpty(t, method) + + // Convert the test case inputs ([]interface{}) to []reflect.Value + var inputValues []reflect.Value + for i := range tc.inputs { + inputValues = append(inputValues, reflect.ValueOf(tc.inputs[i])) + } + + // Invoke the method being tested + actual := method.Call(inputValues) + + // This function asserts that the actual outputs match the expected outputs + checkOutputs := func(expected []interface{}, actual []reflect.Value) { + require.Equal(t, len(expected), len(actual)) + + for i := range actual { + // Get the underlying value from the reflect.Value + a := actual[i].Interface() + + // Check if it's an error + actualErr, actualErrOk := a.(error) + // Check if the expected output element is an error + expectedErr, expectedErrOk := expected[i].(error) + // If both are errors, use EqualError + if actualErrOk && expectedErrOk { + assert.EqualError(t, actualErr, expectedErr.Error()) + continue + } + + // If function returns nil as struct return type, we cannot just + // compare the interface to nil as its type will not be nil, + // only the value will be + if expected[i] == nil && reflect.ValueOf(a).Kind() == reflect.Ptr { + assert.True(t, reflect.ValueOf(a).IsNil()) + continue + } + + // Otherwise, use plain Equal + assert.Equal(t, expected[i], a) + } + } + + // Make sure we get what we expected when getDelegate returned an error + checkOutputs(tc.expectedErrorOutputs, actual) + + // Invoke delegate, make sure all returned values are passed through + p.On("ResetIfNeeded").Return(nil) + + delegate := newMock() + delegate.Test(t) + defer delegate.AssertExpectations(t) + + p.On("GetByKindAndName", key).Return(delegate, nil) + + // Set up the mocked method in the delegate + delegate.On(tc.function, tc.inputs...).Return(tc.expectedDelegateOutputs...) + + // Invoke the method being tested + actual = method.Call(inputValues) + + // Make sure we get what we expected when invoking the delegate + checkOutputs(tc.expectedDelegateOutputs, actual) + }) + } +} diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index 4766081380..e2b55951c7 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -17,11 +17,15 @@ limitations under the License. package clientmgmt import ( + "errors" + "fmt" "strings" "sync" "github.com/sirupsen/logrus" + biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" @@ -36,7 +40,7 @@ type Manager interface { // GetVolumeSnapshotter returns the VolumeSnapshotter plugin for name. GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) - // GetBackupItemActions returns all backup item action plugins. + // GetBackupItemActions returns all v1 backup item action plugins. GetBackupItemActions() ([]biav1.BackupItemAction, error) // GetBackupItemAction returns the backup item action plugin for name. @@ -68,25 +72,25 @@ type Manager interface { type manager struct { logger logrus.FieldLogger logLevel logrus.Level - registry Registry + registry process.Registry - restartableProcessFactory RestartableProcessFactory + restartableProcessFactory process.RestartableProcessFactory // lock guards restartableProcesses lock sync.Mutex - restartableProcesses map[string]RestartableProcess + restartableProcesses map[string]process.RestartableProcess } // NewManager constructs a manager for getting plugins. -func NewManager(logger logrus.FieldLogger, level logrus.Level, registry Registry) Manager { +func NewManager(logger logrus.FieldLogger, level logrus.Level, registry process.Registry) Manager { return &manager{ logger: logger, logLevel: level, registry: registry, - restartableProcessFactory: newRestartableProcessFactory(), + restartableProcessFactory: process.NewRestartableProcessFactory(), - restartableProcesses: make(map[string]RestartableProcess), + restartableProcesses: make(map[string]process.RestartableProcess), } } @@ -94,7 +98,7 @@ func (m *manager) CleanupClients() { m.lock.Lock() for _, restartableProcess := range m.restartableProcesses { - restartableProcess.stop() + restartableProcess.Stop() } m.lock.Unlock() @@ -102,7 +106,7 @@ func (m *manager) CleanupClients() { // getRestartableProcess returns a restartableProcess for a plugin identified by kind and name, creating a // restartableProcess if it is the first time it has been requested. -func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) (RestartableProcess, error) { +func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) (process.RestartableProcess, error) { m.lock.Lock() defer m.lock.Unlock() @@ -127,7 +131,7 @@ func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) logger.Debug("creating new restartable plugin process") - restartableProcess, err = m.restartableProcessFactory.newRestartableProcess(info.Command, m.logger, m.logLevel) + restartableProcess, err = m.restartableProcessFactory.NewRestartableProcess(info.Command, m.logger, m.logLevel) if err != nil { return nil, err } @@ -146,7 +150,7 @@ func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) { return nil, err } - r := newRestartableObjectStore(name, restartableProcess) + r := NewRestartableObjectStore(name, restartableProcess) return r, nil } @@ -160,7 +164,7 @@ func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, e return nil, err } - r := newRestartableVolumeSnapshotter(name, restartableProcess) + r := NewRestartableVolumeSnapshotter(name, restartableProcess) return r, nil } @@ -189,13 +193,18 @@ func (m *manager) GetBackupItemActions() ([]biav1.BackupItemAction, error) { func (m *manager) GetBackupItemAction(name string) (biav1.BackupItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name) - if err != nil { - return nil, err + for _, adaptedBackupItemAction := range biav1cli.AdaptedBackupItemActions() { + restartableProcess, err := m.getRestartableProcess(adaptedBackupItemAction.Kind, name) + // Check if plugin was not found + if errors.Is(err, &process.PluginNotFoundError{}) { + continue + } + if err != nil { + return nil, err + } + return adaptedBackupItemAction.GetRestartable(name, restartableProcess), nil } - - r := newRestartableBackupItemAction(name, restartableProcess) - return r, nil + return nil, fmt.Errorf("unable to get valid BackupItemAction for %q", name) } // GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions. @@ -227,7 +236,7 @@ func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, e return nil, err } - r := newRestartableRestoreItemAction(name, restartableProcess) + r := NewRestartableRestoreItemAction(name, restartableProcess) return r, nil } @@ -260,7 +269,7 @@ func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, err return nil, err } - r := newRestartableDeleteItemAction(name, restartableProcess) + r := NewRestartableDeleteItemAction(name, restartableProcess) return r, nil } @@ -272,7 +281,7 @@ func (m *manager) GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) return nil, err } - r := newRestartableItemSnapshotter(name, restartableProcess) + r := NewRestartableItemSnapshotter(name, restartableProcess) return r, nil } diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index cc36bc6531..64b8bf4eda 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -72,11 +74,11 @@ type mockRestartableProcessFactory struct { mock.Mock } -func (f *mockRestartableProcessFactory) newRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (RestartableProcess, error) { +func (f *mockRestartableProcessFactory) NewRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (process.RestartableProcess, error) { args := f.Called(command, logger, logLevel) - var rp RestartableProcess + var rp process.RestartableProcess if args.Get(0) != nil { - rp = args.Get(0).(RestartableProcess) + rp = args.Get(0).(process.RestartableProcess) } return rp, args.Error(1) } @@ -85,26 +87,26 @@ type mockRestartableProcess struct { mock.Mock } -func (rp *mockRestartableProcess) addReinitializer(key kindAndName, r reinitializer) { +func (rp *mockRestartableProcess) AddReinitializer(key process.KindAndName, r process.Reinitializer) { rp.Called(key, r) } -func (rp *mockRestartableProcess) reset() error { +func (rp *mockRestartableProcess) Reset() error { args := rp.Called() return args.Error(0) } -func (rp *mockRestartableProcess) resetIfNeeded() error { +func (rp *mockRestartableProcess) ResetIfNeeded() error { args := rp.Called() return args.Error(0) } -func (rp *mockRestartableProcess) getByKindAndName(key kindAndName) (interface{}, error) { +func (rp *mockRestartableProcess) GetByKindAndName(key process.KindAndName) (interface{}, error) { args := rp.Called(key) return args.Get(0), args.Error(1) } -func (rp *mockRestartableProcess) stop() { +func (rp *mockRestartableProcess) Stop() { rp.Called() } @@ -135,7 +137,7 @@ func TestGetRestartableProcess(t *testing.T) { Name: pluginName, } registry.On("Get", pluginKind, pluginName).Return(podID, nil) - factory.On("newRestartableProcess", podID.Command, logger, logLevel).Return(nil, errors.Errorf("factory")).Once() + factory.On("NewRestartableProcess", podID.Command, logger, logLevel).Return(nil, errors.Errorf("factory")).Once() rp, err = m.getRestartableProcess(pluginKind, pluginName) assert.Nil(t, rp) assert.EqualError(t, err, "factory") @@ -143,7 +145,7 @@ func TestGetRestartableProcess(t *testing.T) { // Test 3: registry ok, factory ok restartableProcess := &mockRestartableProcess{} defer restartableProcess.AssertExpectations(t) - factory.On("newRestartableProcess", podID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + factory.On("NewRestartableProcess", podID.Command, logger, logLevel).Return(restartableProcess, nil).Once() rp, err = m.getRestartableProcess(pluginKind, pluginName) require.NoError(t, err) assert.Equal(t, restartableProcess, rp) @@ -166,7 +168,7 @@ func TestCleanupClients(t *testing.T) { for i := 0; i < 5; i++ { rp := &mockRestartableProcess{} defer rp.AssertExpectations(t) - rp.On("stop") + rp.On("Stop") m.restartableProcesses[fmt.Sprintf("rp%d", i)] = rp } @@ -180,9 +182,9 @@ func TestGetObjectStore(t *testing.T) { func(m Manager, name string) (interface{}, error) { return m.GetObjectStore(name) }, - func(name string, sharedPluginProcess RestartableProcess) interface{} { + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableObjectStore{ - key: kindAndName{kind: framework.PluginKindObjectStore, name: name}, + key: process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -197,9 +199,9 @@ func TestGetVolumeSnapshotter(t *testing.T) { func(m Manager, name string) (interface{}, error) { return m.GetVolumeSnapshotter(name) }, - func(name string, sharedPluginProcess RestartableProcess) interface{} { + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableVolumeSnapshotter{ - key: kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name}, + key: process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -214,10 +216,10 @@ func TestGetBackupItemAction(t *testing.T) { func(m Manager, name string) (interface{}, error) { return m.GetBackupItemAction(name) }, - func(name string, sharedPluginProcess RestartableProcess) interface{} { - return &restartableBackupItemAction{ - key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name}, - sharedPluginProcess: sharedPluginProcess, + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { + return &biav1cli.RestartableBackupItemAction{ + Key: process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, } }, false, @@ -231,9 +233,9 @@ func TestGetRestoreItemAction(t *testing.T) { func(m Manager, name string) (interface{}, error) { return m.GetRestoreItemAction(name) }, - func(name string, sharedPluginProcess RestartableProcess) interface{} { + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableRestoreItemAction{ - key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}, + key: process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -246,7 +248,7 @@ func getPluginTest( kind framework.PluginKind, name string, getPluginFunc func(m Manager, name string) (interface{}, error), - expectedResultFunc func(name string, sharedPluginProcess RestartableProcess) interface{}, + expectedResultFunc func(name string, sharedPluginProcess process.RestartableProcess) interface{}, reinitializable bool, ) { logger := test.NewLogger() @@ -273,18 +275,18 @@ func getPluginTest( defer restartableProcess.AssertExpectations(t) // Test 1: error getting restartable process - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("newRestartableProcess")).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once() actual, err := getPluginFunc(m, pluginName) assert.Nil(t, actual) - assert.EqualError(t, err, "newRestartableProcess") + assert.EqualError(t, err, "NewRestartableProcess") // Test 2: happy path - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() expected := expectedResultFunc(name, restartableProcess) if reinitializable { - key := kindAndName{kind: pluginID.Kind, name: pluginID.Name} - restartableProcess.On("addReinitializer", key, expected) + key := process.KindAndName{Kind: pluginID.Kind, Name: pluginID.Name} + restartableProcess.On("AddReinitializer", key, expected) } actual, err = getPluginFunc(m, pluginName) @@ -306,8 +308,8 @@ func TestGetBackupItemActions(t *testing.T) { { name: "Error getting restartable process", names: []string{"velero.io/a", "velero.io/b", "velero.io/c"}, - newRestartableProcessError: errors.Errorf("newRestartableProcess"), - expectedError: "newRestartableProcess", + newRestartableProcessError: errors.Errorf("NewRestartableProcess"), + expectedError: "NewRestartableProcess", }, { name: "Happy path", @@ -349,20 +351,20 @@ func TestGetBackupItemActions(t *testing.T) { restartableProcess := &mockRestartableProcess{} defer restartableProcess.AssertExpectations(t) - expected := &restartableBackupItemAction{ - key: kindAndName{kind: pluginKind, name: pluginName}, - sharedPluginProcess: restartableProcess, + expected := &biav1cli.RestartableBackupItemAction{ + Key: process.KindAndName{Kind: pluginKind, Name: pluginName}, + SharedPluginProcess: restartableProcess, } if tc.newRestartableProcessError != nil { // Test 1: error getting restartable process - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("newRestartableProcess")).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once() break } // Test 2: happy path if i == 0 { - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() } expectedActions = append(expectedActions, expected) @@ -371,7 +373,7 @@ func TestGetBackupItemActions(t *testing.T) { backupItemActions, err := m.GetBackupItemActions() if tc.newRestartableProcessError != nil { assert.Nil(t, backupItemActions) - assert.EqualError(t, err, "newRestartableProcess") + assert.EqualError(t, err, "NewRestartableProcess") } else { require.NoError(t, err) var actual []interface{} @@ -398,8 +400,8 @@ func TestGetRestoreItemActions(t *testing.T) { { name: "Error getting restartable process", names: []string{"velero.io/a", "velero.io/b", "velero.io/c"}, - newRestartableProcessError: errors.Errorf("newRestartableProcess"), - expectedError: "newRestartableProcess", + newRestartableProcessError: errors.Errorf("NewRestartableProcess"), + expectedError: "NewRestartableProcess", }, { name: "Happy path", @@ -442,19 +444,19 @@ func TestGetRestoreItemActions(t *testing.T) { defer restartableProcess.AssertExpectations(t) expected := &restartableRestoreItemAction{ - key: kindAndName{kind: pluginKind, name: pluginName}, + key: process.KindAndName{Kind: pluginKind, Name: pluginName}, sharedPluginProcess: restartableProcess, } if tc.newRestartableProcessError != nil { // Test 1: error getting restartable process - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("newRestartableProcess")).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once() break } // Test 2: happy path if i == 0 { - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() } expectedActions = append(expectedActions, expected) @@ -463,7 +465,7 @@ func TestGetRestoreItemActions(t *testing.T) { restoreItemActions, err := m.GetRestoreItemActions() if tc.newRestartableProcessError != nil { assert.Nil(t, restoreItemActions) - assert.EqualError(t, err, "newRestartableProcess") + assert.EqualError(t, err, "NewRestartableProcess") } else { require.NoError(t, err) var actual []interface{} @@ -483,9 +485,9 @@ func TestGetDeleteItemAction(t *testing.T) { func(m Manager, name string) (interface{}, error) { return m.GetDeleteItemAction(name) }, - func(name string, sharedPluginProcess RestartableProcess) interface{} { + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableDeleteItemAction{ - key: kindAndName{kind: framework.PluginKindDeleteItemAction, name: name}, + key: process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -541,19 +543,19 @@ func TestGetDeleteItemActions(t *testing.T) { defer restartableProcess.AssertExpectations(t) expected := &restartableRestoreItemAction{ - key: kindAndName{kind: pluginKind, name: pluginName}, + key: process.KindAndName{Kind: pluginKind, Name: pluginName}, sharedPluginProcess: restartableProcess, } if tc.newRestartableProcessError != nil { // Test 1: error getting restartable process - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("newRestartableProcess")).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once() break } // Test 2: happy path if i == 0 { - factory.On("newRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() } expectedActions = append(expectedActions, expected) @@ -562,7 +564,7 @@ func TestGetDeleteItemActions(t *testing.T) { deleteItemActions, err := m.GetDeleteItemActions() if tc.newRestartableProcessError != nil { assert.Nil(t, deleteItemActions) - assert.EqualError(t, err, "newRestartableProcess") + assert.EqualError(t, err, "NewRestartableProcess") } else { require.NoError(t, err) var actual []interface{} diff --git a/pkg/plugin/clientmgmt/client_builder.go b/pkg/plugin/clientmgmt/process/client_builder.go similarity index 99% rename from pkg/plugin/clientmgmt/client_builder.go rename to pkg/plugin/clientmgmt/process/client_builder.go index 76e3b1985d..0d3948807e 100644 --- a/pkg/plugin/clientmgmt/client_builder.go +++ b/pkg/plugin/clientmgmt/process/client_builder.go @@ -15,7 +15,7 @@ limitations under the License. */ // Package clientmgmt contains the plugin client for Velero. -package clientmgmt +package process import ( "os" diff --git a/pkg/plugin/clientmgmt/client_builder_test.go b/pkg/plugin/clientmgmt/process/client_builder_test.go similarity index 99% rename from pkg/plugin/clientmgmt/client_builder_test.go rename to pkg/plugin/clientmgmt/process/client_builder_test.go index e922c3d002..1b9c77145d 100644 --- a/pkg/plugin/clientmgmt/client_builder_test.go +++ b/pkg/plugin/clientmgmt/process/client_builder_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "os" diff --git a/pkg/plugin/clientmgmt/logrus_adapter.go b/pkg/plugin/clientmgmt/process/logrus_adapter.go similarity index 99% rename from pkg/plugin/clientmgmt/logrus_adapter.go rename to pkg/plugin/clientmgmt/process/logrus_adapter.go index 3bb55d4647..cb73da789c 100644 --- a/pkg/plugin/clientmgmt/logrus_adapter.go +++ b/pkg/plugin/clientmgmt/process/logrus_adapter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "fmt" diff --git a/pkg/plugin/clientmgmt/logrus_adapter_test.go b/pkg/plugin/clientmgmt/process/logrus_adapter_test.go similarity index 99% rename from pkg/plugin/clientmgmt/logrus_adapter_test.go rename to pkg/plugin/clientmgmt/process/logrus_adapter_test.go index ccfdfa8286..dae7f8ce2f 100644 --- a/pkg/plugin/clientmgmt/logrus_adapter_test.go +++ b/pkg/plugin/clientmgmt/process/logrus_adapter_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "testing" diff --git a/pkg/plugin/clientmgmt/process.go b/pkg/plugin/clientmgmt/process/process.go similarity index 93% rename from pkg/plugin/clientmgmt/process.go rename to pkg/plugin/clientmgmt/process/process.go index f2fc8ae367..66033768d8 100644 --- a/pkg/plugin/clientmgmt/process.go +++ b/pkg/plugin/clientmgmt/process/process.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "strings" @@ -42,7 +42,7 @@ func (pf *processFactory) newProcess(command string, logger logrus.FieldLogger, } type Process interface { - dispense(key kindAndName) (interface{}, error) + dispense(key KindAndName) (interface{}, error) exited() bool kill() } @@ -124,21 +124,21 @@ func removeFeaturesFlag(args []string) []string { return commandArgs } -func (r *process) dispense(key kindAndName) (interface{}, error) { +func (r *process) dispense(key KindAndName) (interface{}, error) { // This calls GRPCClient(clientConn) on the plugin instance registered for key.name. - dispensed, err := r.protocolClient.Dispense(key.kind.String()) + dispensed, err := r.protocolClient.Dispense(key.Kind.String()) if err != nil { return nil, errors.WithStack(err) } // Currently all plugins except for PluginLister dispense clientDispenser instances. if clientDispenser, ok := dispensed.(framework.ClientDispenser); ok { - if key.name == "" { - return nil, errors.Errorf("%s plugin requested but name is missing", key.kind.String()) + if key.Name == "" { + return nil, errors.Errorf("%s plugin requested but name is missing", key.Kind.String()) } // Get the instance that implements our plugin interface (e.g. ObjectStore) that is a gRPC-based // client - dispensed = clientDispenser.ClientFor(key.name) + dispensed = clientDispenser.ClientFor(key.Name) } return dispensed, nil diff --git a/pkg/plugin/clientmgmt/process_test.go b/pkg/plugin/clientmgmt/process/process_test.go similarity index 92% rename from pkg/plugin/clientmgmt/process_test.go rename to pkg/plugin/clientmgmt/process/process_test.go index ac82ade87a..a44254dbd0 100644 --- a/pkg/plugin/clientmgmt/process_test.go +++ b/pkg/plugin/clientmgmt/process/process_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "testing" @@ -94,20 +94,20 @@ func TestDispense(t *testing.T) { var client interface{} - key := kindAndName{} + key := KindAndName{} if tc.clientDispenser { - key.kind = framework.PluginKindObjectStore - protocolClient.On("Dispense", key.kind.String()).Return(clientDispenser, tc.dispenseError) + key.Kind = framework.PluginKindObjectStore + protocolClient.On("Dispense", key.Kind.String()).Return(clientDispenser, tc.dispenseError) if !tc.missingKeyName { - key.name = "aws" + key.Name = "aws" client = &framework.BackupItemActionGRPCClient{} - clientDispenser.On("ClientFor", key.name).Return(client) + clientDispenser.On("ClientFor", key.Name).Return(client) } } else { - key.kind = framework.PluginKindPluginLister + key.Kind = framework.PluginKindPluginLister client = &framework.PluginListerGRPCClient{} - protocolClient.On("Dispense", key.kind.String()).Return(client, tc.dispenseError) + protocolClient.On("Dispense", key.Kind.String()).Return(client, tc.dispenseError) } dispensed, err := p.dispense(key) diff --git a/pkg/plugin/clientmgmt/registry.go b/pkg/plugin/clientmgmt/process/registry.go similarity index 91% rename from pkg/plugin/clientmgmt/registry.go rename to pkg/plugin/clientmgmt/process/registry.go index fe8a7f4b44..2e51ea2a2b 100644 --- a/pkg/plugin/clientmgmt/registry.go +++ b/pkg/plugin/clientmgmt/process/registry.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "fmt" @@ -38,10 +38,10 @@ type Registry interface { Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) } -// kindAndName is a convenience struct that combines a PluginKind and a name. -type kindAndName struct { - kind framework.PluginKind - name string +// KindAndName is a convenience struct that combines a PluginKind and a name. +type KindAndName struct { + Kind framework.PluginKind + Name string } // registry implements Registry. @@ -53,7 +53,7 @@ type registry struct { processFactory ProcessFactory fs filesystem.Interface - pluginsByID map[kindAndName]framework.PluginIdentifier + pluginsByID map[KindAndName]framework.PluginIdentifier pluginsByKind map[framework.PluginKind][]framework.PluginIdentifier } @@ -66,7 +66,7 @@ func NewRegistry(dir string, logger logrus.FieldLogger, logLevel logrus.Level) R processFactory: newProcessFactory(), fs: filesystem.NewFileSystem(), - pluginsByID: make(map[kindAndName]framework.PluginIdentifier), + pluginsByID: make(map[KindAndName]framework.PluginIdentifier), pluginsByKind: make(map[framework.PluginKind][]framework.PluginIdentifier), } } @@ -117,7 +117,7 @@ func (r *registry) List(kind framework.PluginKind) []framework.PluginIdentifier // Get returns info about a plugin with the given name and kind, or an // error if one cannot be found. func (r *registry) Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) { - p, found := r.pluginsByID[kindAndName{kind: kind, name: name}] + p, found := r.pluginsByID[KindAndName{Kind: kind, Name: name}] if !found { return framework.PluginIdentifier{}, newPluginNotFoundError(kind, name) } @@ -182,7 +182,7 @@ func (r *registry) listPlugins(command string) ([]framework.PluginIdentifier, er } defer process.kill() - plugin, err := process.dispense(kindAndName{kind: framework.PluginKindPluginLister}) + plugin, err := process.dispense(KindAndName{Kind: framework.PluginKindPluginLister}) if err != nil { return nil, err } @@ -197,7 +197,7 @@ func (r *registry) listPlugins(command string) ([]framework.PluginIdentifier, er // register registers a PluginIdentifier with the registry. func (r *registry) register(id framework.PluginIdentifier) error { - key := kindAndName{kind: id.Kind, name: id.Name} + key := KindAndName{Kind: id.Kind, Name: id.Name} if existing, found := r.pluginsByID[key]; found { return newDuplicatePluginRegistrationError(existing, id) } @@ -214,20 +214,20 @@ func (r *registry) register(id framework.PluginIdentifier) error { } // pluginNotFoundError indicates a plugin could not be located for kind and name. -type pluginNotFoundError struct { +type PluginNotFoundError struct { kind framework.PluginKind name string } // newPluginNotFoundError returns a new pluginNotFoundError for kind and name. -func newPluginNotFoundError(kind framework.PluginKind, name string) *pluginNotFoundError { - return &pluginNotFoundError{ +func newPluginNotFoundError(kind framework.PluginKind, name string) *PluginNotFoundError { + return &PluginNotFoundError{ kind: kind, name: name, } } -func (e *pluginNotFoundError) Error() string { +func (e *PluginNotFoundError) Error() string { return fmt.Sprintf("unable to locate %v plugin named %s", e.kind, e.name) } diff --git a/pkg/plugin/clientmgmt/registry_test.go b/pkg/plugin/clientmgmt/process/registry_test.go similarity index 99% rename from pkg/plugin/clientmgmt/registry_test.go rename to pkg/plugin/clientmgmt/process/registry_test.go index 45bbcbb899..cd0614f857 100644 --- a/pkg/plugin/clientmgmt/registry_test.go +++ b/pkg/plugin/clientmgmt/process/registry_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "os" diff --git a/pkg/plugin/clientmgmt/restartable_process.go b/pkg/plugin/clientmgmt/process/restartable_process.go similarity index 75% rename from pkg/plugin/clientmgmt/restartable_process.go rename to pkg/plugin/clientmgmt/process/restartable_process.go index 54211d8d1f..21ed810225 100644 --- a/pkg/plugin/clientmgmt/restartable_process.go +++ b/pkg/plugin/clientmgmt/process/restartable_process.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package process import ( "sync" @@ -24,26 +24,26 @@ import ( ) type RestartableProcessFactory interface { - newRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (RestartableProcess, error) + NewRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (RestartableProcess, error) } type restartableProcessFactory struct { } -func newRestartableProcessFactory() RestartableProcessFactory { +func NewRestartableProcessFactory() RestartableProcessFactory { return &restartableProcessFactory{} } -func (rpf *restartableProcessFactory) newRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (RestartableProcess, error) { +func (rpf *restartableProcessFactory) NewRestartableProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (RestartableProcess, error) { return newRestartableProcess(command, logger, logLevel) } type RestartableProcess interface { - addReinitializer(key kindAndName, r reinitializer) - reset() error - resetIfNeeded() error - getByKindAndName(key kindAndName) (interface{}, error) - stop() + AddReinitializer(key KindAndName, r Reinitializer) + Reset() error + ResetIfNeeded() error + GetByKindAndName(key KindAndName) (interface{}, error) + Stop() } // restartableProcess encapsulates the lifecycle for all plugins contained in a single executable file. It is able @@ -57,15 +57,15 @@ type restartableProcess struct { // lock guards all of the fields below lock sync.RWMutex process Process - plugins map[kindAndName]interface{} - reinitializers map[kindAndName]reinitializer + plugins map[KindAndName]interface{} + reinitializers map[KindAndName]Reinitializer resetFailures int } // reinitializer is capable of reinitializing a restartable plugin instance using the newly dispensed plugin. -type reinitializer interface { +type Reinitializer interface { // reinitialize reinitializes a restartable plugin instance using the newly dispensed plugin. - reinitialize(dispensed interface{}) error + Reinitialize(dispensed interface{}) error } // newRestartableProcess creates a new restartableProcess for the given command and options. @@ -74,26 +74,26 @@ func newRestartableProcess(command string, logger logrus.FieldLogger, logLevel l command: command, logger: logger, logLevel: logLevel, - plugins: make(map[kindAndName]interface{}), - reinitializers: make(map[kindAndName]reinitializer), + plugins: make(map[KindAndName]interface{}), + reinitializers: make(map[KindAndName]Reinitializer), } // This launches the process - err := p.reset() + err := p.Reset() return p, err } -// addReinitializer registers the reinitializer r for key. -func (p *restartableProcess) addReinitializer(key kindAndName, r reinitializer) { +// AddReinitializer registers the reinitializer r for key. +func (p *restartableProcess) AddReinitializer(key KindAndName, r Reinitializer) { p.lock.Lock() defer p.lock.Unlock() p.reinitializers[key] = r } -// reset acquires the lock and calls resetLH. -func (p *restartableProcess) reset() error { +// Reset acquires the lock and calls resetLH. +func (p *restartableProcess) Reset() error { p.lock.Lock() defer p.lock.Unlock() @@ -118,7 +118,7 @@ func (p *restartableProcess) resetLH() error { // Redispense any previously dispensed plugins, reinitializing if necessary. // Start by creating a new map to hold the newly dispensed plugins. - newPlugins := make(map[kindAndName]interface{}) + newPlugins := make(map[KindAndName]interface{}) for key := range p.plugins { // Re-dispense dispensed, err := p.process.dispense(key) @@ -131,7 +131,7 @@ func (p *restartableProcess) resetLH() error { // Reinitialize if r, found := p.reinitializers[key]; found { - if err := r.reinitialize(dispensed); err != nil { + if err := r.Reinitialize(dispensed); err != nil { p.resetFailures++ return err } @@ -146,8 +146,8 @@ func (p *restartableProcess) resetLH() error { return nil } -// resetIfNeeded checks if the plugin process has exited and resets p if it has. -func (p *restartableProcess) resetIfNeeded() error { +// ResetIfNeeded checks if the plugin process has exited and resets p if it has. +func (p *restartableProcess) ResetIfNeeded() error { p.lock.Lock() defer p.lock.Unlock() @@ -159,8 +159,8 @@ func (p *restartableProcess) resetIfNeeded() error { return nil } -// getByKindAndName acquires the lock and calls getByKindAndNameLH. -func (p *restartableProcess) getByKindAndName(key kindAndName) (interface{}, error) { +// GetByKindAndName acquires the lock and calls getByKindAndNameLH. +func (p *restartableProcess) GetByKindAndName(key KindAndName) (interface{}, error) { p.lock.Lock() defer p.lock.Unlock() @@ -169,7 +169,7 @@ func (p *restartableProcess) getByKindAndName(key kindAndName) (interface{}, err // getByKindAndNameLH returns the dispensed plugin for key. If the plugin hasn't been dispensed before, it dispenses a // new one. -func (p *restartableProcess) getByKindAndNameLH(key kindAndName) (interface{}, error) { +func (p *restartableProcess) getByKindAndNameLH(key KindAndName) (interface{}, error) { dispensed, found := p.plugins[key] if found { return dispensed, nil @@ -184,7 +184,7 @@ func (p *restartableProcess) getByKindAndNameLH(key kindAndName) (interface{}, e } // stop terminates the plugin process. -func (p *restartableProcess) stop() { +func (p *restartableProcess) Stop() { p.lock.Lock() p.process.kill() p.lock.Unlock() diff --git a/pkg/plugin/clientmgmt/restartable_delegate_test.go b/pkg/plugin/clientmgmt/restartable_delegate_test.go index 3e3366c88e..c71d3e2769 100644 --- a/pkg/plugin/clientmgmt/restartable_delegate_test.go +++ b/pkg/plugin/clientmgmt/restartable_delegate_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" ) @@ -43,7 +44,7 @@ type mockable interface { func runRestartableDelegateTests( t *testing.T, kind framework.PluginKind, - newRestartable func(key kindAndName, p RestartableProcess) interface{}, + newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, newMock func() mockable, tests ...restartableDelegateTest, ) { @@ -54,9 +55,9 @@ func runRestartableDelegateTests( defer p.AssertExpectations(t) // getDelegate error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "delegateName" - key := kindAndName{kind: kind, name: name} + key := process.KindAndName{Kind: kind, Name: name} r := newRestartable(key, p) // Get the method we're going to call using reflection @@ -107,13 +108,13 @@ func runRestartableDelegateTests( checkOutputs(tc.expectedErrorOutputs, actual) // Invoke delegate, make sure all returned values are passed through - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) delegate := newMock() delegate.Test(t) defer delegate.AssertExpectations(t) - p.On("getByKindAndName", key).Return(delegate, nil) + p.On("GetByKindAndName", key).Return(delegate, nil) // Set up the mocked method in the delegate delegate.On(tc.function, tc.inputs...).Return(tc.expectedDelegateOutputs...) diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action.go b/pkg/plugin/clientmgmt/restartable_delete_item_action.go index 266500c7a8..977203aae6 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action.go @@ -19,6 +19,7 @@ package clientmgmt import ( "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -28,15 +29,15 @@ import ( // call, the restartableDeleteItemAction asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. type restartableDeleteItemAction struct { - key kindAndName - sharedPluginProcess RestartableProcess + key process.KindAndName + sharedPluginProcess process.RestartableProcess config map[string]string } -// newRestartableDeleteItemAction returns a new restartableDeleteItemAction. -func newRestartableDeleteItemAction(name string, sharedPluginProcess RestartableProcess) *restartableDeleteItemAction { +// NewRestartableDeleteItemAction returns a new restartableDeleteItemAction. +func NewRestartableDeleteItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableDeleteItemAction { r := &restartableDeleteItemAction{ - key: kindAndName{kind: framework.PluginKindDeleteItemAction, name: name}, + key: process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r @@ -45,7 +46,7 @@ func newRestartableDeleteItemAction(name string, sharedPluginProcess Restartable // getDeleteItemAction returns the delete item action for this restartableDeleteItemAction. It does *not* restart the // plugin process. func (r *restartableDeleteItemAction) getDeleteItemAction() (velero.DeleteItemAction, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) if err != nil { return nil, err } @@ -60,7 +61,7 @@ func (r *restartableDeleteItemAction) getDeleteItemAction() (velero.DeleteItemAc // getDelegate restarts the plugin process (if needed) and returns the delete item action for this restartableDeleteItemAction. func (r *restartableDeleteItemAction) getDelegate() (velero.DeleteItemAction, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go index 641959e17f..65fb9646ca 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" @@ -59,10 +60,10 @@ func TestRestartableGetDeleteItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := kindAndName{kind: framework.PluginKindDeleteItemAction, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) - r := newRestartableDeleteItemAction(name, p) + r := NewRestartableDeleteItemAction(name, p) a, err := r.getDeleteItemAction() if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) @@ -80,19 +81,19 @@ func TestRestartableDeleteItemActionGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "pod" - r := newRestartableDeleteItemAction(name, p) + r := NewRestartableDeleteItemAction(name, p) a, err := r.getDelegate() assert.Nil(t, a) assert.EqualError(t, err, "reset error") // Happy path // Currently broken since this mocks out the restore item action interface - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) expected := new(mocks.DeleteItemAction) - key := kindAndName{kind: framework.PluginKindDeleteItemAction, name: name} - p.On("getByKindAndName", key).Return(expected, nil) + key := process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name} + p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -116,7 +117,7 @@ func TestRestartableDeleteItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindDeleteItemAction, - func(key kindAndName, p RestartableProcess) interface{} { + func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableDeleteItemAction{ key: key, sharedPluginProcess: p, diff --git a/pkg/plugin/clientmgmt/restartable_item_snapshotter.go b/pkg/plugin/clientmgmt/restartable_item_snapshotter.go index e211bcf28e..d2743789ac 100644 --- a/pkg/plugin/clientmgmt/restartable_item_snapshotter.go +++ b/pkg/plugin/clientmgmt/restartable_item_snapshotter.go @@ -21,21 +21,21 @@ import ( "github.com/pkg/errors" - isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" - + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) type restartableItemSnapshotter struct { - key kindAndName - sharedPluginProcess RestartableProcess + key process.KindAndName + sharedPluginProcess process.RestartableProcess } -// newRestartableItemSnapshotter returns a new newRestartableItemSnapshotter. -func newRestartableItemSnapshotter(name string, sharedPluginProcess RestartableProcess) *restartableItemSnapshotter { +// NewRestartableItemSnapshotter returns a new restartableItemSnapshotter. +func NewRestartableItemSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *restartableItemSnapshotter { r := &restartableItemSnapshotter{ - key: kindAndName{kind: framework.PluginKindItemSnapshotter, name: name}, + key: process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r @@ -44,7 +44,7 @@ func newRestartableItemSnapshotter(name string, sharedPluginProcess RestartableP // getItemSnapshotter returns the item snapshotter for this restartableItemSnapshotter. It does *not* restart the // plugin process. func (r *restartableItemSnapshotter) getItemSnapshotter() (isv1.ItemSnapshotter, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func (r *restartableItemSnapshotter) getItemSnapshotter() (isv1.ItemSnapshotter, // getDelegate restarts the plugin process (if needed) and returns the item snapshotter for this restartableItemSnapshotter. func (r *restartableItemSnapshotter) getDelegate() (isv1.ItemSnapshotter, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go index 30b2aab437..70d4a934ac 100644 --- a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go @@ -21,8 +21,6 @@ import ( "testing" "time" - isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,8 +30,10 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1/mocks" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) func TestRestartableGetItemSnapshotter(t *testing.T) { @@ -65,10 +65,10 @@ func TestRestartableGetItemSnapshotter(t *testing.T) { defer p.AssertExpectations(t) name := "pvc" - key := kindAndName{kind: framework.PluginKindItemSnapshotter, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) - r := newRestartableItemSnapshotter(name, p) + r := NewRestartableItemSnapshotter(name, p) a, err := r.getItemSnapshotter() if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) @@ -86,18 +86,18 @@ func TestRestartableItemSnapshotterGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "pvc" - r := newRestartableItemSnapshotter(name, p) + r := NewRestartableItemSnapshotter(name, p) a, err := r.getDelegate() assert.Nil(t, a) assert.EqualError(t, err, "reset error") // Happy path - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) expected := new(mocks.ItemSnapshotter) - key := kindAndName{kind: framework.PluginKindItemSnapshotter, name: name} - p.On("getByKindAndName", key).Return(expected, nil) + key := process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name} + p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -178,7 +178,7 @@ func TestRestartableItemSnasphotterDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindItemSnapshotter, - func(key kindAndName, p RestartableProcess) interface{} { + func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableItemSnapshotter{ key: key, sharedPluginProcess: p, diff --git a/pkg/plugin/clientmgmt/restartable_object_store.go b/pkg/plugin/clientmgmt/restartable_object_store.go index 95fbc75c5f..5be1113408 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store.go +++ b/pkg/plugin/clientmgmt/restartable_object_store.go @@ -22,6 +22,7 @@ import ( "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -31,29 +32,29 @@ import ( // call, the restartableObjectStore asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. type restartableObjectStore struct { - key kindAndName - sharedPluginProcess RestartableProcess + key process.KindAndName + sharedPluginProcess process.RestartableProcess // config contains the data used to initialize the plugin. It is used to reinitialize the plugin in the event its // sharedPluginProcess gets restarted. config map[string]string } -// newRestartableObjectStore returns a new restartableObjectStore. -func newRestartableObjectStore(name string, sharedPluginProcess RestartableProcess) *restartableObjectStore { - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} +// NewRestartableObjectStore returns a new restartableObjectStore. +func NewRestartableObjectStore(name string, sharedPluginProcess process.RestartableProcess) *restartableObjectStore { + key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: sharedPluginProcess, } // Register our reinitializer so we can reinitialize after a restart with r.config. - sharedPluginProcess.addReinitializer(key, r) + sharedPluginProcess.AddReinitializer(key, r) return r } // reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). -func (r *restartableObjectStore) reinitialize(dispensed interface{}) error { +func (r *restartableObjectStore) Reinitialize(dispensed interface{}) error { objectStore, ok := dispensed.(velero.ObjectStore) if !ok { return errors.Errorf("%T is not a ObjectStore!", dispensed) @@ -65,7 +66,7 @@ func (r *restartableObjectStore) reinitialize(dispensed interface{}) error { // getObjectStore returns the object store for this restartableObjectStore. It does *not* restart the // plugin process. func (r *restartableObjectStore) getObjectStore() (velero.ObjectStore, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) if err != nil { return nil, err } @@ -80,7 +81,7 @@ func (r *restartableObjectStore) getObjectStore() (velero.ObjectStore, error) { // getDelegate restarts the plugin process (if needed) and returns the object store for this restartableObjectStore. func (r *restartableObjectStore) getDelegate() (velero.ObjectStore, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_object_store_test.go b/pkg/plugin/clientmgmt/restartable_object_store_test.go index 57a64e8b2a..b4e94fccc6 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store_test.go +++ b/pkg/plugin/clientmgmt/restartable_object_store_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" ) @@ -60,8 +61,8 @@ func TestRestartableGetObjectStore(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := &restartableObjectStore{ key: key, @@ -85,7 +86,7 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} + key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, @@ -94,7 +95,7 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) { }, } - err := r.reinitialize(3) + err := r.Reinitialize(3) assert.EqualError(t, err, "int is not a ObjectStore!") objectStore := new(providermocks.ObjectStore) @@ -102,11 +103,11 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) { defer objectStore.AssertExpectations(t) objectStore.On("Init", r.config).Return(errors.Errorf("init error")).Once() - err = r.reinitialize(objectStore) + err = r.Reinitialize(objectStore) assert.EqualError(t, err, "init error") objectStore.On("Init", r.config).Return(nil) - err = r.reinitialize(objectStore) + err = r.Reinitialize(objectStore) assert.NoError(t, err) } @@ -116,9 +117,9 @@ func TestRestartableObjectStoreGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "aws" - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} + key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, @@ -128,11 +129,11 @@ func TestRestartableObjectStoreGetDelegate(t *testing.T) { assert.EqualError(t, err, "reset error") // Happy path - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) objectStore := new(providermocks.ObjectStore) objectStore.Test(t) defer objectStore.AssertExpectations(t) - p.On("getByKindAndName", key).Return(objectStore, nil) + p.On("GetByKindAndName", key).Return(objectStore, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -146,24 +147,24 @@ func TestRestartableObjectStoreInit(t *testing.T) { // getObjectStore error name := "aws" - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} + key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, } - p.On("getByKindAndName", key).Return(nil, errors.Errorf("getByKindAndName error")).Once() + p.On("GetByKindAndName", key).Return(nil, errors.Errorf("GetByKindAndName error")).Once() config := map[string]string{ "color": "blue", } err := r.Init(config) - assert.EqualError(t, err, "getByKindAndName error") + assert.EqualError(t, err, "GetByKindAndName error") // Delegate returns error objectStore := new(providermocks.ObjectStore) objectStore.Test(t) defer objectStore.AssertExpectations(t) - p.On("getByKindAndName", key).Return(objectStore, nil) + p.On("GetByKindAndName", key).Return(objectStore, nil) objectStore.On("Init", config).Return(errors.Errorf("Init error")).Once() err = r.Init(config) @@ -187,7 +188,7 @@ func TestRestartableObjectStoreDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindObjectStore, - func(key kindAndName, p RestartableProcess) interface{} { + func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableObjectStore{ key: key, sharedPluginProcess: p, diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action.go b/pkg/plugin/clientmgmt/restartable_restore_item_action.go index 0f55ebfdec..c4e6911b41 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_restore_item_action.go @@ -19,24 +19,25 @@ package clientmgmt import ( "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) -// restartableRestoreItemAction is a restore item action for a given implementation (such as "pod"). It is associated with +// RestartableRestoreItemAction is a restore item action for a given implementation (such as "pod"). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method // call, the restartableRestoreItemAction asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. type restartableRestoreItemAction struct { - key kindAndName - sharedPluginProcess RestartableProcess + key process.KindAndName + sharedPluginProcess process.RestartableProcess config map[string]string } -// newRestartableRestoreItemAction returns a new restartableRestoreItemAction. -func newRestartableRestoreItemAction(name string, sharedPluginProcess RestartableProcess) *restartableRestoreItemAction { +// NewRestartableRestoreItemAction returns a new RestartableRestoreItemAction. +func NewRestartableRestoreItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableRestoreItemAction { r := &restartableRestoreItemAction{ - key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}, + key: process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r @@ -45,7 +46,7 @@ func newRestartableRestoreItemAction(name string, sharedPluginProcess Restartabl // getRestoreItemAction returns the restore item action for this restartableRestoreItemAction. It does *not* restart the // plugin process. func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreItemAction, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) if err != nil { return nil, err } @@ -60,7 +61,7 @@ func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreIte // getDelegate restarts the plugin process (if needed) and returns the restore item action for this restartableRestoreItemAction. func (r *restartableRestoreItemAction) getDelegate() (velero.RestoreItemAction, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go b/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go index 8a37cdb4a9..3d4fd52e99 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go +++ b/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/restore/mocks" @@ -59,10 +60,10 @@ func TestRestartableGetRestoreItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := kindAndName{kind: framework.PluginKindRestoreItemAction, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) - r := newRestartableRestoreItemAction(name, p) + r := NewRestartableRestoreItemAction(name, p) a, err := r.getRestoreItemAction() if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) @@ -80,18 +81,18 @@ func TestRestartableRestoreItemActionGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "pod" - r := newRestartableRestoreItemAction(name, p) + r := NewRestartableRestoreItemAction(name, p) a, err := r.getDelegate() assert.Nil(t, a) assert.EqualError(t, err, "reset error") // Happy path - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) expected := new(mocks.ItemAction) - key := kindAndName{kind: framework.PluginKindRestoreItemAction, name: name} - p.On("getByKindAndName", key).Return(expected, nil) + key := process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name} + p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -122,7 +123,7 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindRestoreItemAction, - func(key kindAndName, p RestartableProcess) interface{} { + func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableRestoreItemAction{ key: key, sharedPluginProcess: p, diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go index 5e46bf2299..f8a1a66b02 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go @@ -20,36 +20,37 @@ import ( "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) -// restartableVolumeSnapshotter is a volume snapshotter for a given implementation (such as "aws"). It is associated with +// RestartableVolumeSnapshotter is a volume snapshotter for a given implementation (such as "aws"). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method // call, the restartableVolumeSnapshotter asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. type restartableVolumeSnapshotter struct { - key kindAndName - sharedPluginProcess RestartableProcess + key process.KindAndName + sharedPluginProcess process.RestartableProcess config map[string]string } -// newRestartableVolumeSnapshotter returns a new restartableVolumeSnapshotter. -func newRestartableVolumeSnapshotter(name string, sharedPluginProcess RestartableProcess) *restartableVolumeSnapshotter { - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} +// NewRestartableVolumeSnapshotter returns a new RestartableVolumeSnapshotter. +func NewRestartableVolumeSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *restartableVolumeSnapshotter { + key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: sharedPluginProcess, } // Register our reinitializer so we can reinitialize after a restart with r.config. - sharedPluginProcess.addReinitializer(key, r) + sharedPluginProcess.AddReinitializer(key, r) return r } // reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). -func (r *restartableVolumeSnapshotter) reinitialize(dispensed interface{}) error { +func (r *restartableVolumeSnapshotter) Reinitialize(dispensed interface{}) error { volumeSnapshotter, ok := dispensed.(velero.VolumeSnapshotter) if !ok { return errors.Errorf("%T is not a VolumeSnapshotter!", dispensed) @@ -60,7 +61,7 @@ func (r *restartableVolumeSnapshotter) reinitialize(dispensed interface{}) error // getVolumeSnapshotter returns the volume snapshotter for this restartableVolumeSnapshotter. It does *not* restart the // plugin process. func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnapshotter, error) { - plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) if err != nil { return nil, err } @@ -75,7 +76,7 @@ func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnap // getDelegate restarts the plugin process (if needed) and returns the volume snapshotter for this restartableVolumeSnapshotter. func (r *restartableVolumeSnapshotter) getDelegate() (velero.VolumeSnapshotter, error) { - if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go index 762153e054..8d198ec96d 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" ) @@ -59,8 +60,8 @@ func TestRestartableGetVolumeSnapshotter(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} - p.On("getByKindAndName", key).Return(tc.plugin, tc.getError) + key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := &restartableVolumeSnapshotter{ key: key, @@ -84,7 +85,7 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} + key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, @@ -93,7 +94,7 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { }, } - err := r.reinitialize(3) + err := r.Reinitialize(3) assert.EqualError(t, err, "int is not a VolumeSnapshotter!") volumeSnapshotter := new(providermocks.VolumeSnapshotter) @@ -101,11 +102,11 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { defer volumeSnapshotter.AssertExpectations(t) volumeSnapshotter.On("Init", r.config).Return(errors.Errorf("init error")).Once() - err = r.reinitialize(volumeSnapshotter) + err = r.Reinitialize(volumeSnapshotter) assert.EqualError(t, err, "init error") volumeSnapshotter.On("Init", r.config).Return(nil) - err = r.reinitialize(volumeSnapshotter) + err = r.Reinitialize(volumeSnapshotter) assert.NoError(t, err) } @@ -115,9 +116,9 @@ func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { defer p.AssertExpectations(t) // Reset error - p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once() + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "aws" - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} + key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, @@ -127,11 +128,11 @@ func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { assert.EqualError(t, err, "reset error") // Happy path - p.On("resetIfNeeded").Return(nil) + p.On("ResetIfNeeded").Return(nil) volumeSnapshotter := new(providermocks.VolumeSnapshotter) volumeSnapshotter.Test(t) defer volumeSnapshotter.AssertExpectations(t) - p.On("getByKindAndName", key).Return(volumeSnapshotter, nil) + p.On("GetByKindAndName", key).Return(volumeSnapshotter, nil) a, err = r.getDelegate() assert.NoError(t, err) @@ -145,24 +146,24 @@ func TestRestartableVolumeSnapshotterInit(t *testing.T) { // getVolumeSnapshottererror name := "aws" - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} + key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, } - p.On("getByKindAndName", key).Return(nil, errors.Errorf("getByKindAndName error")).Once() + p.On("GetByKindAndName", key).Return(nil, errors.Errorf("GetByKindAndName error")).Once() config := map[string]string{ "color": "blue", } err := r.Init(config) - assert.EqualError(t, err, "getByKindAndName error") + assert.EqualError(t, err, "GetByKindAndName error") // Delegate returns error volumeSnapshotter := new(providermocks.VolumeSnapshotter) volumeSnapshotter.Test(t) defer volumeSnapshotter.AssertExpectations(t) - p.On("getByKindAndName", key).Return(volumeSnapshotter, nil) + p.On("GetByKindAndName", key).Return(volumeSnapshotter, nil) volumeSnapshotter.On("Init", config).Return(errors.Errorf("Init error")).Once() err = r.Init(config) @@ -198,7 +199,7 @@ func TestRestartableVolumeSnapshotterDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, framework.PluginKindVolumeSnapshotter, - func(key kindAndName, p RestartableProcess) interface{} { + func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, diff --git a/pkg/plugin/mocks/process_factory.go b/pkg/plugin/mocks/process_factory.go index 22abc81fc1..586e311216 100644 --- a/pkg/plugin/mocks/process_factory.go +++ b/pkg/plugin/mocks/process_factory.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,11 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ // Code generated by mockery v1.0.0. DO NOT EDIT. + package mocks -import logrus "github.com/sirupsen/logrus" -import mock "github.com/stretchr/testify/mock" -import "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" +import ( + logrus "github.com/sirupsen/logrus" + mock "github.com/stretchr/testify/mock" + + process "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" +) // ProcessFactory is an autogenerated mock type for the ProcessFactory type type ProcessFactory struct { @@ -26,15 +30,15 @@ type ProcessFactory struct { } // newProcess provides a mock function with given fields: command, logger, logLevel -func (_m *ProcessFactory) newProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (clientmgmt.Process, error) { +func (_m *ProcessFactory) newProcess(command string, logger logrus.FieldLogger, logLevel logrus.Level) (process.Process, error) { ret := _m.Called(command, logger, logLevel) - var r0 clientmgmt.Process - if rf, ok := ret.Get(0).(func(string, logrus.FieldLogger, logrus.Level) clientmgmt.Process); ok { + var r0 process.Process + if rf, ok := ret.Get(0).(func(string, logrus.FieldLogger, logrus.Level) process.Process); ok { r0 = rf(command, logger, logLevel) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(clientmgmt.Process) + r0 = ret.Get(0).(process.Process) } } From e3e2a8dfa03fdd83c0acdcb490901a370b92659e Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 1 Sep 2022 14:30:07 -0400 Subject: [PATCH 301/366] plugin/framework refactoring for BackupItemAction v1 Refactors the framework package to implement the plugin versioning changes needed for BIA v1 and overall package refactoring to support plugin versions in different packages. This should be all that's needed to move on to v2 for BackupItemAction. The remaining plugin types still need similar refactoring to what's being done here for BIA before attempting a v2 implementation. Signed-off-by: Scott Seago --- internal/delete/delete_item_action_handler.go | 2 +- internal/velero/serverstatusrequest.go | 5 +- pkg/backup/backup.go | 4 +- pkg/cmd/server/plugin/plugin.go | 11 +- .../server_status_request_controller.go | 3 +- .../server_status_request_controller_test.go | 3 +- .../v1/restartable_backup_item_action.go | 8 +- .../v1/restartable_backup_item_action_test.go | 16 +-- .../backupitemaction/v1/shared_test.go | 4 +- pkg/plugin/clientmgmt/manager.go | 27 +++-- pkg/plugin/clientmgmt/manager_test.go | 35 +++--- .../clientmgmt/process/client_builder.go | 15 +-- .../clientmgmt/process/client_builder_test.go | 15 +-- pkg/plugin/clientmgmt/process/process.go | 4 +- pkg/plugin/clientmgmt/process/process_test.go | 5 +- pkg/plugin/clientmgmt/process/registry.go | 32 ++++-- .../clientmgmt/restartable_delegate_test.go | 4 +- .../restartable_delete_item_action.go | 4 +- .../restartable_delete_item_action_test.go | 8 +- .../restartable_item_snapshotter.go | 4 +- .../restartable_item_snapshotter_test.go | 8 +- .../clientmgmt/restartable_object_store.go | 4 +- .../restartable_object_store_test.go | 12 +- .../restartable_restore_item_action.go | 4 +- .../restartable_restore_item_action_test.go | 8 +- .../restartable_volume_snapshotter.go | 6 +- .../restartable_volume_snapshotter_test.go | 12 +- pkg/plugin/framework/action_resolver.go | 10 +- pkg/plugin/framework/backup_item_action.go | 7 +- .../framework/backup_item_action_client.go | 19 ++-- .../framework/backup_item_action_server.go | 23 ++-- .../framework/backup_item_action_test.go | 11 +- .../{ => common}/client_dispenser.go | 20 ++-- .../{ => common}/client_dispenser_test.go | 18 +-- .../framework/{ => common}/client_errors.go | 16 +-- .../framework/{ => common}/handle_panic.go | 10 +- .../framework/{ => common}/plugin_base.go | 24 ++-- .../{ => common}/plugin_base_test.go | 12 +- .../framework/{ => common}/plugin_kinds.go | 7 +- .../framework/{ => common}/server_errors.go | 22 ++-- .../framework/{ => common}/server_mux.go | 40 +++---- .../framework/{ => common}/server_mux_test.go | 2 +- pkg/plugin/framework/delete_item_action.go | 7 +- .../framework/delete_item_action_client.go | 19 ++-- .../framework/delete_item_action_server.go | 21 ++-- pkg/plugin/framework/interface.go | 2 +- pkg/plugin/framework/item_snapshotter.go | 7 +- .../framework/item_snapshotter_client.go | 27 ++--- .../framework/item_snapshotter_server.go | 67 ++++++------ pkg/plugin/framework/object_store.go | 7 +- pkg/plugin/framework/object_store_client.go | 49 +++++---- pkg/plugin/framework/object_store_server.go | 61 ++++++----- pkg/plugin/framework/plugin_lister.go | 9 +- pkg/plugin/framework/restore_item_action.go | 7 +- .../framework/restore_item_action_client.go | 19 ++-- .../framework/restore_item_action_server.go | 25 +++-- pkg/plugin/framework/server.go | 103 +++++++++--------- pkg/plugin/framework/volume_snapshotter.go | 7 +- .../framework/volume_snapshotter_client.go | 39 +++---- .../framework/volume_snapshotter_server.go | 53 ++++----- .../backupitemaction/v1/BackupItemAction.go} | 34 +++--- pkg/restore/change_pvc_node_selector.go | 4 +- pkg/restore/change_storageclass_action.go | 4 +- pkg/restore/restic_restore_action.go | 6 +- pkg/restore/restore.go | 4 +- 65 files changed, 567 insertions(+), 518 deletions(-) rename pkg/plugin/framework/{ => common}/client_dispenser.go (86%) rename pkg/plugin/framework/{ => common}/client_dispenser_test.go (83%) rename pkg/plugin/framework/{ => common}/client_errors.go (84%) rename pkg/plugin/framework/{ => common}/handle_panic.go (84%) rename pkg/plugin/framework/{ => common}/plugin_base.go (65%) rename pkg/plugin/framework/{ => common}/plugin_base_test.go (84%) rename pkg/plugin/framework/{ => common}/plugin_kinds.go (89%) rename pkg/plugin/framework/{ => common}/server_errors.go (78%) rename pkg/plugin/framework/{ => common}/server_mux.go (78%) rename pkg/plugin/framework/{ => common}/server_mux_test.go (99%) rename pkg/{backup/mocks/item_action.go => plugin/velero/mocks/backupitemaction/v1/BackupItemAction.go} (58%) diff --git a/internal/delete/delete_item_action_handler.go b/internal/delete/delete_item_action_handler.go index b08c8e024e..bdcf511b24 100644 --- a/internal/delete/delete_item_action_handler.go +++ b/internal/delete/delete_item_action_handler.go @@ -48,7 +48,7 @@ type Context struct { func InvokeDeleteActions(ctx *Context) error { var err error resolver := framework.NewDeleteItemActionResolver(ctx.Actions) - ctx.resolvedActions, err = resolver.ResolveActions(ctx.DiscoveryHelper) + ctx.resolvedActions, err = resolver.ResolveActions(ctx.DiscoveryHelper, ctx.Log) // No actions installed and no error means we don't have to continue; // just do the backup deletion without worrying about plugins. if len(ctx.resolvedActions) == 0 && err == nil { diff --git a/internal/velero/serverstatusrequest.go b/internal/velero/serverstatusrequest.go index a220c5c19e..c9c8acc0a5 100644 --- a/internal/velero/serverstatusrequest.go +++ b/internal/velero/serverstatusrequest.go @@ -19,17 +19,18 @@ package velero import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) type PluginLister interface { // List returns all PluginIdentifiers for kind. - List(kind framework.PluginKind) []framework.PluginIdentifier + List(kind common.PluginKind) []framework.PluginIdentifier } // GetInstalledPluginInfo returns a list of installed plugins func GetInstalledPluginInfo(pluginLister PluginLister) []velerov1api.PluginInfo { var plugins []velerov1api.PluginInfo - for _, v := range framework.AllPluginKinds() { + for _, v := range common.AllPluginKinds() { list := pluginLister.List(v) for _, plugin := range list { pluginInfo := velerov1api.PluginInfo{ diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 771ee5ee15..2843cb54ba 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -214,13 +214,13 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, return err } - backupRequest.ResolvedActions, err = backupItemActionResolver.ResolveActions(kb.discoveryHelper) + backupRequest.ResolvedActions, err = backupItemActionResolver.ResolveActions(kb.discoveryHelper, log) if err != nil { log.WithError(errors.WithStack(err)).Debugf("Error from backupItemActionResolver.ResolveActions") return err } - backupRequest.ResolvedItemSnapshotters, err = itemSnapshotterResolver.ResolveActions(kb.discoveryHelper) + backupRequest.ResolvedItemSnapshotters, err = itemSnapshotterResolver.ResolveActions(kb.discoveryHelper, log) if err != nil { log.WithError(errors.WithStack(err)).Debugf("Error from itemSnapshotterResolver.ResolveActions") return err diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 5dec03856b..64dbe4572a 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -28,6 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/client" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" veleroplugin "github.com/vmware-tanzu/velero/pkg/plugin/framework" + plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/restore" ) @@ -76,7 +77,7 @@ func newPodBackupItemAction(logger logrus.FieldLogger) (interface{}, error) { return backup.NewPodAction(logger), nil } -func newServiceAccountBackupItemAction(f client.Factory) veleroplugin.HandlerInitializer { +func newServiceAccountBackupItemAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { // TODO(ncdc): consider a k8s style WantsKubernetesClientSet initialization approach clientset, err := f.KubeClient() @@ -101,7 +102,7 @@ func newServiceAccountBackupItemAction(f client.Factory) veleroplugin.HandlerIni } } -func newRemapCRDVersionAction(f client.Factory) veleroplugin.HandlerInitializer { +func newRemapCRDVersionAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { config, err := f.ClientConfig() if err != nil { @@ -138,7 +139,7 @@ func newInitRestoreHookPodAction(logger logrus.FieldLogger) (interface{}, error) return restore.NewInitRestoreHookPodAction(logger), nil } -func newResticRestoreItemAction(f client.Factory) veleroplugin.HandlerInitializer { +func newResticRestoreItemAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { client, err := f.KubeClient() if err != nil { @@ -174,7 +175,7 @@ func newCRDV1PreserveUnknownFieldsItemAction(logger logrus.FieldLogger) (interfa return restore.NewCRDV1PreserveUnknownFieldsAction(logger), nil } -func newChangeStorageClassRestoreItemAction(f client.Factory) veleroplugin.HandlerInitializer { +func newChangeStorageClassRestoreItemAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { client, err := f.KubeClient() if err != nil { @@ -197,7 +198,7 @@ func newClusterRoleBindingItemAction(logger logrus.FieldLogger) (interface{}, er return restore.NewClusterRoleBindingAction(logger), nil } -func newChangePVCNodeSelectorItemAction(f client.Factory) veleroplugin.HandlerInitializer { +func newChangePVCNodeSelectorItemAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { client, err := f.KubeClient() if err != nil { diff --git a/pkg/controller/server_status_request_controller.go b/pkg/controller/server_status_request_controller.go index 6d76576834..50ae606276 100644 --- a/pkg/controller/server_status_request_controller.go +++ b/pkg/controller/server_status_request_controller.go @@ -33,6 +33,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) const ( @@ -42,7 +43,7 @@ const ( type PluginLister interface { // List returns all PluginIdentifiers for kind. - List(kind framework.PluginKind) []framework.PluginIdentifier + List(kind common.PluginKind) []framework.PluginIdentifier } // serverStatusRequestReconciler reconciles a ServerStatusRequest object diff --git a/pkg/controller/server_status_request_controller_test.go b/pkg/controller/server_status_request_controller_test.go index c968a60180..f86ed49523 100644 --- a/pkg/controller/server_status_request_controller_test.go +++ b/pkg/controller/server_status_request_controller_test.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -247,7 +248,7 @@ type fakePluginLister struct { plugins []framework.PluginIdentifier } -func (l *fakePluginLister) List(kind framework.PluginKind) []framework.PluginIdentifier { +func (l *fakePluginLister) List(kind common.PluginKind) []framework.PluginIdentifier { var plugins []framework.PluginIdentifier for _, plugin := range l.plugins { if plugin.Kind == kind { diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go index 3317be280b..6cbca3f525 100644 --- a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action.go @@ -22,14 +22,14 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" ) // AdaptedBackupItemAction is a backup item action adapted to the v1 BackupItemAction API type AdaptedBackupItemAction struct { - Kind framework.PluginKind + Kind common.PluginKind // Get returns a restartable BackupItemAction for the given name and process, wrapping if necessary GetRestartable func(name string, restartableProcess process.RestartableProcess) biav1.BackupItemAction @@ -38,7 +38,7 @@ type AdaptedBackupItemAction struct { func AdaptedBackupItemActions() []AdaptedBackupItemAction { return []AdaptedBackupItemAction{ { - Kind: framework.PluginKindBackupItemAction, + Kind: common.PluginKindBackupItemAction, GetRestartable: func(name string, restartableProcess process.RestartableProcess) biav1.BackupItemAction { return NewRestartableBackupItemAction(name, restartableProcess) }, @@ -58,7 +58,7 @@ type RestartableBackupItemAction struct { // NewRestartableBackupItemAction returns a new RestartableBackupItemAction. func NewRestartableBackupItemAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableBackupItemAction { r := &RestartableBackupItemAction{ - Key: process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name}, + Key: process.KindAndName{Kind: common.PluginKindBackupItemAction, Name: name}, SharedPluginProcess: sharedPluginProcess, } return r diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go index 947a0f1505..d20b150d96 100644 --- a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go @@ -26,10 +26,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/backup/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/backupitemaction/v1" ) func TestRestartableGetBackupItemAction(t *testing.T) { @@ -51,7 +51,7 @@ func TestRestartableGetBackupItemAction(t *testing.T) { }, { name: "happy path", - plugin: new(mocks.ItemAction), + plugin: new(mocks.BackupItemAction), }, } @@ -61,7 +61,7 @@ func TestRestartableGetBackupItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name} + key := process.KindAndName{Kind: common.PluginKindBackupItemAction, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := NewRestartableBackupItemAction(name, p) @@ -91,8 +91,8 @@ func TestRestartableBackupItemActionGetDelegate(t *testing.T) { // Happy path p.On("ResetIfNeeded").Return(nil) - expected := new(mocks.ItemAction) - key := process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name} + expected := new(mocks.BackupItemAction) + key := process.KindAndName{Kind: common.PluginKindBackupItemAction, Name: name} p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() @@ -123,7 +123,7 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, - framework.PluginKindBackupItemAction, + common.PluginKindBackupItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &RestartableBackupItemAction{ Key: key, @@ -131,7 +131,7 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) { } }, func() mockable { - return new(mocks.ItemAction) + return new(mocks.BackupItemAction) }, restartableDelegateTest{ function: "AppliesTo", diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go b/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go index a612c764cb..6745b8fa0b 100644 --- a/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) type mockRestartableProcess struct { @@ -70,7 +70,7 @@ type mockable interface { func runRestartableDelegateTests( t *testing.T, - kind framework.PluginKind, + kind common.PluginKind, newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, newMock func() mockable, tests ...restartableDelegateTest, diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index e2b55951c7..292e8e9c2b 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -26,7 +26,7 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" @@ -68,6 +68,9 @@ type Manager interface { CleanupClients() } +// Used checking for adapted plugin versions +var pluginNotFoundErrType = &process.PluginNotFoundError{} + // manager implements Manager. type manager struct { logger logrus.FieldLogger @@ -106,7 +109,7 @@ func (m *manager) CleanupClients() { // getRestartableProcess returns a restartableProcess for a plugin identified by kind and name, creating a // restartableProcess if it is the first time it has been requested. -func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) (process.RestartableProcess, error) { +func (m *manager) getRestartableProcess(kind common.PluginKind, name string) (process.RestartableProcess, error) { m.lock.Lock() defer m.lock.Unlock() @@ -145,7 +148,7 @@ func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStore, name) + restartableProcess, err := m.getRestartableProcess(common.PluginKindObjectStore, name) if err != nil { return nil, err } @@ -159,7 +162,7 @@ func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) { func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindVolumeSnapshotter, name) + restartableProcess, err := m.getRestartableProcess(common.PluginKindVolumeSnapshotter, name) if err != nil { return nil, err } @@ -171,7 +174,7 @@ func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, e // GetBackupItemActions returns all backup item actions as restartableBackupItemActions. func (m *manager) GetBackupItemActions() ([]biav1.BackupItemAction, error) { - list := m.registry.List(framework.PluginKindBackupItemAction) + list := m.registry.List(common.PluginKindBackupItemAction) actions := make([]biav1.BackupItemAction, 0, len(list)) @@ -196,7 +199,7 @@ func (m *manager) GetBackupItemAction(name string) (biav1.BackupItemAction, erro for _, adaptedBackupItemAction := range biav1cli.AdaptedBackupItemActions() { restartableProcess, err := m.getRestartableProcess(adaptedBackupItemAction.Kind, name) // Check if plugin was not found - if errors.Is(err, &process.PluginNotFoundError{}) { + if errors.As(err, &pluginNotFoundErrType) { continue } if err != nil { @@ -209,7 +212,7 @@ func (m *manager) GetBackupItemAction(name string) (biav1.BackupItemAction, erro // GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions. func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { - list := m.registry.List(framework.PluginKindRestoreItemAction) + list := m.registry.List(common.PluginKindRestoreItemAction) actions := make([]velero.RestoreItemAction, 0, len(list)) @@ -231,7 +234,7 @@ func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemAction, name) + restartableProcess, err := m.getRestartableProcess(common.PluginKindRestoreItemAction, name) if err != nil { return nil, err } @@ -242,7 +245,7 @@ func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, e // GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions. func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { - list := m.registry.List(framework.PluginKindDeleteItemAction) + list := m.registry.List(common.PluginKindDeleteItemAction) actions := make([]velero.DeleteItemAction, 0, len(list)) @@ -264,7 +267,7 @@ func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindDeleteItemAction, name) + restartableProcess, err := m.getRestartableProcess(common.PluginKindDeleteItemAction, name) if err != nil { return nil, err } @@ -276,7 +279,7 @@ func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, err func (m *manager) GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindItemSnapshotter, name) + restartableProcess, err := m.getRestartableProcess(common.PluginKindItemSnapshotter, name) if err != nil { return nil, err } @@ -286,7 +289,7 @@ func (m *manager) GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) } func (m *manager) GetItemSnapshotters() ([]isv1.ItemSnapshotter, error) { - list := m.registry.List(framework.PluginKindItemSnapshotter) + list := m.registry.List(common.PluginKindItemSnapshotter) actions := make([]isv1.ItemSnapshotter, 0, len(list)) diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index 64b8bf4eda..b5b54b3f49 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -29,6 +29,7 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -41,12 +42,12 @@ func (r *mockRegistry) DiscoverPlugins() error { return args.Error(0) } -func (r *mockRegistry) List(kind framework.PluginKind) []framework.PluginIdentifier { +func (r *mockRegistry) List(kind common.PluginKind) []framework.PluginIdentifier { args := r.Called(kind) return args.Get(0).([]framework.PluginIdentifier) } -func (r *mockRegistry) Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) { +func (r *mockRegistry) Get(kind common.PluginKind, name string) (framework.PluginIdentifier, error) { args := r.Called(kind, name) var id framework.PluginIdentifier if args.Get(0) != nil { @@ -123,7 +124,7 @@ func TestGetRestartableProcess(t *testing.T) { m.restartableProcessFactory = factory // Test 1: registry error - pluginKind := framework.PluginKindBackupItemAction + pluginKind := common.PluginKindBackupItemAction pluginName := "pod" registry.On("Get", pluginKind, pluginName).Return(nil, errors.Errorf("registry")).Once() rp, err := m.getRestartableProcess(pluginKind, pluginName) @@ -177,14 +178,14 @@ func TestCleanupClients(t *testing.T) { func TestGetObjectStore(t *testing.T) { getPluginTest(t, - framework.PluginKindObjectStore, + common.PluginKindObjectStore, "velero.io/aws", func(m Manager, name string) (interface{}, error) { return m.GetObjectStore(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableObjectStore{ - key: process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name}, + key: process.KindAndName{Kind: common.PluginKindObjectStore, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -194,14 +195,14 @@ func TestGetObjectStore(t *testing.T) { func TestGetVolumeSnapshotter(t *testing.T) { getPluginTest(t, - framework.PluginKindVolumeSnapshotter, + common.PluginKindVolumeSnapshotter, "velero.io/aws", func(m Manager, name string) (interface{}, error) { return m.GetVolumeSnapshotter(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableVolumeSnapshotter{ - key: process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name}, + key: process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -211,14 +212,14 @@ func TestGetVolumeSnapshotter(t *testing.T) { func TestGetBackupItemAction(t *testing.T) { getPluginTest(t, - framework.PluginKindBackupItemAction, + common.PluginKindBackupItemAction, "velero.io/pod", func(m Manager, name string) (interface{}, error) { return m.GetBackupItemAction(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &biav1cli.RestartableBackupItemAction{ - Key: process.KindAndName{Kind: framework.PluginKindBackupItemAction, Name: name}, + Key: process.KindAndName{Kind: common.PluginKindBackupItemAction, Name: name}, SharedPluginProcess: sharedPluginProcess, } }, @@ -228,14 +229,14 @@ func TestGetBackupItemAction(t *testing.T) { func TestGetRestoreItemAction(t *testing.T) { getPluginTest(t, - framework.PluginKindRestoreItemAction, + common.PluginKindRestoreItemAction, "velero.io/pod", func(m Manager, name string) (interface{}, error) { return m.GetRestoreItemAction(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableRestoreItemAction{ - key: process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name}, + key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -245,7 +246,7 @@ func TestGetRestoreItemAction(t *testing.T) { func getPluginTest( t *testing.T, - kind framework.PluginKind, + kind common.PluginKind, name string, getPluginFunc func(m Manager, name string) (interface{}, error), expectedResultFunc func(name string, sharedPluginProcess process.RestartableProcess) interface{}, @@ -329,7 +330,7 @@ func TestGetBackupItemActions(t *testing.T) { defer factory.AssertExpectations(t) m.restartableProcessFactory = factory - pluginKind := framework.PluginKindBackupItemAction + pluginKind := common.PluginKindBackupItemAction var pluginIDs []framework.PluginIdentifier for i := range tc.names { pluginID := framework.PluginIdentifier{ @@ -421,7 +422,7 @@ func TestGetRestoreItemActions(t *testing.T) { defer factory.AssertExpectations(t) m.restartableProcessFactory = factory - pluginKind := framework.PluginKindRestoreItemAction + pluginKind := common.PluginKindRestoreItemAction var pluginIDs []framework.PluginIdentifier for i := range tc.names { pluginID := framework.PluginIdentifier{ @@ -480,14 +481,14 @@ func TestGetRestoreItemActions(t *testing.T) { func TestGetDeleteItemAction(t *testing.T) { getPluginTest(t, - framework.PluginKindDeleteItemAction, + common.PluginKindDeleteItemAction, "velero.io/deleter", func(m Manager, name string) (interface{}, error) { return m.GetDeleteItemAction(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { return &restartableDeleteItemAction{ - key: process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name}, + key: process.KindAndName{Kind: common.PluginKindDeleteItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } }, @@ -520,7 +521,7 @@ func TestGetDeleteItemActions(t *testing.T) { defer factory.AssertExpectations(t) m.restartableProcessFactory = factory - pluginKind := framework.PluginKindDeleteItemAction + pluginKind := common.PluginKindDeleteItemAction var pluginIDs []framework.PluginIdentifier for i := range tc.names { pluginID := framework.PluginIdentifier{ diff --git a/pkg/plugin/clientmgmt/process/client_builder.go b/pkg/plugin/clientmgmt/process/client_builder.go index 0d3948807e..4b6c27731f 100644 --- a/pkg/plugin/clientmgmt/process/client_builder.go +++ b/pkg/plugin/clientmgmt/process/client_builder.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) // clientBuilder builds go-plugin Clients. @@ -67,13 +68,13 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig { HandshakeConfig: framework.Handshake(), AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC}, Plugins: map[string]hcplugin.Plugin{ - string(framework.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(framework.ClientLogger(b.clientLogger)), - string(framework.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(framework.ClientLogger(b.clientLogger)), - string(framework.PluginKindObjectStore): framework.NewObjectStorePlugin(framework.ClientLogger(b.clientLogger)), - string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{}, - string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)), - string(framework.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(framework.ClientLogger(b.clientLogger)), - string(framework.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(framework.ClientLogger(b.clientLogger)), + string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindPluginLister): &framework.PluginListerPlugin{}, + string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(b.clientLogger)), }, Logger: b.pluginLogger, Cmd: exec.Command(b.commandName, b.commandArgs...), diff --git a/pkg/plugin/clientmgmt/process/client_builder_test.go b/pkg/plugin/clientmgmt/process/client_builder_test.go index 1b9c77145d..2a3f6df615 100644 --- a/pkg/plugin/clientmgmt/process/client_builder_test.go +++ b/pkg/plugin/clientmgmt/process/client_builder_test.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -60,13 +61,13 @@ func TestClientConfig(t *testing.T) { HandshakeConfig: framework.Handshake(), AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC}, Plugins: map[string]hcplugin.Plugin{ - string(framework.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(framework.ClientLogger(logger)), - string(framework.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(framework.ClientLogger(logger)), - string(framework.PluginKindObjectStore): framework.NewObjectStorePlugin(framework.ClientLogger(logger)), - string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{}, - string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(logger)), - string(framework.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(framework.ClientLogger(logger)), - string(framework.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(framework.ClientLogger(logger)), + string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(logger)), + string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(logger)), + string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(logger)), + string(common.PluginKindPluginLister): &framework.PluginListerPlugin{}, + string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(logger)), + string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(logger)), + string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(logger)), }, Logger: cb.pluginLogger, Cmd: exec.Command(cb.commandName, cb.commandArgs...), diff --git a/pkg/plugin/clientmgmt/process/process.go b/pkg/plugin/clientmgmt/process/process.go index 66033768d8..b6be276871 100644 --- a/pkg/plugin/clientmgmt/process/process.go +++ b/pkg/plugin/clientmgmt/process/process.go @@ -23,7 +23,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) type ProcessFactory interface { @@ -132,7 +132,7 @@ func (r *process) dispense(key KindAndName) (interface{}, error) { } // Currently all plugins except for PluginLister dispense clientDispenser instances. - if clientDispenser, ok := dispensed.(framework.ClientDispenser); ok { + if clientDispenser, ok := dispensed.(common.ClientDispenser); ok { if key.Name == "" { return nil, errors.Errorf("%s plugin requested but name is missing", key.Kind.String()) } diff --git a/pkg/plugin/clientmgmt/process/process_test.go b/pkg/plugin/clientmgmt/process/process_test.go index a44254dbd0..ce7c7c07b9 100644 --- a/pkg/plugin/clientmgmt/process/process_test.go +++ b/pkg/plugin/clientmgmt/process/process_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) type mockClientProtocol struct { @@ -96,7 +97,7 @@ func TestDispense(t *testing.T) { key := KindAndName{} if tc.clientDispenser { - key.Kind = framework.PluginKindObjectStore + key.Kind = common.PluginKindObjectStore protocolClient.On("Dispense", key.Kind.String()).Return(clientDispenser, tc.dispenseError) if !tc.missingKeyName { @@ -105,7 +106,7 @@ func TestDispense(t *testing.T) { clientDispenser.On("ClientFor", key.Name).Return(client) } } else { - key.Kind = framework.PluginKindPluginLister + key.Kind = common.PluginKindPluginLister client = &framework.PluginListerGRPCClient{} protocolClient.On("Dispense", key.Kind.String()).Return(client, tc.dispenseError) } diff --git a/pkg/plugin/clientmgmt/process/registry.go b/pkg/plugin/clientmgmt/process/registry.go index 2e51ea2a2b..11a6bf43ed 100644 --- a/pkg/plugin/clientmgmt/process/registry.go +++ b/pkg/plugin/clientmgmt/process/registry.go @@ -25,6 +25,7 @@ import ( "github.com/sirupsen/logrus" "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -33,14 +34,14 @@ type Registry interface { // DiscoverPlugins discovers all available plugins. DiscoverPlugins() error // List returns all PluginIdentifiers for kind. - List(kind framework.PluginKind) []framework.PluginIdentifier + List(kind common.PluginKind) []framework.PluginIdentifier // Get returns the PluginIdentifier for kind and name. - Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) + Get(kind common.PluginKind, name string) (framework.PluginIdentifier, error) } // KindAndName is a convenience struct that combines a PluginKind and a name. type KindAndName struct { - Kind framework.PluginKind + Kind common.PluginKind Name string } @@ -54,7 +55,7 @@ type registry struct { processFactory ProcessFactory fs filesystem.Interface pluginsByID map[KindAndName]framework.PluginIdentifier - pluginsByKind map[framework.PluginKind][]framework.PluginIdentifier + pluginsByKind map[common.PluginKind][]framework.PluginIdentifier } // NewRegistry returns a new registry. @@ -67,7 +68,7 @@ func NewRegistry(dir string, logger logrus.FieldLogger, logLevel logrus.Level) R processFactory: newProcessFactory(), fs: filesystem.NewFileSystem(), pluginsByID: make(map[KindAndName]framework.PluginIdentifier), - pluginsByKind: make(map[framework.PluginKind][]framework.PluginIdentifier), + pluginsByKind: make(map[common.PluginKind][]framework.PluginIdentifier), } } @@ -110,13 +111,13 @@ func (r *registry) discoverPlugins(commands []string) error { // List returns info about all plugin binaries that implement the given // PluginKind. -func (r *registry) List(kind framework.PluginKind) []framework.PluginIdentifier { +func (r *registry) List(kind common.PluginKind) []framework.PluginIdentifier { return r.pluginsByKind[kind] } // Get returns info about a plugin with the given name and kind, or an // error if one cannot be found. -func (r *registry) Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) { +func (r *registry) Get(kind common.PluginKind, name string) (framework.PluginIdentifier, error) { p, found := r.pluginsByID[KindAndName{Kind: kind, Name: name}] if !found { return framework.PluginIdentifier{}, newPluginNotFoundError(kind, name) @@ -182,7 +183,7 @@ func (r *registry) listPlugins(command string) ([]framework.PluginIdentifier, er } defer process.kill() - plugin, err := process.dispense(KindAndName{Kind: framework.PluginKindPluginLister}) + plugin, err := process.dispense(KindAndName{Kind: common.PluginKindPluginLister}) if err != nil { return nil, err } @@ -203,24 +204,33 @@ func (r *registry) register(id framework.PluginIdentifier) error { } // no need to pass list of existing plugins since the check if this exists was done above - if err := framework.ValidatePluginName(id.Name, nil); err != nil { + if err := common.ValidatePluginName(id.Name, nil); err != nil { return errors.Errorf("invalid plugin name %q: %s", id.Name, err) } r.pluginsByID[key] = id r.pluginsByKind[id.Kind] = append(r.pluginsByKind[id.Kind], id) + // if id.Kind is adaptable to newer plugin versions, list it under the other versions as well + // If BackupItemAction is adaptable to BackupItemActionV2, then it would be listed under both + // kinds + if kinds, ok := common.PluginKindsAdaptableTo[id.Kind]; ok { + for _, kind := range kinds { + r.pluginsByKind[kind] = append(r.pluginsByKind[kind], id) + } + } + return nil } // pluginNotFoundError indicates a plugin could not be located for kind and name. type PluginNotFoundError struct { - kind framework.PluginKind + kind common.PluginKind name string } // newPluginNotFoundError returns a new pluginNotFoundError for kind and name. -func newPluginNotFoundError(kind framework.PluginKind, name string) *PluginNotFoundError { +func newPluginNotFoundError(kind common.PluginKind, name string) *PluginNotFoundError { return &PluginNotFoundError{ kind: kind, name: name, diff --git a/pkg/plugin/clientmgmt/restartable_delegate_test.go b/pkg/plugin/clientmgmt/restartable_delegate_test.go index c71d3e2769..a69f2e05d4 100644 --- a/pkg/plugin/clientmgmt/restartable_delegate_test.go +++ b/pkg/plugin/clientmgmt/restartable_delegate_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) type restartableDelegateTest struct { @@ -43,7 +43,7 @@ type mockable interface { func runRestartableDelegateTests( t *testing.T, - kind framework.PluginKind, + kind common.PluginKind, newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, newMock func() mockable, tests ...restartableDelegateTest, diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action.go b/pkg/plugin/clientmgmt/restartable_delete_item_action.go index 977203aae6..ead2b9b09f 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action.go @@ -20,7 +20,7 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -37,7 +37,7 @@ type restartableDeleteItemAction struct { // NewRestartableDeleteItemAction returns a new restartableDeleteItemAction. func NewRestartableDeleteItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableDeleteItemAction { r := &restartableDeleteItemAction{ - key: process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name}, + key: process.KindAndName{Kind: common.PluginKindDeleteItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go index 65fb9646ca..bd5d734a45 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go @@ -26,7 +26,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" ) @@ -60,7 +60,7 @@ func TestRestartableGetDeleteItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name} + key := process.KindAndName{Kind: common.PluginKindDeleteItemAction, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := NewRestartableDeleteItemAction(name, p) @@ -92,7 +92,7 @@ func TestRestartableDeleteItemActionGetDelegate(t *testing.T) { // Currently broken since this mocks out the restore item action interface p.On("ResetIfNeeded").Return(nil) expected := new(mocks.DeleteItemAction) - key := process.KindAndName{Kind: framework.PluginKindDeleteItemAction, Name: name} + key := process.KindAndName{Kind: common.PluginKindDeleteItemAction, Name: name} p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() @@ -116,7 +116,7 @@ func TestRestartableDeleteItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, - framework.PluginKindDeleteItemAction, + common.PluginKindDeleteItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableDeleteItemAction{ key: key, diff --git a/pkg/plugin/clientmgmt/restartable_item_snapshotter.go b/pkg/plugin/clientmgmt/restartable_item_snapshotter.go index d2743789ac..204d6ca229 100644 --- a/pkg/plugin/clientmgmt/restartable_item_snapshotter.go +++ b/pkg/plugin/clientmgmt/restartable_item_snapshotter.go @@ -22,7 +22,7 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) @@ -35,7 +35,7 @@ type restartableItemSnapshotter struct { // NewRestartableItemSnapshotter returns a new restartableItemSnapshotter. func NewRestartableItemSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *restartableItemSnapshotter { r := &restartableItemSnapshotter{ - key: process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name}, + key: process.KindAndName{Kind: common.PluginKindItemSnapshotter, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r diff --git a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go index 70d4a934ac..5aeca0551d 100644 --- a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go @@ -31,7 +31,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) @@ -65,7 +65,7 @@ func TestRestartableGetItemSnapshotter(t *testing.T) { defer p.AssertExpectations(t) name := "pvc" - key := process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindItemSnapshotter, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := NewRestartableItemSnapshotter(name, p) @@ -96,7 +96,7 @@ func TestRestartableItemSnapshotterGetDelegate(t *testing.T) { // Happy path p.On("ResetIfNeeded").Return(nil) expected := new(mocks.ItemSnapshotter) - key := process.KindAndName{Kind: framework.PluginKindItemSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindItemSnapshotter, Name: name} p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() @@ -177,7 +177,7 @@ func TestRestartableItemSnasphotterDelegatedFunctions(t *testing.T) { } runRestartableDelegateTests( t, - framework.PluginKindItemSnapshotter, + common.PluginKindItemSnapshotter, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableItemSnapshotter{ key: key, diff --git a/pkg/plugin/clientmgmt/restartable_object_store.go b/pkg/plugin/clientmgmt/restartable_object_store.go index 5be1113408..a3552bd0ab 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store.go +++ b/pkg/plugin/clientmgmt/restartable_object_store.go @@ -23,7 +23,7 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -41,7 +41,7 @@ type restartableObjectStore struct { // NewRestartableObjectStore returns a new restartableObjectStore. func NewRestartableObjectStore(name string, sharedPluginProcess process.RestartableProcess) *restartableObjectStore { - key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + key := process.KindAndName{Kind: common.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: sharedPluginProcess, diff --git a/pkg/plugin/clientmgmt/restartable_object_store_test.go b/pkg/plugin/clientmgmt/restartable_object_store_test.go index b4e94fccc6..47699c50bc 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store_test.go +++ b/pkg/plugin/clientmgmt/restartable_object_store_test.go @@ -27,7 +27,7 @@ import ( "github.com/stretchr/testify/require" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" ) @@ -61,7 +61,7 @@ func TestRestartableGetObjectStore(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + key := process.KindAndName{Kind: common.PluginKindObjectStore, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := &restartableObjectStore{ @@ -86,7 +86,7 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + key := process.KindAndName{Kind: common.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, @@ -119,7 +119,7 @@ func TestRestartableObjectStoreGetDelegate(t *testing.T) { // Reset error p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "aws" - key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + key := process.KindAndName{Kind: common.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, @@ -147,7 +147,7 @@ func TestRestartableObjectStoreInit(t *testing.T) { // getObjectStore error name := "aws" - key := process.KindAndName{Kind: framework.PluginKindObjectStore, Name: name} + key := process.KindAndName{Kind: common.PluginKindObjectStore, Name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: p, @@ -187,7 +187,7 @@ func TestRestartableObjectStoreInit(t *testing.T) { func TestRestartableObjectStoreDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, - framework.PluginKindObjectStore, + common.PluginKindObjectStore, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableObjectStore{ key: key, diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action.go b/pkg/plugin/clientmgmt/restartable_restore_item_action.go index c4e6911b41..fdd266d0bf 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_restore_item_action.go @@ -20,7 +20,7 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -37,7 +37,7 @@ type restartableRestoreItemAction struct { // NewRestartableRestoreItemAction returns a new RestartableRestoreItemAction. func NewRestartableRestoreItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableRestoreItemAction { r := &restartableRestoreItemAction{ - key: process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name}, + key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, sharedPluginProcess: sharedPluginProcess, } return r diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go b/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go index 3d4fd52e99..b91d08bfa6 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go +++ b/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go @@ -26,7 +26,7 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/restore/mocks" ) @@ -60,7 +60,7 @@ func TestRestartableGetRestoreItemAction(t *testing.T) { defer p.AssertExpectations(t) name := "pod" - key := process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name} + key := process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := NewRestartableRestoreItemAction(name, p) @@ -91,7 +91,7 @@ func TestRestartableRestoreItemActionGetDelegate(t *testing.T) { // Happy path p.On("ResetIfNeeded").Return(nil) expected := new(mocks.ItemAction) - key := process.KindAndName{Kind: framework.PluginKindRestoreItemAction, Name: name} + key := process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name} p.On("GetByKindAndName", key).Return(expected, nil) a, err = r.getDelegate() @@ -122,7 +122,7 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, - framework.PluginKindRestoreItemAction, + common.PluginKindRestoreItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableRestoreItemAction{ key: key, diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go index f8a1a66b02..1252306ea3 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go @@ -21,7 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -35,9 +35,9 @@ type restartableVolumeSnapshotter struct { config map[string]string } -// NewRestartableVolumeSnapshotter returns a new RestartableVolumeSnapshotter. +// NewRestartableVolumeSnapshotter returns a new restartableVolumeSnapshotter. func NewRestartableVolumeSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *restartableVolumeSnapshotter { - key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: sharedPluginProcess, diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go index 8d198ec96d..4f8c06f8c6 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" ) @@ -60,7 +60,7 @@ func TestRestartableGetVolumeSnapshotter(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) r := &restartableVolumeSnapshotter{ @@ -85,7 +85,7 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { defer p.AssertExpectations(t) name := "aws" - key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, @@ -118,7 +118,7 @@ func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { // Reset error p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "aws" - key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, @@ -146,7 +146,7 @@ func TestRestartableVolumeSnapshotterInit(t *testing.T) { // getVolumeSnapshottererror name := "aws" - key := process.KindAndName{Kind: framework.PluginKindVolumeSnapshotter, Name: name} + key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: p, @@ -198,7 +198,7 @@ func TestRestartableVolumeSnapshotterDelegatedFunctions(t *testing.T) { runRestartableDelegateTests( t, - framework.PluginKindVolumeSnapshotter, + common.PluginKindVolumeSnapshotter, func(key process.KindAndName, p process.RestartableProcess) interface{} { return &restartableVolumeSnapshotter{ key: key, diff --git a/pkg/plugin/framework/action_resolver.go b/pkg/plugin/framework/action_resolver.go index 345061aacb..b1d692fd36 100644 --- a/pkg/plugin/framework/action_resolver.go +++ b/pkg/plugin/framework/action_resolver.go @@ -127,14 +127,14 @@ func NewItemSnapshotterResolver(actions []isv1.ItemSnapshotter) ItemSnapshotterR } type ActionResolver interface { - ResolveAction(helper discovery.Helper, action velero.Applicable) (ResolvedAction, error) + ResolveAction(helper discovery.Helper, action velero.Applicable, log logrus.FieldLogger) (ResolvedAction, error) } type BackupItemActionResolver struct { actions []biav1.BackupItemAction } -func (recv BackupItemActionResolver) ResolveActions(helper discovery.Helper) ([]BackupItemResolvedAction, error) { +func (recv BackupItemActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]BackupItemResolvedAction, error) { var resolved []BackupItemResolvedAction for _, action := range recv.actions { resources, namespaces, selector, err := resolveAction(helper, action) @@ -163,7 +163,7 @@ type RestoreItemActionResolver struct { actions []velero.RestoreItemAction } -func (recv RestoreItemActionResolver) ResolveActions(helper discovery.Helper) ([]RestoreItemResolvedAction, error) { +func (recv RestoreItemActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]RestoreItemResolvedAction, error) { var resolved []RestoreItemResolvedAction for _, action := range recv.actions { resources, namespaces, selector, err := resolveAction(helper, action) @@ -192,7 +192,7 @@ type DeleteItemActionResolver struct { actions []velero.DeleteItemAction } -func (recv DeleteItemActionResolver) ResolveActions(helper discovery.Helper) ([]DeleteItemResolvedAction, error) { +func (recv DeleteItemActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]DeleteItemResolvedAction, error) { var resolved []DeleteItemResolvedAction for _, action := range recv.actions { resources, namespaces, selector, err := resolveAction(helper, action) @@ -221,7 +221,7 @@ type ItemSnapshotterResolver struct { actions []isv1.ItemSnapshotter } -func (recv ItemSnapshotterResolver) ResolveActions(helper discovery.Helper) ([]ItemSnapshotterResolvedAction, error) { +func (recv ItemSnapshotterResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]ItemSnapshotterResolvedAction, error) { var resolved []ItemSnapshotterResolvedAction for _, action := range recv.actions { resources, namespaces, selector, err := resolveAction(helper, action) diff --git a/pkg/plugin/framework/backup_item_action.go b/pkg/plugin/framework/backup_item_action.go index 8780c78264..8f6bc7554a 100644 --- a/pkg/plugin/framework/backup_item_action.go +++ b/pkg/plugin/framework/backup_item_action.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,16 +30,16 @@ import ( // interface. type BackupItemActionPlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns a clientDispenser for BackupItemAction gRPC clients. func (p *BackupItemActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newBackupItemActionGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newBackupItemActionGRPCClient), nil } // GRPCServer registers a BackupItemAction gRPC server. func (p *BackupItemActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - protobiav1.RegisterBackupItemActionServer(server, &BackupItemActionGRPCServer{mux: p.serverMux}) + protobiav1.RegisterBackupItemActionServer(server, &BackupItemActionGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/backup_item_action_client.go b/pkg/plugin/framework/backup_item_action_client.go index 4756ab3920..50101975f9 100644 --- a/pkg/plugin/framework/backup_item_action_client.go +++ b/pkg/plugin/framework/backup_item_action_client.go @@ -27,39 +27,40 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // NewBackupItemActionPlugin constructs a BackupItemActionPlugin. -func NewBackupItemActionPlugin(options ...PluginOption) *BackupItemActionPlugin { +func NewBackupItemActionPlugin(options ...common.PluginOption) *BackupItemActionPlugin { return &BackupItemActionPlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } // BackupItemActionGRPCClient implements the backup/ItemAction interface and uses a // gRPC client to make calls to the plugin server. type BackupItemActionGRPCClient struct { - *clientBase + *common.ClientBase grpcClient protobiav1.BackupItemActionClient } -func newBackupItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newBackupItemActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &BackupItemActionGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: protobiav1.NewBackupItemActionClient(clientConn), } } func (c *BackupItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { req := &protobiav1.BackupItemActionAppliesToRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, } res, err := c.grpcClient.AppliesTo(context.Background(), req) if err != nil { - return velero.ResourceSelector{}, fromGRPCError(err) + return velero.ResourceSelector{}, common.FromGRPCError(err) } if res.ResourceSelector == nil { @@ -87,14 +88,14 @@ func (c *BackupItemActionGRPCClient) Execute(item runtime.Unstructured, backup * } req := &protobiav1.ExecuteRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Item: itemJSON, Backup: backupJSON, } res, err := c.grpcClient.Execute(context.Background(), req) if err != nil { - return nil, nil, fromGRPCError(err) + return nil, nil, common.FromGRPCError(err) } var updatedItem unstructured.Unstructured diff --git a/pkg/plugin/framework/backup_item_action_server.go b/pkg/plugin/framework/backup_item_action_server.go index fec8f07008..bde6c2148a 100644 --- a/pkg/plugin/framework/backup_item_action_server.go +++ b/pkg/plugin/framework/backup_item_action_server.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" @@ -33,11 +34,11 @@ import ( // BackupItemActionGRPCServer implements the proto-generated BackupItemAction interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type BackupItemActionGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (s *BackupItemActionGRPCServer) getImpl(name string) (biav1.BackupItemAction, error) { - impl, err := s.mux.getHandler(name) + impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } @@ -54,19 +55,19 @@ func (s *BackupItemActionGRPCServer) AppliesTo( ctx context.Context, req *protobiav1.BackupItemActionAppliesToRequest) ( response *protobiav1.BackupItemActionAppliesToResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } resourceSelector, err := impl.AppliesTo() if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &protobiav1.BackupItemActionAppliesToResponse{ @@ -83,29 +84,29 @@ func (s *BackupItemActionGRPCServer) AppliesTo( func (s *BackupItemActionGRPCServer) Execute( ctx context.Context, req *protobiav1.ExecuteRequest) (response *protobiav1.ExecuteResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var item unstructured.Unstructured var backup api.Backup if err := json.Unmarshal(req.Item, &item); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := json.Unmarshal(req.Backup, &backup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } updatedItem, additionalItems, err := impl.Execute(&item, &backup) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } // If the plugin implementation returned a nil updatedItem (meaning no modifications), reset updatedItem to the @@ -116,7 +117,7 @@ func (s *BackupItemActionGRPCServer) Execute( } else { updatedItemJSON, err = json.Marshal(updatedItem.UnstructuredContent()) if err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } } diff --git a/pkg/plugin/framework/backup_item_action_test.go b/pkg/plugin/framework/backup_item_action_test.go index 32d7ea5515..a88aaf853b 100644 --- a/pkg/plugin/framework/backup_item_action_test.go +++ b/pkg/plugin/framework/backup_item_action_test.go @@ -29,10 +29,11 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/backup/mocks" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" protobiav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/backupitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -147,16 +148,16 @@ func TestBackupItemActionGRPCServerExecute(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - itemAction := &mocks.ItemAction{} + itemAction := &mocks.BackupItemAction{} defer itemAction.AssertExpectations(t) if !test.skipMock { itemAction.On("Execute", &validItemObject, &validBackupObject).Return(test.implUpdatedItem, test.implAdditionalItems, test.implError) } - s := &BackupItemActionGRPCServer{mux: &serverMux{ - serverLog: velerotest.NewLogger(), - handlers: map[string]interface{}{ + s := &BackupItemActionGRPCServer{mux: &common.ServerMux{ + ServerLog: velerotest.NewLogger(), + Handlers: map[string]interface{}{ "xyz": itemAction, }, }} diff --git a/pkg/plugin/framework/client_dispenser.go b/pkg/plugin/framework/common/client_dispenser.go similarity index 86% rename from pkg/plugin/framework/client_dispenser.go rename to pkg/plugin/framework/common/client_dispenser.go index 83e934d725..1abb11c2df 100644 --- a/pkg/plugin/framework/client_dispenser.go +++ b/pkg/plugin/framework/common/client_dispenser.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "github.com/sirupsen/logrus" "google.golang.org/grpc" ) -// clientBase implements client and contains shared fields common to all clients. -type clientBase struct { - plugin string - logger logrus.FieldLogger +// ClientBase implements client and contains shared fields common to all clients. +type ClientBase struct { + Plugin string + Logger logrus.FieldLogger } type ClientDispenser interface { @@ -44,10 +44,10 @@ type clientDispenser struct { clients map[string]interface{} } -type clientInitFunc func(base *clientBase, clientConn *grpc.ClientConn) interface{} +type clientInitFunc func(base *ClientBase, clientConn *grpc.ClientConn) interface{} // newClientDispenser creates a new clientDispenser. -func newClientDispenser(logger logrus.FieldLogger, clientConn *grpc.ClientConn, initFunc clientInitFunc) *clientDispenser { +func NewClientDispenser(logger logrus.FieldLogger, clientConn *grpc.ClientConn, initFunc clientInitFunc) *clientDispenser { return &clientDispenser{ clientConn: clientConn, logger: logger, @@ -63,9 +63,9 @@ func (cd *clientDispenser) ClientFor(name string) interface{} { return client } - base := &clientBase{ - plugin: name, - logger: cd.logger, + base := &ClientBase{ + Plugin: name, + Logger: cd.logger, } // Initialize the plugin (e.g. newBackupItemActionGRPCClient()) client := cd.initFunc(base, cd.clientConn) diff --git a/pkg/plugin/framework/client_dispenser_test.go b/pkg/plugin/framework/common/client_dispenser_test.go similarity index 83% rename from pkg/plugin/framework/client_dispenser_test.go rename to pkg/plugin/framework/common/client_dispenser_test.go index 9ec5266691..bd1461658a 100644 --- a/pkg/plugin/framework/client_dispenser_test.go +++ b/pkg/plugin/framework/common/client_dispenser_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "testing" @@ -26,7 +26,7 @@ import ( ) type fakeClient struct { - base *clientBase + base *ClientBase clientConn *grpc.ClientConn } @@ -36,11 +36,11 @@ func TestNewClientDispenser(t *testing.T) { clientConn := new(grpc.ClientConn) c := 3 - initFunc := func(base *clientBase, clientConn *grpc.ClientConn) interface{} { + initFunc := func(base *ClientBase, clientConn *grpc.ClientConn) interface{} { return c } - cd := newClientDispenser(logger, clientConn, initFunc) + cd := NewClientDispenser(logger, clientConn, initFunc) assert.Equal(t, clientConn, cd.clientConn) assert.NotNil(t, cd.clients) assert.Empty(t, cd.clients) @@ -52,23 +52,23 @@ func TestClientFor(t *testing.T) { c := new(fakeClient) count := 0 - initFunc := func(base *clientBase, clientConn *grpc.ClientConn) interface{} { + initFunc := func(base *ClientBase, clientConn *grpc.ClientConn) interface{} { c.base = base c.clientConn = clientConn count++ return c } - cd := newClientDispenser(logger, clientConn, initFunc) + cd := NewClientDispenser(logger, clientConn, initFunc) actual := cd.ClientFor("pod") require.IsType(t, &fakeClient{}, actual) typed := actual.(*fakeClient) assert.Equal(t, 1, count) assert.Equal(t, &typed, &c) - expectedBase := &clientBase{ - plugin: "pod", - logger: logger, + expectedBase := &ClientBase{ + Plugin: "pod", + Logger: logger, } assert.Equal(t, expectedBase, typed.base) assert.Equal(t, clientConn, typed.clientConn) diff --git a/pkg/plugin/framework/client_errors.go b/pkg/plugin/framework/common/client_errors.go similarity index 84% rename from pkg/plugin/framework/client_errors.go rename to pkg/plugin/framework/common/client_errors.go index adad199dbe..377851aa8f 100644 --- a/pkg/plugin/framework/client_errors.go +++ b/pkg/plugin/framework/common/client_errors.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "google.golang.org/grpc/status" @@ -22,7 +22,7 @@ import ( proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) -// fromGRPCError takes a gRPC status error, extracts a stack trace +// FromGRPCError takes a gRPC status error, extracts a stack trace // from the details if it exists, and returns an error that can // provide information about where it was created. // @@ -30,7 +30,7 @@ import ( // all errors returned from the plugin server before they're passed back to // the rest of the Velero codebase. This will enable them to display location // information when they're logged. -func fromGRPCError(err error) error { +func FromGRPCError(err error) error { statusErr, ok := status.FromError(err) if !ok { return statusErr.Err() @@ -39,7 +39,7 @@ func fromGRPCError(err error) error { for _, detail := range statusErr.Details() { switch t := detail.(type) { case *proto.Stack: - return &protoStackError{ + return &ProtoStackError{ error: err, stack: t, } @@ -49,12 +49,12 @@ func fromGRPCError(err error) error { return err } -type protoStackError struct { +type ProtoStackError struct { error stack *proto.Stack } -func (e *protoStackError) File() string { +func (e *ProtoStackError) File() string { if e.stack == nil || len(e.stack.Frames) < 1 { return "" } @@ -62,7 +62,7 @@ func (e *protoStackError) File() string { return e.stack.Frames[0].File } -func (e *protoStackError) Line() int32 { +func (e *ProtoStackError) Line() int32 { if e.stack == nil || len(e.stack.Frames) < 1 { return 0 } @@ -70,7 +70,7 @@ func (e *protoStackError) Line() int32 { return e.stack.Frames[0].Line } -func (e *protoStackError) Function() string { +func (e *ProtoStackError) Function() string { if e.stack == nil || len(e.stack.Frames) < 1 { return "" } diff --git a/pkg/plugin/framework/handle_panic.go b/pkg/plugin/framework/common/handle_panic.go similarity index 84% rename from pkg/plugin/framework/handle_panic.go rename to pkg/plugin/framework/common/handle_panic.go index 4ea0ec2b52..e4324898dd 100644 --- a/pkg/plugin/framework/handle_panic.go +++ b/pkg/plugin/framework/common/handle_panic.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "runtime/debug" @@ -23,8 +23,8 @@ import ( "google.golang.org/grpc/codes" ) -// handlePanic is a panic handler for the server half of velero plugins. -func handlePanic(p interface{}) error { +// HandlePanic is a panic handler for the server half of velero plugins. +func HandlePanic(p interface{}) error { if p == nil { return nil } @@ -37,7 +37,7 @@ func handlePanic(p interface{}) error { if panicErr, ok := p.(error); !ok { err = errors.Errorf("plugin panicked: %v", p) } else { - if _, ok := panicErr.(stackTracer); ok { + if _, ok := panicErr.(StackTracer); ok { err = panicErr } else { errWithStacktrace := errors.Errorf("%v, stack trace: %s", panicErr, debug.Stack()) @@ -45,5 +45,5 @@ func handlePanic(p interface{}) error { } } - return newGRPCErrorWithCode(err, codes.Aborted) + return NewGRPCErrorWithCode(err, codes.Aborted) } diff --git a/pkg/plugin/framework/plugin_base.go b/pkg/plugin/framework/common/plugin_base.go similarity index 65% rename from pkg/plugin/framework/plugin_base.go rename to pkg/plugin/framework/common/plugin_base.go index 4a97fa47b9..12e444b749 100644 --- a/pkg/plugin/framework/plugin_base.go +++ b/pkg/plugin/framework/common/plugin_base.go @@ -14,35 +14,35 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "github.com/sirupsen/logrus" ) -type pluginBase struct { - clientLogger logrus.FieldLogger - *serverMux +type PluginBase struct { + ClientLogger logrus.FieldLogger + *ServerMux } -func newPluginBase(options ...PluginOption) *pluginBase { - base := new(pluginBase) +func NewPluginBase(options ...PluginOption) *PluginBase { + base := new(PluginBase) for _, option := range options { option(base) } return base } -type PluginOption func(base *pluginBase) +type PluginOption func(base *PluginBase) func ClientLogger(logger logrus.FieldLogger) PluginOption { - return func(base *pluginBase) { - base.clientLogger = logger + return func(base *PluginBase) { + base.ClientLogger = logger } } -func serverLogger(logger logrus.FieldLogger) PluginOption { - return func(base *pluginBase) { - base.serverMux = newServerMux(logger) +func ServerLogger(logger logrus.FieldLogger) PluginOption { + return func(base *PluginBase) { + base.ServerMux = NewServerMux(logger) } } diff --git a/pkg/plugin/framework/plugin_base_test.go b/pkg/plugin/framework/common/plugin_base_test.go similarity index 84% rename from pkg/plugin/framework/plugin_base_test.go rename to pkg/plugin/framework/common/plugin_base_test.go index 3d25732586..04e066d0c6 100644 --- a/pkg/plugin/framework/plugin_base_test.go +++ b/pkg/plugin/framework/common/plugin_base_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "testing" @@ -24,17 +24,17 @@ import ( ) func TestClientLogger(t *testing.T) { - base := &pluginBase{} + base := &PluginBase{} logger := test.NewLogger() f := ClientLogger(logger) f(base) - assert.Equal(t, logger, base.clientLogger) + assert.Equal(t, logger, base.ClientLogger) } func TestServerLogger(t *testing.T) { - base := &pluginBase{} + base := &PluginBase{} logger := test.NewLogger() - f := serverLogger(logger) + f := ServerLogger(logger) f(base) - assert.Equal(t, newServerMux(logger), base.serverMux) + assert.Equal(t, NewServerMux(logger), base.ServerMux) } diff --git a/pkg/plugin/framework/plugin_kinds.go b/pkg/plugin/framework/common/plugin_kinds.go similarity index 89% rename from pkg/plugin/framework/plugin_kinds.go rename to pkg/plugin/framework/common/plugin_kinds.go index f48f7f4983..2e0119e2fd 100644 --- a/pkg/plugin/framework/plugin_kinds.go +++ b/pkg/plugin/framework/common/plugin_kinds.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common // PluginKind is a type alias for a string that describes // the kind of a Velero-supported plugin. @@ -48,6 +48,11 @@ const ( PluginKindPluginLister PluginKind = "PluginLister" ) +// If there are plugin kinds that are adaptable to newer API versions, list them here. +// The older (adaptable) version is the key, and the value is the full list of newer +// plugin kinds that are capable of adapting it. +var PluginKindsAdaptableTo = map[PluginKind][]PluginKind{} + // AllPluginKinds contains all the valid plugin kinds that Velero supports, excluding PluginLister because that is not a // kind that a developer would ever need to implement (it's handled by Velero and the Velero plugin library code). func AllPluginKinds() map[string]PluginKind { diff --git a/pkg/plugin/framework/server_errors.go b/pkg/plugin/framework/common/server_errors.go similarity index 78% rename from pkg/plugin/framework/server_errors.go rename to pkg/plugin/framework/common/server_errors.go index 55f8859fde..7763d4e9ec 100644 --- a/pkg/plugin/framework/server_errors.go +++ b/pkg/plugin/framework/common/server_errors.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( goproto "github.com/golang/protobuf/proto" @@ -26,13 +26,13 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" ) -// newGRPCErrorWithCode wraps err in a gRPC status error with the error's stack trace +// NewGRPCErrorWithCode wraps err in a gRPC status error with the error's stack trace // included in the details if it exists. This provides an easy way to send // stack traces from plugin servers across the wire to the plugin client. // // This function should be used in the internal plugin server code to wrap // all errors before they're returned. -func newGRPCErrorWithCode(err error, code codes.Code, details ...goproto.Message) error { +func NewGRPCErrorWithCode(err error, code codes.Code, details ...goproto.Message) error { // if it's already a gRPC status error, use it; otherwise, create a new one statusErr, ok := status.FromError(err) if !ok { @@ -40,7 +40,7 @@ func newGRPCErrorWithCode(err error, code codes.Code, details ...goproto.Message } // get a Stack for the error and add it to details - if stack := errorStack(err); stack != nil { + if stack := ErrorStack(err); stack != nil { details = append(details, stack) } @@ -52,16 +52,16 @@ func newGRPCErrorWithCode(err error, code codes.Code, details ...goproto.Message return statusErr.Err() } -// newGRPCError is a convenience function for creating a new gRPC error +// NewGRPCError is a convenience function for creating a new gRPC error // with code = codes.Unknown -func newGRPCError(err error, details ...goproto.Message) error { - return newGRPCErrorWithCode(err, codes.Unknown, details...) +func NewGRPCError(err error, details ...goproto.Message) error { + return NewGRPCErrorWithCode(err, codes.Unknown, details...) } -// errorStack gets a stack trace, if it exists, from the provided error, and +// ErrorStack gets a stack trace, if it exists, from the provided error, and // returns it as a *proto.Stack. -func errorStack(err error) *proto.Stack { - stackTracer, ok := err.(stackTracer) +func ErrorStack(err error) *proto.Stack { + stackTracer, ok := err.(StackTracer) if !ok { return nil } @@ -80,6 +80,6 @@ func errorStack(err error) *proto.Stack { return stackTrace } -type stackTracer interface { +type StackTracer interface { StackTrace() errors.StackTrace } diff --git a/pkg/plugin/framework/server_mux.go b/pkg/plugin/framework/common/server_mux.go similarity index 78% rename from pkg/plugin/framework/server_mux.go rename to pkg/plugin/framework/common/server_mux.go index 4babea3841..960975c7e6 100644 --- a/pkg/plugin/framework/server_mux.go +++ b/pkg/plugin/framework/common/server_mux.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "strings" @@ -29,42 +29,42 @@ import ( // (ObjectStore, VolumeSnapshotter, BackupItemAction, RestoreItemAction). type HandlerInitializer func(logger logrus.FieldLogger) (interface{}, error) -// serverMux manages multiple implementations of a single plugin kind, such as pod and pvc BackupItemActions. -type serverMux struct { +// ServerMux manages multiple implementations of a single plugin kind, such as pod and pvc BackupItemActions. +type ServerMux struct { kind PluginKind initializers map[string]HandlerInitializer - handlers map[string]interface{} - serverLog logrus.FieldLogger + Handlers map[string]interface{} + ServerLog logrus.FieldLogger } -// newServerMux returns a new serverMux. -func newServerMux(logger logrus.FieldLogger) *serverMux { - return &serverMux{ +// NewServerMux returns a new ServerMux. +func NewServerMux(logger logrus.FieldLogger) *ServerMux { + return &ServerMux{ initializers: make(map[string]HandlerInitializer), - handlers: make(map[string]interface{}), - serverLog: logger, + Handlers: make(map[string]interface{}), + ServerLog: logger, } } // register validates the plugin name and registers the // initializer for the given name. -func (m *serverMux) register(name string, f HandlerInitializer) { - if err := ValidatePluginName(name, m.names()); err != nil { - m.serverLog.Errorf("invalid plugin name %q: %s", name, err) +func (m *ServerMux) Register(name string, f HandlerInitializer) { + if err := ValidatePluginName(name, m.Names()); err != nil { + m.ServerLog.Errorf("invalid plugin name %q: %s", name, err) return } m.initializers[name] = f } // names returns a list of all registered implementations. -func (m *serverMux) names() []string { +func (m *ServerMux) Names() []string { return sets.StringKeySet(m.initializers).List() } -// getHandler returns the instance for a plugin with the given name. If an instance has already been initialized, +// GetHandler returns the instance for a plugin with the given name. If an instance has already been initialized, // that is returned. Otherwise, the instance is initialized by calling its initialization function. -func (m *serverMux) getHandler(name string) (interface{}, error) { - if instance, found := m.handlers[name]; found { +func (m *ServerMux) GetHandler(name string) (interface{}, error) { + if instance, found := m.Handlers[name]; found { return instance, nil } @@ -73,14 +73,14 @@ func (m *serverMux) getHandler(name string) (interface{}, error) { return nil, errors.Errorf("%v plugin: %s was not found or has an invalid name format", m.kind, name) } - instance, err := initializer(m.serverLog) + instance, err := initializer(m.ServerLog) if err != nil { return nil, err } - m.handlers[name] = instance + m.Handlers[name] = instance - return m.handlers[name], nil + return m.Handlers[name], nil } // ValidatePluginName checks if the given name: diff --git a/pkg/plugin/framework/server_mux_test.go b/pkg/plugin/framework/common/server_mux_test.go similarity index 99% rename from pkg/plugin/framework/server_mux_test.go rename to pkg/plugin/framework/common/server_mux_test.go index 1a9be9a68b..1e84555f3a 100644 --- a/pkg/plugin/framework/server_mux_test.go +++ b/pkg/plugin/framework/common/server_mux_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package common import ( "strings" diff --git a/pkg/plugin/framework/delete_item_action.go b/pkg/plugin/framework/delete_item_action.go index fc9be502c7..7d70938ca7 100644 --- a/pkg/plugin/framework/delete_item_action.go +++ b/pkg/plugin/framework/delete_item_action.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,16 +30,16 @@ import ( // interface. type DeleteItemActionPlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns a RestoreItemAction gRPC client. func (p *DeleteItemActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newDeleteItemActionGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newDeleteItemActionGRPCClient), nil } // GRPCServer registers a DeleteItemAction gRPC server. func (p *DeleteItemActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterDeleteItemActionServer(server, &DeleteItemActionGRPCServer{mux: p.serverMux}) + proto.RegisterDeleteItemActionServer(server, &DeleteItemActionGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/delete_item_action_client.go b/pkg/plugin/framework/delete_item_action_client.go index e9adae6d01..4988d5f085 100644 --- a/pkg/plugin/framework/delete_item_action_client.go +++ b/pkg/plugin/framework/delete_item_action_client.go @@ -23,6 +23,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -30,30 +31,30 @@ import ( var _ velero.DeleteItemAction = &DeleteItemActionGRPCClient{} // NewDeleteItemActionPlugin constructs a DeleteItemActionPlugin. -func NewDeleteItemActionPlugin(options ...PluginOption) *DeleteItemActionPlugin { +func NewDeleteItemActionPlugin(options ...common.PluginOption) *DeleteItemActionPlugin { return &DeleteItemActionPlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } // DeleteItemActionGRPCClient implements the backup/ItemAction interface and uses a // gRPC client to make calls to the plugin server. type DeleteItemActionGRPCClient struct { - *clientBase + *common.ClientBase grpcClient proto.DeleteItemActionClient } -func newDeleteItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newDeleteItemActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &DeleteItemActionGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: proto.NewDeleteItemActionClient(clientConn), } } func (c *DeleteItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { - res, err := c.grpcClient.AppliesTo(context.Background(), &proto.DeleteItemActionAppliesToRequest{Plugin: c.plugin}) + res, err := c.grpcClient.AppliesTo(context.Background(), &proto.DeleteItemActionAppliesToRequest{Plugin: c.Plugin}) if err != nil { - return velero.ResourceSelector{}, fromGRPCError(err) + return velero.ResourceSelector{}, common.FromGRPCError(err) } if res.ResourceSelector == nil { @@ -81,14 +82,14 @@ func (c *DeleteItemActionGRPCClient) Execute(input *velero.DeleteItemActionExecu } req := &proto.DeleteItemActionExecuteRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Item: itemJSON, Backup: backupJSON, } // First return item is just an empty struct no matter what. if _, err = c.grpcClient.Execute(context.Background(), req); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil diff --git a/pkg/plugin/framework/delete_item_action_server.go b/pkg/plugin/framework/delete_item_action_server.go index d9d3a7dc70..e298969d13 100644 --- a/pkg/plugin/framework/delete_item_action_server.go +++ b/pkg/plugin/framework/delete_item_action_server.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -31,11 +32,11 @@ import ( // DeleteItemActionGRPCServer implements the proto-generated DeleteItemActionServer interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type DeleteItemActionGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (s *DeleteItemActionGRPCServer) getImpl(name string) (velero.DeleteItemAction, error) { - impl, err := s.mux.getHandler(name) + impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } @@ -50,19 +51,19 @@ func (s *DeleteItemActionGRPCServer) getImpl(name string) (velero.DeleteItemActi func (s *DeleteItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.DeleteItemActionAppliesToRequest) (response *proto.DeleteItemActionAppliesToResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } resourceSelector, err := impl.AppliesTo() if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.DeleteItemActionAppliesToResponse{ @@ -78,14 +79,14 @@ func (s *DeleteItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.D func (s *DeleteItemActionGRPCServer) Execute(ctx context.Context, req *proto.DeleteItemActionExecuteRequest) (_ *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var ( @@ -94,18 +95,18 @@ func (s *DeleteItemActionGRPCServer) Execute(ctx context.Context, req *proto.Del ) if err := json.Unmarshal(req.Item, &item); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err = json.Unmarshal(req.Backup, &backup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := impl.Execute(&velero.DeleteItemActionExecuteInput{ Item: &item, Backup: &backup, }); err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil diff --git a/pkg/plugin/framework/interface.go b/pkg/plugin/framework/interface.go index 1394e8e826..4eeb3049b5 100644 --- a/pkg/plugin/framework/interface.go +++ b/pkg/plugin/framework/interface.go @@ -24,5 +24,5 @@ type Interface interface { // names returns a list of all the registered implementations for this plugin (such as "pod" and "pvc" for // BackupItemAction). - names() []string + Names() []string } diff --git a/pkg/plugin/framework/item_snapshotter.go b/pkg/plugin/framework/item_snapshotter.go index 6e6f91439f..f9af50e68c 100644 --- a/pkg/plugin/framework/item_snapshotter.go +++ b/pkg/plugin/framework/item_snapshotter.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,16 +30,16 @@ import ( // interface. type ItemSnapshotterPlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns a clientDispenser for ItemSnapshotter gRPC clients. func (p *ItemSnapshotterPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newItemSnapshotterGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newItemSnapshotterGRPCClient), nil } // GRPCServer registers an ItemSnapshotter gRPC server. func (p *ItemSnapshotterPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterItemSnapshotterServer(server, &ItemSnapshotterGRPCServer{mux: p.serverMux}) + proto.RegisterItemSnapshotterServer(server, &ItemSnapshotterGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/item_snapshotter_client.go b/pkg/plugin/framework/item_snapshotter_client.go index 23786046a3..323c3541b3 100644 --- a/pkg/plugin/framework/item_snapshotter_client.go +++ b/pkg/plugin/framework/item_snapshotter_client.go @@ -25,21 +25,22 @@ import ( "google.golang.org/grpc" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) // NewItemSnapshotterPlugin constructs a ItemSnapshotterPlugin. -func NewItemSnapshotterPlugin(options ...PluginOption) *ItemSnapshotterPlugin { +func NewItemSnapshotterPlugin(options ...common.PluginOption) *ItemSnapshotterPlugin { return &ItemSnapshotterPlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } -func newItemSnapshotterGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newItemSnapshotterGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &ItemSnapshotterGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: proto.NewItemSnapshotterClient(clientConn), } } @@ -47,13 +48,13 @@ func newItemSnapshotterGRPCClient(base *clientBase, clientConn *grpc.ClientConn) // ItemSnapshotterGRPCClient implements the ItemSnapshotter interface and uses a // gRPC client to make calls to the plugin server. type ItemSnapshotterGRPCClient struct { - *clientBase + *common.ClientBase grpcClient proto.ItemSnapshotterClient } func (recv ItemSnapshotterGRPCClient) Init(config map[string]string) error { req := &proto.ItemSnapshotterInitRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, Config: config, } @@ -63,12 +64,12 @@ func (recv ItemSnapshotterGRPCClient) Init(config map[string]string) error { func (recv ItemSnapshotterGRPCClient) AppliesTo() (velero.ResourceSelector, error) { req := &proto.ItemSnapshotterAppliesToRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, } res, err := recv.grpcClient.AppliesTo(context.Background(), req) if err != nil { - return velero.ResourceSelector{}, fromGRPCError(err) + return velero.ResourceSelector{}, common.FromGRPCError(err) } if res.ResourceSelector == nil { @@ -95,7 +96,7 @@ func (recv ItemSnapshotterGRPCClient) AlsoHandles(input *isv1.AlsoHandlesInput) return nil, errors.WithStack(err) } req := &proto.AlsoHandlesRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, Item: itemJSON, Backup: backupJSON, } @@ -120,7 +121,7 @@ func (recv ItemSnapshotterGRPCClient) SnapshotItem(ctx context.Context, input *i return nil, errors.WithStack(err) } req := &proto.SnapshotItemRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, Item: itemJSON, Backup: backupJSON, } @@ -152,7 +153,7 @@ func (recv ItemSnapshotterGRPCClient) Progress(input *isv1.ProgressInput) (*isv1 return nil, errors.WithStack(err) } req := &proto.ProgressRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, ItemID: resourceIdentifierToProto(input.ItemID), SnapshotID: input.SnapshotID, Backup: backupJSON, @@ -183,7 +184,7 @@ func (recv ItemSnapshotterGRPCClient) Progress(input *isv1.ProgressInput) (*isv1 func (recv ItemSnapshotterGRPCClient) DeleteSnapshot(ctx context.Context, input *isv1.DeleteSnapshotInput) error { req := &proto.DeleteItemSnapshotRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, Params: input.Params, SnapshotID: input.SnapshotID, } @@ -209,7 +210,7 @@ func (recv ItemSnapshotterGRPCClient) CreateItemFromSnapshot(ctx context.Context return nil, errors.WithStack(err) } req := &proto.CreateItemFromSnapshotRequest{ - Plugin: recv.plugin, + Plugin: recv.Plugin, Item: itemJSON, SnapshotID: input.SnapshotID, ItemFromBackup: itemFromBackupJSON, diff --git a/pkg/plugin/framework/item_snapshotter_server.go b/pkg/plugin/framework/item_snapshotter_server.go index d746947577..21ab92110f 100644 --- a/pkg/plugin/framework/item_snapshotter_server.go +++ b/pkg/plugin/framework/item_snapshotter_server.go @@ -27,17 +27,18 @@ import ( "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) // ItemSnapshotterGRPCServer implements the proto-generated ItemSnapshotterServer interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type ItemSnapshotterGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (recv *ItemSnapshotterGRPCServer) getImpl(name string) (isv1.ItemSnapshotter, error) { - impl, err := recv.mux.getHandler(name) + impl, err := recv.mux.GetHandler(name) if err != nil { return nil, err } @@ -52,19 +53,19 @@ func (recv *ItemSnapshotterGRPCServer) getImpl(name string) (isv1.ItemSnapshotte func (recv *ItemSnapshotterGRPCServer) Init(c context.Context, req *proto.ItemSnapshotterInitRequest) (response *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } err = impl.Init(req.Config) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil @@ -72,19 +73,19 @@ func (recv *ItemSnapshotterGRPCServer) Init(c context.Context, req *proto.ItemSn func (recv *ItemSnapshotterGRPCServer) AppliesTo(ctx context.Context, req *proto.ItemSnapshotterAppliesToRequest) (response *proto.ItemSnapshotterAppliesToResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } resourceSelector, err := impl.AppliesTo() if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.ItemSnapshotterAppliesToResponse{ @@ -100,23 +101,23 @@ func (recv *ItemSnapshotterGRPCServer) AppliesTo(ctx context.Context, req *proto func (recv *ItemSnapshotterGRPCServer) AlsoHandles(ctx context.Context, req *proto.AlsoHandlesRequest) (res *proto.AlsoHandlesResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var item unstructured.Unstructured var backup api.Backup if err := json.Unmarshal(req.Item, &item); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := json.Unmarshal(req.Backup, &backup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } ahi := isv1.AlsoHandlesInput{ Item: &item, @@ -124,7 +125,7 @@ func (recv *ItemSnapshotterGRPCServer) AlsoHandles(ctx context.Context, req *pro } alsoHandles, err := impl.AlsoHandles(&ahi) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } res = &proto.AlsoHandlesResponse{} @@ -136,23 +137,23 @@ func (recv *ItemSnapshotterGRPCServer) AlsoHandles(ctx context.Context, req *pro func (recv *ItemSnapshotterGRPCServer) SnapshotItem(ctx context.Context, req *proto.SnapshotItemRequest) (res *proto.SnapshotItemResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var item unstructured.Unstructured var backup api.Backup if err := json.Unmarshal(req.Item, &item); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := json.Unmarshal(req.Backup, &backup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } sii := isv1.SnapshotItemInput{ Item: &item, @@ -169,7 +170,7 @@ func (recv *ItemSnapshotterGRPCServer) SnapshotItem(ctx context.Context, req *pr } else { updatedItemJSON, err = json.Marshal(sio.UpdatedItem.UnstructuredContent()) if err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } } res = &proto.SnapshotItemResponse{ @@ -184,18 +185,18 @@ func (recv *ItemSnapshotterGRPCServer) SnapshotItem(ctx context.Context, req *pr func (recv *ItemSnapshotterGRPCServer) Progress(ctx context.Context, req *proto.ProgressRequest) (res *proto.ProgressResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var backup api.Backup if err := json.Unmarshal(req.Backup, &backup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } sipi := &isv1.ProgressInput{ ItemID: protoToResourceIdentifier(req.ItemID), @@ -205,7 +206,7 @@ func (recv *ItemSnapshotterGRPCServer) Progress(ctx context.Context, req *proto. sipo, err := impl.Progress(sipi) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } res = &proto.ProgressResponse{ @@ -223,18 +224,18 @@ func (recv *ItemSnapshotterGRPCServer) Progress(ctx context.Context, req *proto. func (recv *ItemSnapshotterGRPCServer) DeleteSnapshot(ctx context.Context, req *proto.DeleteItemSnapshotRequest) (empty *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var itemFromBackup unstructured.Unstructured if err := json.Unmarshal(req.ItemFromBackup, &itemFromBackup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } disi := isv1.DeleteSnapshotInput{ @@ -246,36 +247,36 @@ func (recv *ItemSnapshotterGRPCServer) DeleteSnapshot(ctx context.Context, req * err = impl.DeleteSnapshot(ctx, &disi) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return } func (recv *ItemSnapshotterGRPCServer) CreateItemFromSnapshot(ctx context.Context, req *proto.CreateItemFromSnapshotRequest) (res *proto.CreateItemFromSnapshotResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := recv.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var snapshottedItem unstructured.Unstructured if err := json.Unmarshal(req.Item, &snapshottedItem); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } var itemFromBackup unstructured.Unstructured if err := json.Unmarshal(req.Item, &itemFromBackup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } var restore api.Restore if err := json.Unmarshal(req.Restore, &restore); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } cii := isv1.CreateItemInput{ @@ -289,7 +290,7 @@ func (recv *ItemSnapshotterGRPCServer) CreateItemFromSnapshot(ctx context.Contex cio, err := impl.CreateItemFromSnapshot(ctx, &cii) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var updatedItemJSON []byte @@ -298,7 +299,7 @@ func (recv *ItemSnapshotterGRPCServer) CreateItemFromSnapshot(ctx context.Contex } else { updatedItemJSON, err = json.Marshal(cio.UpdatedItem.UnstructuredContent()) if err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } } res = &proto.CreateItemFromSnapshotResponse{ diff --git a/pkg/plugin/framework/object_store.go b/pkg/plugin/framework/object_store.go index bd7a90cf3c..ddf7414218 100644 --- a/pkg/plugin/framework/object_store.go +++ b/pkg/plugin/framework/object_store.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,17 +30,17 @@ import ( // interface. type ObjectStorePlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns an ObjectStore gRPC client. func (p *ObjectStorePlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newObjectStoreGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newObjectStoreGRPCClient), nil } // GRPCServer registers an ObjectStore gRPC server. func (p *ObjectStorePlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterObjectStoreServer(server, &ObjectStoreGRPCServer{mux: p.serverMux}) + proto.RegisterObjectStoreServer(server, &ObjectStoreGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/object_store_client.go b/pkg/plugin/framework/object_store_client.go index 8442749076..7f40921c2a 100644 --- a/pkg/plugin/framework/object_store_client.go +++ b/pkg/plugin/framework/object_store_client.go @@ -24,28 +24,29 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) const byteChunkSize = 16384 // NewObjectStorePlugin construct an ObjectStorePlugin. -func NewObjectStorePlugin(options ...PluginOption) *ObjectStorePlugin { +func NewObjectStorePlugin(options ...common.PluginOption) *ObjectStorePlugin { return &ObjectStorePlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } // ObjectStoreGRPCClient implements the ObjectStore interface and uses a // gRPC client to make calls to the plugin server. type ObjectStoreGRPCClient struct { - *clientBase + *common.ClientBase grpcClient proto.ObjectStoreClient } -func newObjectStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newObjectStoreGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &ObjectStoreGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: proto.NewObjectStoreClient(clientConn), } } @@ -55,12 +56,12 @@ func newObjectStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) int // cannot be initialized from the provided config. func (c *ObjectStoreGRPCClient) Init(config map[string]string) error { req := &proto.ObjectStoreInitRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Config: config, } if _, err := c.grpcClient.Init(context.Background(), req); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil @@ -71,7 +72,7 @@ func (c *ObjectStoreGRPCClient) Init(config map[string]string) error { func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) error { stream, err := c.grpcClient.PutObject(context.Background()) if err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } // read from the provider io.Reader into chunks, and send each one over @@ -81,7 +82,7 @@ func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) er n, err := body.Read(chunk) if err == io.EOF { if _, resErr := stream.CloseAndRecv(); resErr != nil { - return fromGRPCError(resErr) + return common.FromGRPCError(resErr) } return nil } @@ -90,8 +91,8 @@ func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) er return errors.WithStack(err) } - if err := stream.Send(&proto.PutObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key, Body: chunk[0:n]}); err != nil { - return fromGRPCError(err) + if err := stream.Send(&proto.PutObjectRequest{Plugin: c.Plugin, Bucket: bucket, Key: key, Body: chunk[0:n]}); err != nil { + return common.FromGRPCError(err) } } } @@ -99,7 +100,7 @@ func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) er // ObjectExists checks if there is an object with the given key in the object storage bucket. func (c *ObjectStoreGRPCClient) ObjectExists(bucket, key string) (bool, error) { req := &proto.ObjectExistsRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Key: key, } @@ -116,14 +117,14 @@ func (c *ObjectStoreGRPCClient) ObjectExists(bucket, key string) (bool, error) { // bucket in object storage. func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, error) { req := &proto.GetObjectRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Key: key, } stream, err := c.grpcClient.GetObject(context.Background(), req) if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } receive := func() ([]byte, error) { @@ -135,7 +136,7 @@ func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, er return nil, err } if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } return data.Data, nil @@ -143,7 +144,7 @@ func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, er close := func() error { if err := stream.CloseSend(); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil } @@ -156,7 +157,7 @@ func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, er // often used to simulate a directory hierarchy in object storage). func (c *ObjectStoreGRPCClient) ListCommonPrefixes(bucket, prefix, delimiter string) ([]string, error) { req := &proto.ListCommonPrefixesRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Prefix: prefix, Delimiter: delimiter, @@ -164,7 +165,7 @@ func (c *ObjectStoreGRPCClient) ListCommonPrefixes(bucket, prefix, delimiter str res, err := c.grpcClient.ListCommonPrefixes(context.Background(), req) if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } return res.Prefixes, nil @@ -173,14 +174,14 @@ func (c *ObjectStoreGRPCClient) ListCommonPrefixes(bucket, prefix, delimiter str // ListObjects gets a list of all objects in bucket that have the same prefix. func (c *ObjectStoreGRPCClient) ListObjects(bucket, prefix string) ([]string, error) { req := &proto.ListObjectsRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Prefix: prefix, } res, err := c.grpcClient.ListObjects(context.Background(), req) if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } return res.Keys, nil @@ -190,13 +191,13 @@ func (c *ObjectStoreGRPCClient) ListObjects(bucket, prefix string) ([]string, er // bucket. func (c *ObjectStoreGRPCClient) DeleteObject(bucket, key string) error { req := &proto.DeleteObjectRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Key: key, } if _, err := c.grpcClient.DeleteObject(context.Background(), req); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil @@ -205,7 +206,7 @@ func (c *ObjectStoreGRPCClient) DeleteObject(bucket, key string) error { // CreateSignedURL creates a pre-signed URL for the given bucket and key that expires after ttl. func (c *ObjectStoreGRPCClient) CreateSignedURL(bucket, key string, ttl time.Duration) (string, error) { req := &proto.CreateSignedURLRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Bucket: bucket, Key: key, Ttl: int64(ttl), @@ -213,7 +214,7 @@ func (c *ObjectStoreGRPCClient) CreateSignedURL(bucket, key string, ttl time.Dur res, err := c.grpcClient.CreateSignedURL(context.Background(), req) if err != nil { - return "", fromGRPCError(err) + return "", common.FromGRPCError(err) } return res.Url, nil diff --git a/pkg/plugin/framework/object_store_server.go b/pkg/plugin/framework/object_store_server.go index b2f2359ce5..2d3ef3658b 100644 --- a/pkg/plugin/framework/object_store_server.go +++ b/pkg/plugin/framework/object_store_server.go @@ -23,6 +23,7 @@ import ( "github.com/pkg/errors" "golang.org/x/net/context" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -30,11 +31,11 @@ import ( // ObjectStoreGRPCServer implements the proto-generated ObjectStoreServer interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type ObjectStoreGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (s *ObjectStoreGRPCServer) getImpl(name string) (velero.ObjectStore, error) { - impl, err := s.mux.getHandler(name) + impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } @@ -52,18 +53,18 @@ func (s *ObjectStoreGRPCServer) getImpl(name string) (velero.ObjectStore, error) // cannot be initialized from the provided config. func (s *ObjectStoreGRPCServer) Init(ctx context.Context, req *proto.ObjectStoreInitRequest) (response *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } if err := impl.Init(req.Config); err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil @@ -73,7 +74,7 @@ func (s *ObjectStoreGRPCServer) Init(ctx context.Context, req *proto.ObjectStore // object storage bucket with the given key. func (s *ObjectStoreGRPCServer) PutObject(stream proto.ObjectStore_PutObjectServer) (err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() @@ -82,12 +83,12 @@ func (s *ObjectStoreGRPCServer) PutObject(stream proto.ObjectStore_PutObjectServ // in our receive method, we'll use `first` on the first call firstChunk, err := stream.Recv() if err != nil { - return newGRPCError(errors.WithStack(err)) + return common.NewGRPCError(errors.WithStack(err)) } impl, err := s.getImpl(firstChunk.Plugin) if err != nil { - return newGRPCError(err) + return common.NewGRPCError(err) } bucket := firstChunk.Bucket @@ -118,11 +119,11 @@ func (s *ObjectStoreGRPCServer) PutObject(stream proto.ObjectStore_PutObjectServ } if err := impl.PutObject(bucket, key, &StreamReadCloser{receive: receive, close: close}); err != nil { - return newGRPCError(err) + return common.NewGRPCError(err) } if err := stream.SendAndClose(&proto.Empty{}); err != nil { - return newGRPCError(errors.WithStack(err)) + return common.NewGRPCError(errors.WithStack(err)) } return nil @@ -131,19 +132,19 @@ func (s *ObjectStoreGRPCServer) PutObject(stream proto.ObjectStore_PutObjectServ // ObjectExists checks if there is an object with the given key in the object storage bucket. func (s *ObjectStoreGRPCServer) ObjectExists(ctx context.Context, req *proto.ObjectExistsRequest) (response *proto.ObjectExistsResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } exists, err := impl.ObjectExists(req.Bucket, req.Key) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.ObjectExistsResponse{Exists: exists}, nil @@ -153,19 +154,19 @@ func (s *ObjectStoreGRPCServer) ObjectExists(ctx context.Context, req *proto.Obj // bucket in object storage. func (s *ObjectStoreGRPCServer) GetObject(req *proto.GetObjectRequest, stream proto.ObjectStore_GetObjectServer) (err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return newGRPCError(err) + return common.NewGRPCError(err) } rdr, err := impl.GetObject(req.Bucket, req.Key) if err != nil { - return newGRPCError(err) + return common.NewGRPCError(err) } defer rdr.Close() @@ -173,14 +174,14 @@ func (s *ObjectStoreGRPCServer) GetObject(req *proto.GetObjectRequest, stream pr for { n, err := rdr.Read(chunk) if err != nil && err != io.EOF { - return newGRPCError(errors.WithStack(err)) + return common.NewGRPCError(errors.WithStack(err)) } if n == 0 { return nil } if err := stream.Send(&proto.Bytes{Data: chunk[0:n]}); err != nil { - return newGRPCError(errors.WithStack(err)) + return common.NewGRPCError(errors.WithStack(err)) } } } @@ -190,19 +191,19 @@ func (s *ObjectStoreGRPCServer) GetObject(req *proto.GetObjectRequest, stream pr // (this is often used to simulate a directory hierarchy in object storage). func (s *ObjectStoreGRPCServer) ListCommonPrefixes(ctx context.Context, req *proto.ListCommonPrefixesRequest) (response *proto.ListCommonPrefixesResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } prefixes, err := impl.ListCommonPrefixes(req.Bucket, req.Prefix, req.Delimiter) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.ListCommonPrefixesResponse{Prefixes: prefixes}, nil @@ -211,19 +212,19 @@ func (s *ObjectStoreGRPCServer) ListCommonPrefixes(ctx context.Context, req *pro // ListObjects gets a list of all objects in bucket that have the same prefix. func (s *ObjectStoreGRPCServer) ListObjects(ctx context.Context, req *proto.ListObjectsRequest) (response *proto.ListObjectsResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } keys, err := impl.ListObjects(req.Bucket, req.Prefix) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.ListObjectsResponse{Keys: keys}, nil @@ -233,18 +234,18 @@ func (s *ObjectStoreGRPCServer) ListObjects(ctx context.Context, req *proto.List // bucket. func (s *ObjectStoreGRPCServer) DeleteObject(ctx context.Context, req *proto.DeleteObjectRequest) (response *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } if err := impl.DeleteObject(req.Bucket, req.Key); err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil @@ -253,19 +254,19 @@ func (s *ObjectStoreGRPCServer) DeleteObject(ctx context.Context, req *proto.Del // CreateSignedURL creates a pre-signed URL for the given bucket and key that expires after ttl. func (s *ObjectStoreGRPCServer) CreateSignedURL(ctx context.Context, req *proto.CreateSignedURLRequest) (response *proto.CreateSignedURLResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } url, err := impl.CreateSignedURL(req.Bucket, req.Key, time.Duration(req.Ttl)) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.CreateSignedURLResponse{Url: url}, nil diff --git a/pkg/plugin/framework/plugin_lister.go b/pkg/plugin/framework/plugin_lister.go index aed7de77d4..e658ae8a21 100644 --- a/pkg/plugin/framework/plugin_lister.go +++ b/pkg/plugin/framework/plugin_lister.go @@ -22,13 +22,14 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) // PluginIdentifier uniquely identifies a plugin by command, kind, and name. type PluginIdentifier struct { Command string - Kind PluginKind + Kind common.PluginKind Name string } @@ -87,13 +88,13 @@ func (c *PluginListerGRPCClient) ListPlugins() ([]PluginIdentifier, error) { ret := make([]PluginIdentifier, len(resp.Plugins)) for i, id := range resp.Plugins { - if _, ok := AllPluginKinds()[id.Kind]; !ok { + if _, ok := common.AllPluginKinds()[id.Kind]; !ok { return nil, errors.Errorf("invalid plugin kind: %s", id.Kind) } ret[i] = PluginIdentifier{ Command: id.Command, - Kind: PluginKind(id.Kind), + Kind: common.PluginKind(id.Kind), Name: id.Name, } } @@ -126,7 +127,7 @@ func (s *PluginListerGRPCServer) ListPlugins(ctx context.Context, req *proto.Emp plugins := make([]*proto.PluginIdentifier, len(list)) for i, id := range list { - if _, ok := AllPluginKinds()[id.Kind.String()]; !ok { + if _, ok := common.AllPluginKinds()[id.Kind.String()]; !ok { return nil, errors.Errorf("invalid plugin kind: %s", id.Kind) } diff --git a/pkg/plugin/framework/restore_item_action.go b/pkg/plugin/framework/restore_item_action.go index 255c67ae88..b5a3d41c16 100644 --- a/pkg/plugin/framework/restore_item_action.go +++ b/pkg/plugin/framework/restore_item_action.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,16 +30,16 @@ import ( // interface. type RestoreItemActionPlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns a RestoreItemAction gRPC client. func (p *RestoreItemActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newRestoreItemActionGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newRestoreItemActionGRPCClient), nil } // GRPCServer registers a RestoreItemAction gRPC server. func (p *RestoreItemActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterRestoreItemActionServer(server, &RestoreItemActionGRPCServer{mux: p.serverMux}) + proto.RegisterRestoreItemActionServer(server, &RestoreItemActionGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/restore_item_action_client.go b/pkg/plugin/framework/restore_item_action_client.go index 33d7fff3ae..03907f390b 100644 --- a/pkg/plugin/framework/restore_item_action_client.go +++ b/pkg/plugin/framework/restore_item_action_client.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -32,30 +33,30 @@ import ( var _ velero.RestoreItemAction = &RestoreItemActionGRPCClient{} // NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin. -func NewRestoreItemActionPlugin(options ...PluginOption) *RestoreItemActionPlugin { +func NewRestoreItemActionPlugin(options ...common.PluginOption) *RestoreItemActionPlugin { return &RestoreItemActionPlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } // RestoreItemActionGRPCClient implements the backup/ItemAction interface and uses a // gRPC client to make calls to the plugin server. type RestoreItemActionGRPCClient struct { - *clientBase + *common.ClientBase grpcClient proto.RestoreItemActionClient } -func newRestoreItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newRestoreItemActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &RestoreItemActionGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: proto.NewRestoreItemActionClient(clientConn), } } func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { - res, err := c.grpcClient.AppliesTo(context.Background(), &proto.RestoreItemActionAppliesToRequest{Plugin: c.plugin}) + res, err := c.grpcClient.AppliesTo(context.Background(), &proto.RestoreItemActionAppliesToRequest{Plugin: c.Plugin}) if err != nil { - return velero.ResourceSelector{}, fromGRPCError(err) + return velero.ResourceSelector{}, common.FromGRPCError(err) } if res.ResourceSelector == nil { @@ -88,7 +89,7 @@ func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExe } req := &proto.RestoreItemActionExecuteRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Item: itemJSON, ItemFromBackup: itemFromBackupJSON, Restore: restoreJSON, @@ -96,7 +97,7 @@ func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExe res, err := c.grpcClient.Execute(context.Background(), req) if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } var updatedItem unstructured.Unstructured diff --git a/pkg/plugin/framework/restore_item_action_server.go b/pkg/plugin/framework/restore_item_action_server.go index ae5affb151..5b45f4e91f 100644 --- a/pkg/plugin/framework/restore_item_action_server.go +++ b/pkg/plugin/framework/restore_item_action_server.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -31,11 +32,11 @@ import ( // RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type RestoreItemActionGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAction, error) { - impl, err := s.mux.getHandler(name) + impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } @@ -50,19 +51,19 @@ func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAc func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.RestoreItemActionAppliesToRequest) (response *proto.RestoreItemActionAppliesToResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } resourceSelector, err := impl.AppliesTo() if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.RestoreItemActionAppliesToResponse{ @@ -78,14 +79,14 @@ func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto. func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.RestoreItemActionExecuteRequest) (response *proto.RestoreItemActionExecuteResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var ( @@ -95,15 +96,15 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re ) if err := json.Unmarshal(req.Item, &item); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := json.Unmarshal(req.ItemFromBackup, &itemFromBackup); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } if err := json.Unmarshal(req.Restore, &restoreObj); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{ @@ -112,7 +113,7 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re Restore: &restoreObj, }) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } // If the plugin implementation returned a nil updateItem (meaning no modifications), reset updatedItem to the @@ -123,7 +124,7 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re } else { updatedItemJSON, err = json.Marshal(executeOutput.UpdatedItem.UnstructuredContent()) if err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } } diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index b25ef341de..20c5e48e00 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -25,6 +25,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/pflag" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -40,43 +41,43 @@ type Server interface { // RegisterBackupItemAction registers a backup item action. Accepted format // for the plugin name is /. - RegisterBackupItemAction(pluginName string, initializer HandlerInitializer) Server + RegisterBackupItemAction(pluginName string, initializer common.HandlerInitializer) Server // RegisterBackupItemActions registers multiple backup item actions. - RegisterBackupItemActions(map[string]HandlerInitializer) Server + RegisterBackupItemActions(map[string]common.HandlerInitializer) Server // RegisterVolumeSnapshotter registers a volume snapshotter. Accepted format // for the plugin name is /. - RegisterVolumeSnapshotter(pluginName string, initializer HandlerInitializer) Server + RegisterVolumeSnapshotter(pluginName string, initializer common.HandlerInitializer) Server // RegisterVolumeSnapshotters registers multiple volume snapshotters. - RegisterVolumeSnapshotters(map[string]HandlerInitializer) Server + RegisterVolumeSnapshotters(map[string]common.HandlerInitializer) Server // RegisterObjectStore registers an object store. Accepted format // for the plugin name is /. - RegisterObjectStore(pluginName string, initializer HandlerInitializer) Server + RegisterObjectStore(pluginName string, initializer common.HandlerInitializer) Server // RegisterObjectStores registers multiple object stores. - RegisterObjectStores(map[string]HandlerInitializer) Server + RegisterObjectStores(map[string]common.HandlerInitializer) Server // RegisterRestoreItemAction registers a restore item action. Accepted format // for the plugin name is /. - RegisterRestoreItemAction(pluginName string, initializer HandlerInitializer) Server + RegisterRestoreItemAction(pluginName string, initializer common.HandlerInitializer) Server // RegisterRestoreItemActions registers multiple restore item actions. - RegisterRestoreItemActions(map[string]HandlerInitializer) Server + RegisterRestoreItemActions(map[string]common.HandlerInitializer) Server // RegisterDeleteItemAction registers a delete item action. Accepted format // for the plugin name is /. - RegisterDeleteItemAction(pluginName string, initializer HandlerInitializer) Server + RegisterDeleteItemAction(pluginName string, initializer common.HandlerInitializer) Server // RegisterDeleteItemActions registers multiple Delete item actions. - RegisterDeleteItemActions(map[string]HandlerInitializer) Server + RegisterDeleteItemActions(map[string]common.HandlerInitializer) Server - RegisterItemSnapshotter(pluginName string, initializer HandlerInitializer) Server + RegisterItemSnapshotter(pluginName string, initializer common.HandlerInitializer) Server // RegisterItemSnapshotters registers multiple Item Snapshotters - RegisterItemSnapshotters(map[string]HandlerInitializer) Server + RegisterItemSnapshotters(map[string]common.HandlerInitializer) Server // Server runs the plugin server. Serve() @@ -102,12 +103,12 @@ func NewServer() Server { return &server{ log: log, logLevelFlag: logging.LogLevelFlag(log.Level), - backupItemAction: NewBackupItemActionPlugin(serverLogger(log)), - volumeSnapshotter: NewVolumeSnapshotterPlugin(serverLogger(log)), - objectStore: NewObjectStorePlugin(serverLogger(log)), - restoreItemAction: NewRestoreItemActionPlugin(serverLogger(log)), - deleteItemAction: NewDeleteItemActionPlugin(serverLogger(log)), - itemSnapshotter: NewItemSnapshotterPlugin(serverLogger(log)), + backupItemAction: NewBackupItemActionPlugin(common.ServerLogger(log)), + volumeSnapshotter: NewVolumeSnapshotterPlugin(common.ServerLogger(log)), + objectStore: NewObjectStorePlugin(common.ServerLogger(log)), + restoreItemAction: NewRestoreItemActionPlugin(common.ServerLogger(log)), + deleteItemAction: NewDeleteItemActionPlugin(common.ServerLogger(log)), + itemSnapshotter: NewItemSnapshotterPlugin(common.ServerLogger(log)), } } @@ -119,71 +120,71 @@ func (s *server) BindFlags(flags *pflag.FlagSet) Server { return s } -func (s *server) RegisterBackupItemAction(name string, initializer HandlerInitializer) Server { - s.backupItemAction.register(name, initializer) +func (s *server) RegisterBackupItemAction(name string, initializer common.HandlerInitializer) Server { + s.backupItemAction.Register(name, initializer) return s } -func (s *server) RegisterBackupItemActions(m map[string]HandlerInitializer) Server { +func (s *server) RegisterBackupItemActions(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterBackupItemAction(name, m[name]) } return s } -func (s *server) RegisterVolumeSnapshotter(name string, initializer HandlerInitializer) Server { - s.volumeSnapshotter.register(name, initializer) +func (s *server) RegisterVolumeSnapshotter(name string, initializer common.HandlerInitializer) Server { + s.volumeSnapshotter.Register(name, initializer) return s } -func (s *server) RegisterVolumeSnapshotters(m map[string]HandlerInitializer) Server { +func (s *server) RegisterVolumeSnapshotters(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterVolumeSnapshotter(name, m[name]) } return s } -func (s *server) RegisterObjectStore(name string, initializer HandlerInitializer) Server { - s.objectStore.register(name, initializer) +func (s *server) RegisterObjectStore(name string, initializer common.HandlerInitializer) Server { + s.objectStore.Register(name, initializer) return s } -func (s *server) RegisterObjectStores(m map[string]HandlerInitializer) Server { +func (s *server) RegisterObjectStores(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterObjectStore(name, m[name]) } return s } -func (s *server) RegisterRestoreItemAction(name string, initializer HandlerInitializer) Server { - s.restoreItemAction.register(name, initializer) +func (s *server) RegisterRestoreItemAction(name string, initializer common.HandlerInitializer) Server { + s.restoreItemAction.Register(name, initializer) return s } -func (s *server) RegisterRestoreItemActions(m map[string]HandlerInitializer) Server { +func (s *server) RegisterRestoreItemActions(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterRestoreItemAction(name, m[name]) } return s } -func (s *server) RegisterDeleteItemAction(name string, initializer HandlerInitializer) Server { - s.deleteItemAction.register(name, initializer) +func (s *server) RegisterDeleteItemAction(name string, initializer common.HandlerInitializer) Server { + s.deleteItemAction.Register(name, initializer) return s } -func (s *server) RegisterDeleteItemActions(m map[string]HandlerInitializer) Server { +func (s *server) RegisterDeleteItemActions(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterDeleteItemAction(name, m[name]) } return s } -func (s *server) RegisterItemSnapshotter(name string, initializer HandlerInitializer) Server { - s.itemSnapshotter.register(name, initializer) +func (s *server) RegisterItemSnapshotter(name string, initializer common.HandlerInitializer) Server { + s.itemSnapshotter.Register(name, initializer) return s } -func (s *server) RegisterItemSnapshotters(m map[string]HandlerInitializer) Server { +func (s *server) RegisterItemSnapshotters(m map[string]common.HandlerInitializer) Server { for name := range m { s.RegisterItemSnapshotter(name, m[name]) } @@ -191,10 +192,10 @@ func (s *server) RegisterItemSnapshotters(m map[string]HandlerInitializer) Serve } // getNames returns a list of PluginIdentifiers registered with plugin. -func getNames(command string, kind PluginKind, plugin Interface) []PluginIdentifier { +func getNames(command string, kind common.PluginKind, plugin Interface) []PluginIdentifier { var pluginIdentifiers []PluginIdentifier - for _, name := range plugin.names() { + for _, name := range plugin.Names() { id := PluginIdentifier{Command: command, Kind: kind, Name: name} pluginIdentifiers = append(pluginIdentifiers, id) } @@ -214,25 +215,25 @@ func (s *server) Serve() { command := os.Args[0] var pluginIdentifiers []PluginIdentifier - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindBackupItemAction, s.backupItemAction)...) - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindVolumeSnapshotter, s.volumeSnapshotter)...) - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindObjectStore, s.objectStore)...) - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindRestoreItemAction, s.restoreItemAction)...) - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindDeleteItemAction, s.deleteItemAction)...) - pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindItemSnapshotter, s.itemSnapshotter)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindBackupItemAction, s.backupItemAction)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindVolumeSnapshotter, s.volumeSnapshotter)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindObjectStore, s.objectStore)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindRestoreItemAction, s.restoreItemAction)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindDeleteItemAction, s.deleteItemAction)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindItemSnapshotter, s.itemSnapshotter)...) pluginLister := NewPluginLister(pluginIdentifiers...) plugin.Serve(&plugin.ServeConfig{ HandshakeConfig: Handshake(), Plugins: map[string]plugin.Plugin{ - string(PluginKindBackupItemAction): s.backupItemAction, - string(PluginKindVolumeSnapshotter): s.volumeSnapshotter, - string(PluginKindObjectStore): s.objectStore, - string(PluginKindPluginLister): NewPluginListerPlugin(pluginLister), - string(PluginKindRestoreItemAction): s.restoreItemAction, - string(PluginKindDeleteItemAction): s.deleteItemAction, - string(PluginKindItemSnapshotter): s.itemSnapshotter, + string(common.PluginKindBackupItemAction): s.backupItemAction, + string(common.PluginKindVolumeSnapshotter): s.volumeSnapshotter, + string(common.PluginKindObjectStore): s.objectStore, + string(common.PluginKindPluginLister): NewPluginListerPlugin(pluginLister), + string(common.PluginKindRestoreItemAction): s.restoreItemAction, + string(common.PluginKindDeleteItemAction): s.deleteItemAction, + string(common.PluginKindItemSnapshotter): s.itemSnapshotter, }, GRPCServer: plugin.DefaultGRPCServer, }) diff --git a/pkg/plugin/framework/volume_snapshotter.go b/pkg/plugin/framework/volume_snapshotter.go index 50602b2c05..566fc1eb72 100644 --- a/pkg/plugin/framework/volume_snapshotter.go +++ b/pkg/plugin/framework/volume_snapshotter.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) @@ -29,16 +30,16 @@ import ( // interface. type VolumeSnapshotterPlugin struct { plugin.NetRPCUnsupportedPlugin - *pluginBase + *common.PluginBase } // GRPCClient returns a VolumeSnapshotter gRPC client. func (p *VolumeSnapshotterPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { - return newClientDispenser(p.clientLogger, clientConn, newVolumeSnapshotterGRPCClient), nil + return common.NewClientDispenser(p.ClientLogger, clientConn, newVolumeSnapshotterGRPCClient), nil } // GRPCServer registers a VolumeSnapshotter gRPC server. func (p *VolumeSnapshotterPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { - proto.RegisterVolumeSnapshotterServer(server, &VolumeSnapshotterGRPCServer{mux: p.serverMux}) + proto.RegisterVolumeSnapshotterServer(server, &VolumeSnapshotterGRPCServer{mux: p.ServerMux}) return nil } diff --git a/pkg/plugin/framework/volume_snapshotter_client.go b/pkg/plugin/framework/volume_snapshotter_client.go index 53ac58d25b..a359046177 100644 --- a/pkg/plugin/framework/volume_snapshotter_client.go +++ b/pkg/plugin/framework/volume_snapshotter_client.go @@ -25,26 +25,27 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" ) // NewVolumeSnapshotterPlugin constructs a VolumeSnapshotterPlugin. -func NewVolumeSnapshotterPlugin(options ...PluginOption) *VolumeSnapshotterPlugin { +func NewVolumeSnapshotterPlugin(options ...common.PluginOption) *VolumeSnapshotterPlugin { return &VolumeSnapshotterPlugin{ - pluginBase: newPluginBase(options...), + PluginBase: common.NewPluginBase(options...), } } // VolumeSnapshotterGRPCClient implements the cloudprovider.VolumeSnapshotter interface and uses a // gRPC client to make calls to the plugin server. type VolumeSnapshotterGRPCClient struct { - *clientBase + *common.ClientBase grpcClient proto.VolumeSnapshotterClient } -func newVolumeSnapshotterGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} { +func newVolumeSnapshotterGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { return &VolumeSnapshotterGRPCClient{ - clientBase: base, + ClientBase: base, grpcClient: proto.NewVolumeSnapshotterClient(clientConn), } } @@ -54,12 +55,12 @@ func newVolumeSnapshotterGRPCClient(base *clientBase, clientConn *grpc.ClientCon // cannot be initialized from the provided config. func (c *VolumeSnapshotterGRPCClient) Init(config map[string]string) error { req := &proto.VolumeSnapshotterInitRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, Config: config, } if _, err := c.grpcClient.Init(context.Background(), req); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil @@ -69,7 +70,7 @@ func (c *VolumeSnapshotterGRPCClient) Init(config map[string]string) error { // and with the specified type and IOPS (if using provisioned IOPS). func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) { req := &proto.CreateVolumeRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, SnapshotID: snapshotID, VolumeType: volumeType, VolumeAZ: volumeAZ, @@ -83,7 +84,7 @@ func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volum res, err := c.grpcClient.CreateVolumeFromSnapshot(context.Background(), req) if err != nil { - return "", fromGRPCError(err) + return "", common.FromGRPCError(err) } return res.VolumeID, nil @@ -93,14 +94,14 @@ func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volum // volume. func (c *VolumeSnapshotterGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) { req := &proto.GetVolumeInfoRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, VolumeID: volumeID, VolumeAZ: volumeAZ, } res, err := c.grpcClient.GetVolumeInfo(context.Background(), req) if err != nil { - return "", nil, fromGRPCError(err) + return "", nil, common.FromGRPCError(err) } var iops *int64 @@ -115,7 +116,7 @@ func (c *VolumeSnapshotterGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) ( // set of tags to the snapshot. func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) { req := &proto.CreateSnapshotRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, VolumeID: volumeID, VolumeAZ: volumeAZ, Tags: tags, @@ -123,7 +124,7 @@ func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, res, err := c.grpcClient.CreateSnapshot(context.Background(), req) if err != nil { - return "", fromGRPCError(err) + return "", common.FromGRPCError(err) } return res.SnapshotID, nil @@ -132,12 +133,12 @@ func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, // DeleteSnapshot deletes the specified volume snapshot. func (c *VolumeSnapshotterGRPCClient) DeleteSnapshot(snapshotID string) error { req := &proto.DeleteSnapshotRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, SnapshotID: snapshotID, } if _, err := c.grpcClient.DeleteSnapshot(context.Background(), req); err != nil { - return fromGRPCError(err) + return common.FromGRPCError(err) } return nil @@ -150,13 +151,13 @@ func (c *VolumeSnapshotterGRPCClient) GetVolumeID(pv runtime.Unstructured) (stri } req := &proto.GetVolumeIDRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, PersistentVolume: encodedPV, } resp, err := c.grpcClient.GetVolumeID(context.Background(), req) if err != nil { - return "", fromGRPCError(err) + return "", common.FromGRPCError(err) } return resp.VolumeID, nil @@ -169,14 +170,14 @@ func (c *VolumeSnapshotterGRPCClient) SetVolumeID(pv runtime.Unstructured, volum } req := &proto.SetVolumeIDRequest{ - Plugin: c.plugin, + Plugin: c.Plugin, PersistentVolume: encodedPV, VolumeID: volumeID, } resp, err := c.grpcClient.SetVolumeID(context.Background(), req) if err != nil { - return nil, fromGRPCError(err) + return nil, common.FromGRPCError(err) } var updatedPV unstructured.Unstructured diff --git a/pkg/plugin/framework/volume_snapshotter_server.go b/pkg/plugin/framework/volume_snapshotter_server.go index bde371c93f..e0073db9aa 100644 --- a/pkg/plugin/framework/volume_snapshotter_server.go +++ b/pkg/plugin/framework/volume_snapshotter_server.go @@ -23,6 +23,7 @@ import ( "golang.org/x/net/context" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -30,11 +31,11 @@ import ( // VolumeSnapshotterGRPCServer implements the proto-generated VolumeSnapshotterServer interface, and accepts // gRPC calls and forwards them to an implementation of the pluggable interface. type VolumeSnapshotterGRPCServer struct { - mux *serverMux + mux *common.ServerMux } func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (velero.VolumeSnapshotter, error) { - impl, err := s.mux.getHandler(name) + impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } @@ -52,18 +53,18 @@ func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (velero.VolumeSnapsho // cannot be initialized from the provided config. func (s *VolumeSnapshotterGRPCServer) Init(ctx context.Context, req *proto.VolumeSnapshotterInitRequest) (response *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } if err := impl.Init(req.Config); err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil @@ -73,14 +74,14 @@ func (s *VolumeSnapshotterGRPCServer) Init(ctx context.Context, req *proto.Volum // and with the specified type and IOPS (if using provisioned IOPS). func (s *VolumeSnapshotterGRPCServer) CreateVolumeFromSnapshot(ctx context.Context, req *proto.CreateVolumeRequest) (response *proto.CreateVolumeResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } snapshotID := req.SnapshotID @@ -94,7 +95,7 @@ func (s *VolumeSnapshotterGRPCServer) CreateVolumeFromSnapshot(ctx context.Conte volumeID, err := impl.CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ, iops) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.CreateVolumeResponse{VolumeID: volumeID}, nil @@ -104,19 +105,19 @@ func (s *VolumeSnapshotterGRPCServer) CreateVolumeFromSnapshot(ctx context.Conte // volume. func (s *VolumeSnapshotterGRPCServer) GetVolumeInfo(ctx context.Context, req *proto.GetVolumeInfoRequest) (response *proto.GetVolumeInfoResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } volumeType, iops, err := impl.GetVolumeInfo(req.VolumeID, req.VolumeAZ) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } res := &proto.GetVolumeInfoResponse{ @@ -134,19 +135,19 @@ func (s *VolumeSnapshotterGRPCServer) GetVolumeInfo(ctx context.Context, req *pr // set of tags to the snapshot. func (s *VolumeSnapshotterGRPCServer) CreateSnapshot(ctx context.Context, req *proto.CreateSnapshotRequest) (response *proto.CreateSnapshotResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } snapshotID, err := impl.CreateSnapshot(req.VolumeID, req.VolumeAZ, req.Tags) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.CreateSnapshotResponse{SnapshotID: snapshotID}, nil @@ -155,18 +156,18 @@ func (s *VolumeSnapshotterGRPCServer) CreateSnapshot(ctx context.Context, req *p // DeleteSnapshot deletes the specified volume snapshot. func (s *VolumeSnapshotterGRPCServer) DeleteSnapshot(ctx context.Context, req *proto.DeleteSnapshotRequest) (response *proto.Empty, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } if err := impl.DeleteSnapshot(req.SnapshotID); err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.Empty{}, nil @@ -174,25 +175,25 @@ func (s *VolumeSnapshotterGRPCServer) DeleteSnapshot(ctx context.Context, req *p func (s *VolumeSnapshotterGRPCServer) GetVolumeID(ctx context.Context, req *proto.GetVolumeIDRequest) (response *proto.GetVolumeIDResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var pv unstructured.Unstructured if err := json.Unmarshal(req.PersistentVolume, &pv); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } volumeID, err := impl.GetVolumeID(&pv) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.GetVolumeIDResponse{VolumeID: volumeID}, nil @@ -200,29 +201,29 @@ func (s *VolumeSnapshotterGRPCServer) GetVolumeID(ctx context.Context, req *prot func (s *VolumeSnapshotterGRPCServer) SetVolumeID(ctx context.Context, req *proto.SetVolumeIDRequest) (response *proto.SetVolumeIDResponse, err error) { defer func() { - if recoveredErr := handlePanic(recover()); recoveredErr != nil { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { err = recoveredErr } }() impl, err := s.getImpl(req.Plugin) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } var pv unstructured.Unstructured if err := json.Unmarshal(req.PersistentVolume, &pv); err != nil { - return nil, newGRPCError(errors.WithStack(err)) + return nil, common.NewGRPCError(errors.WithStack(err)) } updatedPV, err := impl.SetVolumeID(&pv, req.VolumeID) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } updatedPVBytes, err := json.Marshal(updatedPV.UnstructuredContent()) if err != nil { - return nil, newGRPCError(err) + return nil, common.NewGRPCError(err) } return &proto.SetVolumeIDResponse{PersistentVolume: updatedPVBytes}, nil diff --git a/pkg/backup/mocks/item_action.go b/pkg/plugin/velero/mocks/backupitemaction/v1/BackupItemAction.go similarity index 58% rename from pkg/backup/mocks/item_action.go rename to pkg/plugin/velero/mocks/backupitemaction/v1/BackupItemAction.go index 5d4bd8db55..cfca2ef6d2 100644 --- a/pkg/backup/mocks/item_action.go +++ b/pkg/plugin/velero/mocks/backupitemaction/v1/BackupItemAction.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,23 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ // Code generated by mockery v1.0.0. DO NOT EDIT. -package mocks + +package v1 import ( mock "github.com/stretchr/testify/mock" runtime "k8s.io/apimachinery/pkg/runtime" - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) -// ItemAction is an autogenerated mock type for the ItemAction type -type ItemAction struct { +// BackupItemAction is an autogenerated mock type for the BackupItemAction type +type BackupItemAction struct { mock.Mock } // AppliesTo provides a mock function with given fields: -func (_m *ItemAction) AppliesTo() (velero.ResourceSelector, error) { +func (_m *BackupItemAction) AppliesTo() (velero.ResourceSelector, error) { ret := _m.Called() var r0 velero.ResourceSelector @@ -50,13 +52,13 @@ func (_m *ItemAction) AppliesTo() (velero.ResourceSelector, error) { return r0, r1 } -// Execute provides a mock function with given fields: item, _a1 -func (_m *ItemAction) Execute(item runtime.Unstructured, _a1 *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { - ret := _m.Called(item, _a1) +// Execute provides a mock function with given fields: item, backup +func (_m *BackupItemAction) Execute(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + ret := _m.Called(item, backup) var r0 runtime.Unstructured - if rf, ok := ret.Get(0).(func(runtime.Unstructured, *v1.Backup) runtime.Unstructured); ok { - r0 = rf(item, _a1) + if rf, ok := ret.Get(0).(func(runtime.Unstructured, *velerov1.Backup) runtime.Unstructured); ok { + r0 = rf(item, backup) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(runtime.Unstructured) @@ -64,8 +66,8 @@ func (_m *ItemAction) Execute(item runtime.Unstructured, _a1 *v1.Backup) (runtim } var r1 []velero.ResourceIdentifier - if rf, ok := ret.Get(1).(func(runtime.Unstructured, *v1.Backup) []velero.ResourceIdentifier); ok { - r1 = rf(item, _a1) + if rf, ok := ret.Get(1).(func(runtime.Unstructured, *velerov1.Backup) []velero.ResourceIdentifier); ok { + r1 = rf(item, backup) } else { if ret.Get(1) != nil { r1 = ret.Get(1).([]velero.ResourceIdentifier) @@ -73,8 +75,8 @@ func (_m *ItemAction) Execute(item runtime.Unstructured, _a1 *v1.Backup) (runtim } var r2 error - if rf, ok := ret.Get(2).(func(runtime.Unstructured, *v1.Backup) error); ok { - r2 = rf(item, _a1) + if rf, ok := ret.Get(2).(func(runtime.Unstructured, *velerov1.Backup) error); ok { + r2 = rf(item, backup) } else { r2 = ret.Error(2) } diff --git a/pkg/restore/change_pvc_node_selector.go b/pkg/restore/change_pvc_node_selector.go index b4d947c21f..d281d318f7 100644 --- a/pkg/restore/change_pvc_node_selector.go +++ b/pkg/restore/change_pvc_node_selector.go @@ -26,7 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -130,7 +130,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe func getNewNodeFromConfigMap(client corev1client.ConfigMapInterface, node string) (string, error) { // fetch node mapping from configMap - config, err := getPluginConfig(framework.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) + config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) if err != nil { return "", err } diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index 577ab28ec2..a02b7c484e 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -29,7 +29,7 @@ import ( corev1client "k8s.io/client-go/kubernetes/typed/core/v1" storagev1client "k8s.io/client-go/kubernetes/typed/storage/v1" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -69,7 +69,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut defer a.logger.Info("Done executing ChangeStorageClassAction") a.logger.Debug("Getting plugin config") - config, err := getPluginConfig(framework.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) + config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) if err != nil { return nil, err } diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index 586dbf7852..f2d8df9a1d 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -34,7 +34,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -107,7 +107,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu // TODO we might want/need to get plugin config at the top of this method at some point; for now, wait // until we know we're doing a restore before getting config. log.Debugf("Getting plugin config") - config, err := getPluginConfig(framework.PluginKindRestoreItemAction, "velero.io/restic", a.client) + config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/restic", a.client) if err != nil { return nil, err } @@ -261,7 +261,7 @@ func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (strin // TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple // plugins. -func getPluginConfig(kind framework.PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { +func getPluginConfig(kind common.PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { opts := metav1.ListOptions{ // velero.io/plugin-config: true // velero.io/restic: RestoreItemAction diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index a2794bdfc5..37b0f6b01a 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -226,12 +226,12 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( Includes(req.Restore.Spec.IncludedNamespaces...). Excludes(req.Restore.Spec.ExcludedNamespaces...) - resolvedActions, err := restoreItemActionResolver.ResolveActions(kr.discoveryHelper) + resolvedActions, err := restoreItemActionResolver.ResolveActions(kr.discoveryHelper, kr.logger) if err != nil { return Result{}, Result{Velero: []string{err.Error()}} } - resolvedItemSnapshotterActions, err := itemSnapshotterResolver.ResolveActions(kr.discoveryHelper) + resolvedItemSnapshotterActions, err := itemSnapshotterResolver.ResolveActions(kr.discoveryHelper, kr.logger) if err != nil { return Result{}, Result{Velero: []string{err.Error()}} } From d1347832825d96ec13eef5f182029aa226227813 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 16 Sep 2022 15:57:55 +0800 Subject: [PATCH 302/366] Clarify the help message for the default value of parameter --snapshot-volumes, when it's not set. Signed-off-by: Xun Jiang --- changelogs/unreleased/5350-blackpiglet | 1 + pkg/cmd/cli/backup/create.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5350-blackpiglet diff --git a/changelogs/unreleased/5350-blackpiglet b/changelogs/unreleased/5350-blackpiglet new file mode 100644 index 0000000000..c44dc8535f --- /dev/null +++ b/changelogs/unreleased/5350-blackpiglet @@ -0,0 +1 @@ +Clarify the help message for the default value of parameter --snapshot-volumes, when it's not set. \ No newline at end of file diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 3c2c23838d..0289524f51 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -124,7 +124,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.VarP(&o.Selector, "selector", "l", "Only back up resources matching this label selector.") flags.StringVar(&o.OrderedResources, "ordered-resources", "", "Mapping Kinds to an ordered list of specific resources of that Kind. Resource names are separated by commas and their names are in format 'namespace/resourcename'. For cluster scope resource, simply use resource name. Key-value pairs in the mapping are separated by semi-colon. Example: 'pods=ns1/pod1,ns1/pod2;persistentvolumeclaims=ns1/pvc4,ns1/pvc8'. Optional.") flags.DurationVar(&o.CSISnapshotTimeout, "csi-snapshot-timeout", o.CSISnapshotTimeout, "How long to wait for CSI snapshot creation before timeout.") - f := flags.VarPF(&o.SnapshotVolumes, "snapshot-volumes", "", "Take snapshots of PersistentVolumes as part of the backup.") + f := flags.VarPF(&o.SnapshotVolumes, "snapshot-volumes", "", "Take snapshots of PersistentVolumes as part of the backup. If the parameter is not set, it is treated as setting to 'true'.") // this allows the user to just specify "--snapshot-volumes" as shorthand for "--snapshot-volumes=true" // like a normal bool flag f.NoOptDefVal = "true" From 648d56e5410370508805171cf1e727f13f2eaecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Wed, 7 Sep 2022 19:39:31 -0400 Subject: [PATCH 303/366] Bump Go to 1.18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .github/workflows/crds-verify-kind.yaml | 2 +- .github/workflows/e2e-test-kind.yaml | 4 +- .github/workflows/pr-ci-check.yml | 2 +- .github/workflows/push.yml | 2 +- Dockerfile | 2 +- Tiltfile | 2 +- go.mod | 2 +- go.sum | 171 ------------------------ test/e2e/Makefile | 2 +- 9 files changed, 9 insertions(+), 180 deletions(-) diff --git a/.github/workflows/crds-verify-kind.yaml b/.github/workflows/crds-verify-kind.yaml index 398039f380..a7383a98bc 100644 --- a/.github/workflows/crds-verify-kind.yaml +++ b/.github/workflows/crds-verify-kind.yaml @@ -14,7 +14,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 id: go # Look for a CLI that's made for this PR - name: Fetch built CLI diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 3f079d3339..6047f076a3 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -14,7 +14,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 id: go # Look for a CLI that's made for this PR - name: Fetch built CLI @@ -76,7 +76,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Check out the code uses: actions/checkout@v2 diff --git a/.github/workflows/pr-ci-check.yml b/.github/workflows/pr-ci-check.yml index 834c3c589b..2b98f731fa 100644 --- a/.github/workflows/pr-ci-check.yml +++ b/.github/workflows/pr-ci-check.yml @@ -8,7 +8,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Check out the code uses: actions/checkout@v2 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 20da9f459f..060dda2bd7 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Check out code into the Go module directory diff --git a/Dockerfile b/Dockerfile index 8ffe8abff4..2eacd87baf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=$BUILDPLATFORM golang:1.17 as builder-env +FROM --platform=$BUILDPLATFORM golang:1.18 as builder-env ARG GOPROXY ARG PKG diff --git a/Tiltfile b/Tiltfile index 0d9a642632..2f62534e3a 100644 --- a/Tiltfile +++ b/Tiltfile @@ -50,7 +50,7 @@ git_sha = str(local("git rev-parse HEAD", quiet = True, echo_off = True)).strip( tilt_helper_dockerfile_header = """ # Tilt image -FROM golang:1.17 as tilt-helper +FROM golang:1.18 as tilt-helper # Support live reloading with Tilt RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \ diff --git a/go.mod b/go.mod index 6d36169039..7ec43d365b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/vmware-tanzu/velero -go 1.17 +go 1.18 require ( cloud.google.com/go/storage v1.21.0 diff --git a/go.sum b/go.sum index 8fa8ba1958..a7e9a3cfa9 100644 --- a/go.sum +++ b/go.sum @@ -58,7 +58,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14= cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v61.4.0+incompatible h1:BF2Pm3aQWIa6q9KmxyF1JYKYXtVw67vtvu2Wd54NGuY= @@ -116,9 +115,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= -github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -126,32 +122,19 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/kingpin v0.0.0-20200323085623-b6657d9477a6/go.mod h1:b6br6/pDFSfMkBgC96TbpOji05q5pa+v5rIlS0Y6XtI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -166,25 +149,18 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bombsimon/logrusr v1.1.0 h1:Y03FI4Z/Shyrc9jF26vuaUbnPxC5NMJnTtJA/3Lihq8= github.com/bombsimon/logrusr v1.1.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo= github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0= -github.com/chromedp/cdproto v0.0.0-20220321060548-7bc2623472b3/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= -github.com/chromedp/chromedp v0.8.0/go.mod h1:odCVV9o9i7HUKwHMFz9Y7T6s4Kbcz4GOyPlwKWopI9Q= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -198,7 +174,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -210,14 +185,10 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= -github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -234,16 +205,10 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -261,13 +226,9 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/foomo/htpasswd v0.0.0-20200116085101-e3a90e78da9c/go.mod h1:SHawtolbB0ZOFoRWgDwakX5WpwuIWAK88bUXVZqK0Ss= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8= -github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= @@ -281,7 +242,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -306,28 +266,21 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -362,12 +315,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/fswalker v0.2.1-0.20200214223026-f0e929ba4126/go.mod h1:ZSEBqY0IHKqWPeAbTyvccv9bb9vCnaQfHe31cm911Ng= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -406,7 +357,6 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/readahead v0.0.0-20161222183148-eaceba169032/go.mod h1:qYysrqQXuV4tzsizt4oOQ6mrBZQ0xnQXP3ylXX8Jk5Y= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -426,10 +376,6 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -441,11 +387,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hanwen/go-fuse/v2 v2.1.1-0.20220112183258-f57e95bda82d/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= @@ -460,7 +403,6 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -474,7 +416,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -483,11 +424,9 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -500,7 +439,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -512,12 +450,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= @@ -527,7 +461,6 @@ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kopia/htmluibuild v0.0.0-20220326183613-bbc499ed4dad/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw= github.com/kopia/kopia v0.10.7 h1:6s0ZIZW3Ge2ozzefddASy7CIUadp/5tF9yCDKQfAKKI= github.com/kopia/kopia v0.10.7/go.mod h1:0d9THPD+jwomPcXvPbCdmLyX6phQVP7AqcCcDEajfNA= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -544,14 +477,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -560,7 +488,6 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -580,12 +507,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.23 h1:NleyGQvAn9VQMU+YHVrgV4CX+EPtxPt/78lHOOTncy4= github.com/minio/minio-go/v7 v7.0.23/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -620,25 +545,16 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -646,84 +562,55 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -731,14 +618,10 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sanity-io/litter v1.5.4/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -748,12 +631,10 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -777,14 +658,10 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -793,15 +670,12 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/studio-b12/gowebdav v0.0.0-20211106090535-29e74efa701f/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vladimirvivien/gexe v0.1.1 h1:2A0SBaOSKH+cwLVdt6H+KkHZotZWRNLlWygANGw5DxE= github.com/vladimirvivien/gexe v0.1.1/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= github.com/vmware-tanzu/crash-diagnostics v0.3.7 h1:6gbv/3o1FzyRLS7Dz/+yVg1Lk1oRBQLyI3d1YTtlTT8= @@ -814,7 +688,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= @@ -825,7 +698,6 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= @@ -834,8 +706,6 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -861,8 +731,6 @@ go.starlark.net v0.0.0-20201006213952-227f4aabceb5 h1:ApvY/1gw+Yiqb/FKeks3KnVPWp go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -870,43 +738,32 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -932,18 +789,15 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -955,7 +809,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1044,7 +897,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1066,7 +918,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1096,7 +947,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1108,7 +958,6 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1118,7 +967,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1155,7 +1003,6 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1175,8 +1022,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1186,8 +1031,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1229,7 +1072,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1271,7 +1113,6 @@ google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1285,7 +1126,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1358,15 +1198,11 @@ google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1418,21 +1254,17 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216/go.mod h1:zJ2QpyDCYo1KvLXlmdnFlQAyF/Qfth0fB8239Qg7BIE= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1450,7 +1282,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1458,7 +1289,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.19.12/go.mod h1:EK+KvSq2urA6+CjVdZyAHEphXoLq2K2eW6lxOzTKSaY= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= @@ -1519,4 +1349,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 53d52364e7..46f4c552c2 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -98,7 +98,7 @@ STANDBY_CLUSTER ?= .PHONY:ginkgo ginkgo: # Make sure ginkgo is in $GOPATH/bin - go get github.com/onsi/ginkgo/ginkgo + go install github.com/onsi/ginkgo/ginkgo@v1.16.5 .PHONY: run run: ginkgo From dedb3e00987c3c400e96fbffd25afd108a5fb1bf Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 19 Sep 2022 11:39:11 +0800 Subject: [PATCH 304/366] fix issue 5352 Signed-off-by: Lyndon-Li --- pkg/podvolume/restorer_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pkg/podvolume/restorer_test.go b/pkg/podvolume/restorer_test.go index 2241884293..19e3ec44bd 100644 --- a/pkg/podvolume/restorer_test.go +++ b/pkg/podvolume/restorer_test.go @@ -28,6 +28,7 @@ func TestGetVolumesRepositoryType(t *testing.T) { volumes map[string]volumeBackupInfo expected string expectedErr string + prefixOnly bool }{ { name: "empty volume", @@ -65,7 +66,8 @@ func TestGetVolumesRepositoryType(t *testing.T) { "volume1": {"", "", "fake-type1"}, "volume2": {"fake-snapshot-id-2", "fake-uploader-2", "fake-type2"}, }, - expectedErr: "multiple repository type in one backup, current type fake-type1, differential one [type fake-type2, snapshot ID fake-snapshot-id-2, uploader fake-uploader-2]", + prefixOnly: true, + expectedErr: "multiple repository type in one backup", }, { name: "success", @@ -84,9 +86,17 @@ func TestGetVolumesRepositoryType(t *testing.T) { assert.Equal(t, tc.expected, actual) if err != nil { - assert.EqualError(t, err, tc.expectedErr) + if tc.prefixOnly { + errMsg := err.Error() + if len(errMsg) >= len(tc.expectedErr) { + errMsg = errMsg[0:len(tc.expectedErr)] + } + + assert.Equal(t, tc.expectedErr, errMsg) + } else { + assert.EqualError(t, err, tc.expectedErr) + } } }) - } } From 18bda60791309547ac2622e7f7db886c076e242e Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 19 Sep 2022 16:28:23 +0800 Subject: [PATCH 305/366] fix issue 5358 Signed-off-by: Lyndon-Li --- changelogs/unreleased/5359-lyndon | 1 + pkg/repository/backup_repo_op.go | 2 +- pkg/repository/backup_repo_op_test.go | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5359-lyndon diff --git a/changelogs/unreleased/5359-lyndon b/changelogs/unreleased/5359-lyndon new file mode 100644 index 0000000000..230ba3f724 --- /dev/null +++ b/changelogs/unreleased/5359-lyndon @@ -0,0 +1 @@ +Fix a repoEnsurer problem introduced by the refactor - The repoEnsurer didn't check "" state of BackupRepository, as a result, the function GetBackupRepository always returns without an error even though the ensreReady is specified. \ No newline at end of file diff --git a/pkg/repository/backup_repo_op.go b/pkg/repository/backup_repo_op.go index 237d8ddafb..3ae0f28121 100644 --- a/pkg/repository/backup_repo_op.go +++ b/pkg/repository/backup_repo_op.go @@ -83,7 +83,7 @@ func GetBackupRepository(ctx context.Context, cli client.Client, namespace strin return nil, errors.Errorf("backup repository is not ready: %s", repo.Status.Message) } - if repo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { + if repo.Status.Phase == "" || repo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { return nil, backupRepoNotProvisionedError } } diff --git a/pkg/repository/backup_repo_op_test.go b/pkg/repository/backup_repo_op_test.go index 104c4d6163..a317e22c2b 100644 --- a/pkg/repository/backup_repo_op_test.go +++ b/pkg/repository/backup_repo_op_test.go @@ -96,6 +96,14 @@ func TestGetBackupRepository(t *testing.T) { backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, expected: buildBackupRepoPointer(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, velerov1api.BackupRepositoryPhaseNew, "02"), }, + { + name: "repository state is empty, not expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, "", "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + expected: buildBackupRepoPointer(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, "", "02"), + }, { name: "repository not ready, expect ready", backupRepositories: []velerov1api.BackupRepository{ @@ -114,6 +122,15 @@ func TestGetBackupRepository(t *testing.T) { ensureReady: true, expectedErr: "backup repository not provisioned", }, + { + name: "repository state is empty, expect ready", + backupRepositories: []velerov1api.BackupRepository{ + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-01", "fake-bsl-01", "fake-repository-type-01"}, velerov1api.BackupRepositoryPhaseReady, "01"), + buildBackupRepo(BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, "", "02")}, + backupRepositoryKey: BackupRepositoryKey{"fake-volume-ns-02", "fake-bsl-02", "fake-repository-type-02"}, + ensureReady: true, + expectedErr: "backup repository not provisioned", + }, { name: "repository ready, expect ready", backupRepositories: []velerov1api.BackupRepository{ @@ -135,7 +152,7 @@ func TestGetBackupRepository(t *testing.T) { backupRepo, err := GetBackupRepository(context.Background(), fakeClient, velerov1api.DefaultNamespace, tc.backupRepositoryKey, tc.ensureReady) - if backupRepo != nil { + if backupRepo != nil && tc.expected != nil { backupRepo.ResourceVersion = tc.expected.ResourceVersion require.Equal(t, *tc.expected, *backupRepo) } else { From f51c8bf44bd9f908945f59bb62951532b8ef332e Mon Sep 17 00:00:00 2001 From: cleverhu Date: Sat, 17 Sep 2022 20:05:29 +0800 Subject: [PATCH 306/366] add useOwnerReferencesInBackup field doc Signed-off-by: cleverhu --- changelogs/unreleased/5353-cleverhu | 1 + site/content/docs/main/api-types/schedule.md | 3 +++ site/content/docs/v1.6/api-types/schedule.md | 3 +++ site/content/docs/v1.7/api-types/schedule.md | 3 +++ site/content/docs/v1.8/api-types/schedule.md | 3 +++ site/content/docs/v1.9/api-types/schedule.md | 3 +++ 6 files changed, 16 insertions(+) create mode 100644 changelogs/unreleased/5353-cleverhu diff --git a/changelogs/unreleased/5353-cleverhu b/changelogs/unreleased/5353-cleverhu new file mode 100644 index 0000000000..91e8153f8e --- /dev/null +++ b/changelogs/unreleased/5353-cleverhu @@ -0,0 +1 @@ +Add useOwnerReferencesInBackup field doc for schedule. \ No newline at end of file diff --git a/site/content/docs/main/api-types/schedule.md b/site/content/docs/main/api-types/schedule.md index 5bc430c8ff..31130f43ae 100644 --- a/site/content/docs/main/api-types/schedule.md +++ b/site/content/docs/main/api-types/schedule.md @@ -136,6 +136,9 @@ spec: # processed. Only "exec" hooks are supported. post: # Same content as pre above. + # Specifies whether to use OwnerReferences on backups created by this Schedule. + # Notice: if set to true, when schedule is deleted, backups will be deleted too. Optional. + useOwnerReferencesInBackup: false status: # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. phase: "" diff --git a/site/content/docs/v1.6/api-types/schedule.md b/site/content/docs/v1.6/api-types/schedule.md index 8ccc299698..3d77a05a29 100644 --- a/site/content/docs/v1.6/api-types/schedule.md +++ b/site/content/docs/v1.6/api-types/schedule.md @@ -125,6 +125,9 @@ spec: # processed. Only "exec" hooks are supported. post: # Same content as pre above. + # Specifies whether to use OwnerReferences on backups created by this Schedule. + # Notice: if set to true, when schedule is deleted, backups will be deleted too. Optional. + useOwnerReferencesInBackup: false status: # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. phase: "" diff --git a/site/content/docs/v1.7/api-types/schedule.md b/site/content/docs/v1.7/api-types/schedule.md index 3c98bef16d..57c1539c8a 100644 --- a/site/content/docs/v1.7/api-types/schedule.md +++ b/site/content/docs/v1.7/api-types/schedule.md @@ -130,6 +130,9 @@ spec: # processed. Only "exec" hooks are supported. post: # Same content as pre above. + # Specifies whether to use OwnerReferences on backups created by this Schedule. + # Notice: if set to true, when schedule is deleted, backups will be deleted too. Optional. + useOwnerReferencesInBackup: false status: # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. phase: "" diff --git a/site/content/docs/v1.8/api-types/schedule.md b/site/content/docs/v1.8/api-types/schedule.md index 9c5dc26cee..0c7e9fffcb 100644 --- a/site/content/docs/v1.8/api-types/schedule.md +++ b/site/content/docs/v1.8/api-types/schedule.md @@ -132,6 +132,9 @@ spec: # processed. Only "exec" hooks are supported. post: # Same content as pre above. + # Specifies whether to use OwnerReferences on backups created by this Schedule. + # Notice: if set to true, when schedule is deleted, backups will be deleted too. Optional. + useOwnerReferencesInBackup: false status: # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. phase: "" diff --git a/site/content/docs/v1.9/api-types/schedule.md b/site/content/docs/v1.9/api-types/schedule.md index ccc441999d..69ed033816 100644 --- a/site/content/docs/v1.9/api-types/schedule.md +++ b/site/content/docs/v1.9/api-types/schedule.md @@ -137,6 +137,9 @@ spec: # processed. Only "exec" hooks are supported. post: # Same content as pre above. + # Specifies whether to use OwnerReferences on backups created by this Schedule. + # Notice: if set to true, when schedule is deleted, backups will be deleted too. Optional. + useOwnerReferencesInBackup: false status: # The current phase of the latest scheduled backup. Valid values are New, FailedValidation, InProgress, Completed, PartiallyFailed, Failed. phase: "" From a5f1e7ac118ee8b94a2006a62862324afd0fa443 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Tue, 20 Sep 2022 11:26:03 +0800 Subject: [PATCH 307/366] Added backupController's UT to test the prepareBackupRequest() method BackupStorageLocation processing logic Signed-off-by: Niu Lechuan --- pkg/controller/backup_controller_test.go | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 20e340df06..4768757bb1 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -33,6 +33,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/version" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -287,6 +288,117 @@ func TestBackupLocationLabel(t *testing.T) { } } +func Test_prepareBackupRequest_BackupStorageLocation(t *testing.T) { + var ( + defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour} + defaultBackupLocation = "default-location" + ) + + now, err := time.Parse(time.RFC1123Z, time.RFC1123Z) + require.NoError(t, err) + + tests := []struct { + name string + backup *velerov1api.Backup + backupLocationNameInBackup string + backupLocationInApiServer *velerov1api.BackupStorageLocation + defaultBackupLocationInApiServer *velerov1api.BackupStorageLocation + expectedBackupLocation string + expectedSuccess bool + expectedValidationError string + }{ + { + name: "BackupLocation is specified in backup CR'spec and it can be found in ApiServer", + backup: builder.ForBackup("velero", "backup-1").Result(), + backupLocationNameInBackup: "test-backup-location", + backupLocationInApiServer: builder.ForBackupStorageLocation("velero", "test-backup-location").Result(), + defaultBackupLocationInApiServer: builder.ForBackupStorageLocation("velero", "default-location").Result(), + expectedBackupLocation: "test-backup-location", + expectedSuccess: true, + }, + { + name: "BackupLocation is specified in backup CR'spec and it can't be found in ApiServer", + backup: builder.ForBackup("velero", "backup-1").Result(), + backupLocationNameInBackup: "test-backup-location", + backupLocationInApiServer: nil, + defaultBackupLocationInApiServer: nil, + expectedSuccess: false, + expectedValidationError: "an existing backup storage location wasn't specified at backup creation time and the default 'test-backup-location' wasn't found. Please address this issue (see `velero backup-location -h` for options) and create a new backup. Error: backupstoragelocations.velero.io \"test-backup-location\" not found", + }, + { + name: "Using default BackupLocation and it can be found in ApiServer", + backup: builder.ForBackup("velero", "backup-1").Result(), + backupLocationNameInBackup: "", + backupLocationInApiServer: builder.ForBackupStorageLocation("velero", "test-backup-location").Result(), + defaultBackupLocationInApiServer: builder.ForBackupStorageLocation("velero", "default-location").Result(), + expectedBackupLocation: defaultBackupLocation, + expectedSuccess: true, + }, + { + name: "Using default BackupLocation and it can't be found in ApiServer", + backup: builder.ForBackup("velero", "backup-1").Result(), + backupLocationNameInBackup: "", + backupLocationInApiServer: nil, + defaultBackupLocationInApiServer: nil, + expectedSuccess: false, + expectedValidationError: fmt.Sprintf("an existing backup storage location wasn't specified at backup creation time and the server default '%s' doesn't exist. Please address this issue (see `velero backup-location -h` for options) and create a new backup. Error: backupstoragelocations.velero.io \"%s\" not found", defaultBackupLocation, defaultBackupLocation), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Arrange + var ( + formatFlag = logging.FormatText + logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) + apiServer = velerotest.NewAPIServer(t) + sharedInformers = informers.NewSharedInformerFactory(apiServer.VeleroClient, 0) + ) + + // objects that should init with client + objects := make([]runtime.Object, 0) + if test.backupLocationInApiServer != nil { + objects = append(objects, test.backupLocationInApiServer) + } + if test.defaultBackupLocationInApiServer != nil { + objects = append(objects, test.defaultBackupLocationInApiServer) + } + fakeClient := velerotest.NewFakeControllerRuntimeClient(t, objects...) + + discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) + require.NoError(t, err) + + c := &backupController{ + genericController: newGenericController("backup-test", logger), + discoveryHelper: discoveryHelper, + defaultBackupLocation: defaultBackupLocation, + kbClient: fakeClient, + snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), + defaultBackupTTL: defaultBackupTTL.Duration, + clock: clock.NewFakeClock(now), + formatFlag: formatFlag, + } + + test.backup.Spec.StorageLocation = test.backupLocationNameInBackup + + // Run + res := c.prepareBackupRequest(test.backup) + + // Assert + if test.expectedSuccess { + assert.Equal(t, test.expectedBackupLocation, res.Spec.StorageLocation) + assert.NotNil(t, res) + } else { + // in every test case, we only trigger one error at once + if len(res.Status.ValidationErrors) > 1 { + assert.Fail(t, "multi error found in request") + } + assert.Equal(t, test.expectedValidationError, res.Status.ValidationErrors[0]) + } + }) + } +} + func TestDefaultBackupTTL(t *testing.T) { var ( defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour} From 32ef20d31794a07e49ca87ccb5f061f858a355a7 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 7 Sep 2022 18:29:37 -0400 Subject: [PATCH 308/366] RestoreItemAction v1 refactoring for plugin api versioning Signed-off-by: Scott Seago --- changelogs/unreleased/5312-sseago | 1 + .../restartabletest/restartable_delegate.go | 52 +++---- pkg/controller/restore_controller_test.go | 4 +- .../v1/restartable_backup_item_action_test.go | 29 ++-- pkg/plugin/clientmgmt/manager.go | 29 ++-- pkg/plugin/clientmgmt/manager_test.go | 59 +++----- .../clientmgmt/restartable_delegate_test.go | 129 ------------------ .../restartable_delete_item_action_test.go | 29 ++-- .../restartable_item_snapshotter_test.go | 79 +++++------ .../restartable_object_store_test.go | 73 +++++----- .../restartable_volume_snapshotter_test.go | 73 +++++----- .../v1}/restartable_restore_item_action.go | 56 +++++--- .../restartable_restore_item_action_test.go | 50 +++---- pkg/plugin/framework/action_resolver.go | 7 +- .../framework/restore_item_action_client.go | 7 +- .../framework/restore_item_action_server.go | 7 +- pkg/plugin/mocks/manager.go | 120 ++++++++-------- .../v1/RestoreItemAction.go} | 25 ++-- .../v1}/restore_item_action.go | 9 +- pkg/restore/add_pv_from_pvc_action.go | 7 +- pkg/restore/add_pv_from_pvc_action_test.go | 3 +- pkg/restore/add_pvc_from_pod_action.go | 5 +- pkg/restore/add_pvc_from_pod_action_test.go | 3 +- pkg/restore/admissionwebhook_config_action.go | 9 +- .../admissionwebhook_config_action_test.go | 4 +- pkg/restore/apiservice_action.go | 5 +- pkg/restore/apiservice_action_test.go | 4 +- pkg/restore/change_pvc_node_selector.go | 15 +- pkg/restore/change_pvc_node_selector_test.go | 4 +- pkg/restore/change_storageclass_action.go | 9 +- .../change_storageclass_action_test.go | 4 +- pkg/restore/clusterrolebinding_action.go | 7 +- pkg/restore/clusterrolebinding_action_test.go | 3 +- .../crd_v1_preserve_unknown_fields_action.go | 7 +- ..._v1_preserve_unknown_fields_action_test.go | 4 +- pkg/restore/init_restorehook_pod_action.go | 5 +- .../init_restorehook_pod_action_test.go | 4 +- pkg/restore/job_action.go | 5 +- pkg/restore/job_action_test.go | 4 +- pkg/restore/pod_action.go | 5 +- pkg/restore/pod_action_test.go | 3 +- pkg/restore/restic_restore_action.go | 7 +- pkg/restore/restic_restore_action_test.go | 4 +- pkg/restore/restore.go | 7 +- pkg/restore/restore_test.go | 59 ++++---- pkg/restore/rolebinding_action.go | 7 +- pkg/restore/rolebinding_action_test.go | 3 +- pkg/restore/service_account_action.go | 5 +- pkg/restore/service_account_action_test.go | 3 +- pkg/restore/service_action.go | 5 +- pkg/restore/service_action_test.go | 4 +- 51 files changed, 487 insertions(+), 574 deletions(-) create mode 100644 changelogs/unreleased/5312-sseago rename pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go => internal/restartabletest/restartable_delegate.go (74%) delete mode 100644 pkg/plugin/clientmgmt/restartable_delegate_test.go rename pkg/plugin/clientmgmt/{ => restoreitemaction/v1}/restartable_restore_item_action.go (52%) rename pkg/plugin/clientmgmt/{ => restoreitemaction/v1}/restartable_restore_item_action_test.go (72%) rename pkg/{restore/mocks/item_action.go => plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go} (60%) rename pkg/plugin/velero/{ => restoreitemaction/v1}/restore_item_action.go (94%) diff --git a/changelogs/unreleased/5312-sseago b/changelogs/unreleased/5312-sseago new file mode 100644 index 0000000000..2856fc123f --- /dev/null +++ b/changelogs/unreleased/5312-sseago @@ -0,0 +1 @@ +RestoreItemAction v1 refactoring for plugin api versioning diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go b/internal/restartabletest/restartable_delegate.go similarity index 74% rename from pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go rename to internal/restartabletest/restartable_delegate.go index 6745b8fa0b..f91a3eb37f 100644 --- a/pkg/plugin/clientmgmt/backupitemaction/v1/shared_test.go +++ b/internal/restartabletest/restartable_delegate.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package restartabletest import ( "reflect" @@ -28,56 +28,56 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ) -type mockRestartableProcess struct { +type MockRestartableProcess struct { mock.Mock } -func (rp *mockRestartableProcess) AddReinitializer(key process.KindAndName, r process.Reinitializer) { +func (rp *MockRestartableProcess) AddReinitializer(key process.KindAndName, r process.Reinitializer) { rp.Called(key, r) } -func (rp *mockRestartableProcess) Reset() error { +func (rp *MockRestartableProcess) Reset() error { args := rp.Called() return args.Error(0) } -func (rp *mockRestartableProcess) ResetIfNeeded() error { +func (rp *MockRestartableProcess) ResetIfNeeded() error { args := rp.Called() return args.Error(0) } -func (rp *mockRestartableProcess) GetByKindAndName(key process.KindAndName) (interface{}, error) { +func (rp *MockRestartableProcess) GetByKindAndName(key process.KindAndName) (interface{}, error) { args := rp.Called(key) return args.Get(0), args.Error(1) } -func (rp *mockRestartableProcess) Stop() { +func (rp *MockRestartableProcess) Stop() { rp.Called() } -type restartableDelegateTest struct { - function string - inputs []interface{} - expectedErrorOutputs []interface{} - expectedDelegateOutputs []interface{} +type RestartableDelegateTest struct { + Function string + Inputs []interface{} + ExpectedErrorOutputs []interface{} + ExpectedDelegateOutputs []interface{} } -type mockable interface { +type Mockable interface { Test(t mock.TestingT) On(method string, args ...interface{}) *mock.Call AssertExpectations(t mock.TestingT) bool } -func runRestartableDelegateTests( +func RunRestartableDelegateTests( t *testing.T, kind common.PluginKind, newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, - newMock func() mockable, - tests ...restartableDelegateTest, + newMock func() Mockable, + tests ...RestartableDelegateTest, ) { for _, tc := range tests { - t.Run(tc.function, func(t *testing.T) { - p := new(mockRestartableProcess) + t.Run(tc.Function, func(t *testing.T) { + p := new(MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -88,19 +88,19 @@ func runRestartableDelegateTests( r := newRestartable(key, p) // Get the method we're going to call using reflection - method := reflect.ValueOf(r).MethodByName(tc.function) + method := reflect.ValueOf(r).MethodByName(tc.Function) require.NotEmpty(t, method) // Convert the test case inputs ([]interface{}) to []reflect.Value var inputValues []reflect.Value - for i := range tc.inputs { - inputValues = append(inputValues, reflect.ValueOf(tc.inputs[i])) + for i := range tc.Inputs { + inputValues = append(inputValues, reflect.ValueOf(tc.Inputs[i])) } // Invoke the method being tested actual := method.Call(inputValues) - // This function asserts that the actual outputs match the expected outputs + // This Function asserts that the actual outputs match the expected outputs checkOutputs := func(expected []interface{}, actual []reflect.Value) { require.Equal(t, len(expected), len(actual)) @@ -118,7 +118,7 @@ func runRestartableDelegateTests( continue } - // If function returns nil as struct return type, we cannot just + // If Function returns nil as struct return type, we cannot just // compare the interface to nil as its type will not be nil, // only the value will be if expected[i] == nil && reflect.ValueOf(a).Kind() == reflect.Ptr { @@ -132,7 +132,7 @@ func runRestartableDelegateTests( } // Make sure we get what we expected when getDelegate returned an error - checkOutputs(tc.expectedErrorOutputs, actual) + checkOutputs(tc.ExpectedErrorOutputs, actual) // Invoke delegate, make sure all returned values are passed through p.On("ResetIfNeeded").Return(nil) @@ -144,13 +144,13 @@ func runRestartableDelegateTests( p.On("GetByKindAndName", key).Return(delegate, nil) // Set up the mocked method in the delegate - delegate.On(tc.function, tc.inputs...).Return(tc.expectedDelegateOutputs...) + delegate.On(tc.Function, tc.Inputs...).Return(tc.ExpectedDelegateOutputs...) // Invoke the method being tested actual = method.Call(inputValues) // Make sure we get what we expected when invoking the delegate - checkOutputs(tc.expectedDelegateOutputs, actual) + checkOutputs(tc.ExpectedDelegateOutputs, actual) }) } } diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 16dd444e94..fff95340e5 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -46,8 +46,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/framework" pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" pkgrestore "github.com/vmware-tanzu/velero/pkg/restore" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -861,7 +861,7 @@ type fakeRestorer struct { func (r *fakeRestorer) Restore( info pkgrestore.Request, - actions []velero.RestoreItemAction, + actions []riav1.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter pkgrestore.VolumeSnapshotterGetter, ) (pkgrestore.Result, pkgrestore.Result) { diff --git a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go index d20b150d96..53bf0e4de8 100644 --- a/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go +++ b/pkg/plugin/clientmgmt/backupitemaction/v1/restartable_backup_item_action_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/vmware-tanzu/velero/internal/restartabletest" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" @@ -57,7 +58,7 @@ func TestRestartableGetBackupItemAction(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) name := "pod" @@ -78,7 +79,7 @@ func TestRestartableGetBackupItemAction(t *testing.T) { } func TestRestartableBackupItemActionGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) // Reset error @@ -121,7 +122,7 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) { }, } - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindBackupItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { @@ -130,20 +131,20 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) { SharedPluginProcess: p, } }, - func() mockable { + func() restartabletest.Mockable { return new(mocks.BackupItemAction) }, - restartableDelegateTest{ - function: "AppliesTo", - inputs: []interface{}{}, - expectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "AppliesTo", + Inputs: []interface{}{}, + ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "Execute", - inputs: []interface{}{pv, b}, - expectedErrorOutputs: []interface{}{nil, ([]velero.ResourceIdentifier)(nil), errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{pvToReturn, additionalItems, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "Execute", + Inputs: []interface{}{pv, b}, + ExpectedErrorOutputs: []interface{}{nil, ([]velero.ResourceIdentifier)(nil), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{pvToReturn, additionalItems, errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index 292e8e9c2b..e5442ffd32 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -26,10 +26,12 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" + riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // Manager manages the lifecycles of plugins. @@ -47,10 +49,10 @@ type Manager interface { GetBackupItemAction(name string) (biav1.BackupItemAction, error) // GetRestoreItemActions returns all restore item action plugins. - GetRestoreItemActions() ([]velero.RestoreItemAction, error) + GetRestoreItemActions() ([]riav1.RestoreItemAction, error) // GetRestoreItemAction returns the restore item action plugin for name. - GetRestoreItemAction(name string) (velero.RestoreItemAction, error) + GetRestoreItemAction(name string) (riav1.RestoreItemAction, error) // GetDeleteItemActions returns all delete item action plugins. GetDeleteItemActions() ([]velero.DeleteItemAction, error) @@ -211,10 +213,10 @@ func (m *manager) GetBackupItemAction(name string) (biav1.BackupItemAction, erro } // GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions. -func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { +func (m *manager) GetRestoreItemActions() ([]riav1.RestoreItemAction, error) { list := m.registry.List(common.PluginKindRestoreItemAction) - actions := make([]velero.RestoreItemAction, 0, len(list)) + actions := make([]riav1.RestoreItemAction, 0, len(list)) for i := range list { id := list[i] @@ -231,16 +233,21 @@ func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { } // GetRestoreItemAction returns a restartableRestoreItemAction for name. -func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) { +func (m *manager) GetRestoreItemAction(name string) (riav1.RestoreItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(common.PluginKindRestoreItemAction, name) - if err != nil { - return nil, err + for _, adaptedRestoreItemAction := range riav1cli.AdaptedRestoreItemActions() { + restartableProcess, err := m.getRestartableProcess(adaptedRestoreItemAction.Kind, name) + // Check if plugin was not found + if errors.As(err, &pluginNotFoundErrType) { + continue + } + if err != nil { + return nil, err + } + return adaptedRestoreItemAction.GetRestartable(name, restartableProcess), nil } - - r := NewRestartableRestoreItemAction(name, restartableProcess) - return r, nil + return nil, fmt.Errorf("unable to get valid RestoreItemAction for %q", name) } // GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions. diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index b5b54b3f49..9796dcac87 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -26,8 +26,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/vmware-tanzu/velero/internal/restartabletest" biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" + riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/test" @@ -84,33 +86,6 @@ func (f *mockRestartableProcessFactory) NewRestartableProcess(command string, lo return rp, args.Error(1) } -type mockRestartableProcess struct { - mock.Mock -} - -func (rp *mockRestartableProcess) AddReinitializer(key process.KindAndName, r process.Reinitializer) { - rp.Called(key, r) -} - -func (rp *mockRestartableProcess) Reset() error { - args := rp.Called() - return args.Error(0) -} - -func (rp *mockRestartableProcess) ResetIfNeeded() error { - args := rp.Called() - return args.Error(0) -} - -func (rp *mockRestartableProcess) GetByKindAndName(key process.KindAndName) (interface{}, error) { - args := rp.Called(key) - return args.Get(0), args.Error(1) -} - -func (rp *mockRestartableProcess) Stop() { - rp.Called() -} - func TestGetRestartableProcess(t *testing.T) { logger := test.NewLogger() logLevel := logrus.InfoLevel @@ -144,7 +119,7 @@ func TestGetRestartableProcess(t *testing.T) { assert.EqualError(t, err, "factory") // Test 3: registry ok, factory ok - restartableProcess := &mockRestartableProcess{} + restartableProcess := &restartabletest.MockRestartableProcess{} defer restartableProcess.AssertExpectations(t) factory.On("NewRestartableProcess", podID.Command, logger, logLevel).Return(restartableProcess, nil).Once() rp, err = m.getRestartableProcess(pluginKind, pluginName) @@ -167,7 +142,7 @@ func TestCleanupClients(t *testing.T) { m := NewManager(logger, logLevel, registry).(*manager) for i := 0; i < 5; i++ { - rp := &mockRestartableProcess{} + rp := &restartabletest.MockRestartableProcess{} defer rp.AssertExpectations(t) rp.On("Stop") m.restartableProcesses[fmt.Sprintf("rp%d", i)] = rp @@ -235,9 +210,9 @@ func TestGetRestoreItemAction(t *testing.T) { return m.GetRestoreItemAction(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { - return &restartableRestoreItemAction{ - key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, - sharedPluginProcess: sharedPluginProcess, + return &riav1cli.RestartableRestoreItemAction{ + Key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, } }, false, @@ -272,7 +247,7 @@ func getPluginTest( } registry.On("Get", pluginKind, pluginName).Return(pluginID, nil) - restartableProcess := &mockRestartableProcess{} + restartableProcess := &restartabletest.MockRestartableProcess{} defer restartableProcess.AssertExpectations(t) // Test 1: error getting restartable process @@ -349,7 +324,7 @@ func TestGetBackupItemActions(t *testing.T) { registry.On("Get", pluginKind, pluginName).Return(pluginID, nil) - restartableProcess := &mockRestartableProcess{} + restartableProcess := &restartabletest.MockRestartableProcess{} defer restartableProcess.AssertExpectations(t) expected := &biav1cli.RestartableBackupItemAction{ @@ -441,12 +416,12 @@ func TestGetRestoreItemActions(t *testing.T) { registry.On("Get", pluginKind, pluginName).Return(pluginID, nil) - restartableProcess := &mockRestartableProcess{} + restartableProcess := &restartabletest.MockRestartableProcess{} defer restartableProcess.AssertExpectations(t) - expected := &restartableRestoreItemAction{ - key: process.KindAndName{Kind: pluginKind, Name: pluginName}, - sharedPluginProcess: restartableProcess, + expected := &riav1cli.RestartableRestoreItemAction{ + Key: process.KindAndName{Kind: pluginKind, Name: pluginName}, + SharedPluginProcess: restartableProcess, } if tc.newRestartableProcessError != nil { @@ -540,12 +515,12 @@ func TestGetDeleteItemActions(t *testing.T) { registry.On("Get", pluginKind, pluginName).Return(pluginID, nil) - restartableProcess := &mockRestartableProcess{} + restartableProcess := &restartabletest.MockRestartableProcess{} defer restartableProcess.AssertExpectations(t) - expected := &restartableRestoreItemAction{ - key: process.KindAndName{Kind: pluginKind, Name: pluginName}, - sharedPluginProcess: restartableProcess, + expected := &riav1cli.RestartableRestoreItemAction{ + Key: process.KindAndName{Kind: pluginKind, Name: pluginName}, + SharedPluginProcess: restartableProcess, } if tc.newRestartableProcessError != nil { diff --git a/pkg/plugin/clientmgmt/restartable_delegate_test.go b/pkg/plugin/clientmgmt/restartable_delegate_test.go deleted file mode 100644 index a69f2e05d4..0000000000 --- a/pkg/plugin/clientmgmt/restartable_delegate_test.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright 2018 the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package clientmgmt - -import ( - "reflect" - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" - "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" -) - -type restartableDelegateTest struct { - function string - inputs []interface{} - expectedErrorOutputs []interface{} - expectedDelegateOutputs []interface{} -} - -type mockable interface { - Test(t mock.TestingT) - On(method string, args ...interface{}) *mock.Call - AssertExpectations(t mock.TestingT) bool -} - -func runRestartableDelegateTests( - t *testing.T, - kind common.PluginKind, - newRestartable func(key process.KindAndName, p process.RestartableProcess) interface{}, - newMock func() mockable, - tests ...restartableDelegateTest, -) { - for _, tc := range tests { - t.Run(tc.function, func(t *testing.T) { - p := new(mockRestartableProcess) - p.Test(t) - defer p.AssertExpectations(t) - - // getDelegate error - p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() - name := "delegateName" - key := process.KindAndName{Kind: kind, Name: name} - r := newRestartable(key, p) - - // Get the method we're going to call using reflection - method := reflect.ValueOf(r).MethodByName(tc.function) - require.NotEmpty(t, method) - - // Convert the test case inputs ([]interface{}) to []reflect.Value - var inputValues []reflect.Value - for i := range tc.inputs { - inputValues = append(inputValues, reflect.ValueOf(tc.inputs[i])) - } - - // Invoke the method being tested - actual := method.Call(inputValues) - - // This function asserts that the actual outputs match the expected outputs - checkOutputs := func(expected []interface{}, actual []reflect.Value) { - require.Equal(t, len(expected), len(actual)) - - for i := range actual { - // Get the underlying value from the reflect.Value - a := actual[i].Interface() - - // Check if it's an error - actualErr, actualErrOk := a.(error) - // Check if the expected output element is an error - expectedErr, expectedErrOk := expected[i].(error) - // If both are errors, use EqualError - if actualErrOk && expectedErrOk { - assert.EqualError(t, actualErr, expectedErr.Error()) - continue - } - - // If function returns nil as struct return type, we cannot just - // compare the interface to nil as its type will not be nil, - // only the value will be - if expected[i] == nil && reflect.ValueOf(a).Kind() == reflect.Ptr { - assert.True(t, reflect.ValueOf(a).IsNil()) - continue - } - - // Otherwise, use plain Equal - assert.Equal(t, expected[i], a) - } - } - - // Make sure we get what we expected when getDelegate returned an error - checkOutputs(tc.expectedErrorOutputs, actual) - - // Invoke delegate, make sure all returned values are passed through - p.On("ResetIfNeeded").Return(nil) - - delegate := newMock() - delegate.Test(t) - defer delegate.AssertExpectations(t) - - p.On("GetByKindAndName", key).Return(delegate, nil) - - // Set up the mocked method in the delegate - delegate.On(tc.function, tc.inputs...).Return(tc.expectedDelegateOutputs...) - - // Invoke the method being tested - actual = method.Call(inputValues) - - // Make sure we get what we expected when invoking the delegate - checkOutputs(tc.expectedDelegateOutputs, actual) - }) - } -} diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go index bd5d734a45..fae5444aca 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/internal/restartabletest" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" @@ -56,7 +57,7 @@ func TestRestartableGetDeleteItemAction(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) name := "pod" @@ -77,7 +78,7 @@ func TestRestartableGetDeleteItemAction(t *testing.T) { } func TestRestartableDeleteItemActionGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) // Reset error @@ -114,7 +115,7 @@ func TestRestartableDeleteItemActionDelegatedFunctions(t *testing.T) { Backup: backup, } - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindDeleteItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { @@ -123,21 +124,21 @@ func TestRestartableDeleteItemActionDelegatedFunctions(t *testing.T) { sharedPluginProcess: p, } }, - func() mockable { + func() restartabletest.Mockable { // Currently broken because this mocks the restore item action interface return new(mocks.DeleteItemAction) }, - restartableDelegateTest{ - function: "AppliesTo", - inputs: []interface{}{}, - expectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "AppliesTo", + Inputs: []interface{}{}, + ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "Execute", - inputs: []interface{}{input}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "Execute", + Inputs: []interface{}{input}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go index 5aeca0551d..29805bc424 100644 --- a/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_item_snapshotter_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/vmware-tanzu/velero/internal/restartabletest" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1/mocks" @@ -61,7 +62,7 @@ func TestRestartableGetItemSnapshotter(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) name := "pvc" @@ -82,7 +83,7 @@ func TestRestartableGetItemSnapshotter(t *testing.T) { } func TestRestartableItemSnapshotterGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) // Reset error @@ -175,7 +176,7 @@ func TestRestartableItemSnasphotterDelegatedFunctions(t *testing.T) { SnapshotMetadata: nil, Params: nil, } - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindItemSnapshotter, func(key process.KindAndName, p process.RestartableProcess) interface{} { @@ -184,50 +185,50 @@ func TestRestartableItemSnasphotterDelegatedFunctions(t *testing.T) { sharedPluginProcess: p, } }, - func() mockable { + func() restartabletest.Mockable { return new(mocks.ItemSnapshotter) }, - restartableDelegateTest{ - function: "Init", - inputs: []interface{}{map[string]string{}}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "Init", + Inputs: []interface{}{map[string]string{}}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "AppliesTo", - inputs: []interface{}{}, - expectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "AppliesTo", + Inputs: []interface{}{}, + ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "AlsoHandles", - inputs: []interface{}{&isv1.AlsoHandlesInput{}}, - expectedErrorOutputs: []interface{}{[]velero.ResourceIdentifier([]velero.ResourceIdentifier(nil)), errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{[]velero.ResourceIdentifier([]velero.ResourceIdentifier(nil)), errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "AlsoHandles", + Inputs: []interface{}{&isv1.AlsoHandlesInput{}}, + ExpectedErrorOutputs: []interface{}{[]velero.ResourceIdentifier([]velero.ResourceIdentifier(nil)), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{[]velero.ResourceIdentifier([]velero.ResourceIdentifier(nil)), errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "SnapshotItem", - inputs: []interface{}{ctx, sii}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{sio, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "SnapshotItem", + Inputs: []interface{}{ctx, sii}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{sio, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "CreateItemFromSnapshot", - inputs: []interface{}{ctx, cii}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{cio, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "CreateItemFromSnapshot", + Inputs: []interface{}{ctx, cii}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{cio, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "Progress", - inputs: []interface{}{pi}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{po, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "Progress", + Inputs: []interface{}{pi}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{po, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "DeleteSnapshot", - inputs: []interface{}{ctx, dsi}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "DeleteSnapshot", + Inputs: []interface{}{ctx, dsi}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/clientmgmt/restartable_object_store_test.go b/pkg/plugin/clientmgmt/restartable_object_store_test.go index 47699c50bc..2c6bc0fdce 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store_test.go +++ b/pkg/plugin/clientmgmt/restartable_object_store_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vmware-tanzu/velero/internal/restartabletest" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" @@ -56,7 +57,7 @@ func TestRestartableGetObjectStore(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -81,7 +82,7 @@ func TestRestartableGetObjectStore(t *testing.T) { } func TestRestartableObjectStoreReinitialize(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -112,7 +113,7 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) { } func TestRestartableObjectStoreGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -141,7 +142,7 @@ func TestRestartableObjectStoreGetDelegate(t *testing.T) { } func TestRestartableObjectStoreInit(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -185,7 +186,7 @@ func TestRestartableObjectStoreInit(t *testing.T) { } func TestRestartableObjectStoreDelegatedFunctions(t *testing.T) { - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindObjectStore, func(key process.KindAndName, p process.RestartableProcess) interface{} { @@ -194,44 +195,44 @@ func TestRestartableObjectStoreDelegatedFunctions(t *testing.T) { sharedPluginProcess: p, } }, - func() mockable { + func() restartabletest.Mockable { return new(providermocks.ObjectStore) }, - restartableDelegateTest{ - function: "PutObject", - inputs: []interface{}{"bucket", "key", strings.NewReader("body")}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "PutObject", + Inputs: []interface{}{"bucket", "key", strings.NewReader("body")}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "GetObject", - inputs: []interface{}{"bucket", "key"}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{ioutil.NopCloser(strings.NewReader("object")), errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "GetObject", + Inputs: []interface{}{"bucket", "key"}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{ioutil.NopCloser(strings.NewReader("object")), errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "ListCommonPrefixes", - inputs: []interface{}{"bucket", "prefix", "delimiter"}, - expectedErrorOutputs: []interface{}{([]string)(nil), errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{[]string{"a", "b"}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "ListCommonPrefixes", + Inputs: []interface{}{"bucket", "prefix", "delimiter"}, + ExpectedErrorOutputs: []interface{}{([]string)(nil), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{[]string{"a", "b"}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "ListObjects", - inputs: []interface{}{"bucket", "prefix"}, - expectedErrorOutputs: []interface{}{([]string)(nil), errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{[]string{"a", "b"}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "ListObjects", + Inputs: []interface{}{"bucket", "prefix"}, + ExpectedErrorOutputs: []interface{}{([]string)(nil), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{[]string{"a", "b"}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "DeleteObject", - inputs: []interface{}{"bucket", "key"}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "DeleteObject", + Inputs: []interface{}{"bucket", "key"}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "CreateSignedURL", - inputs: []interface{}{"bucket", "key", 30 * time.Minute}, - expectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{"signedURL", errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "CreateSignedURL", + Inputs: []interface{}{"bucket", "key", 30 * time.Minute}, + ExpectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{"signedURL", errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go index 4f8c06f8c6..d7d0dfc3d6 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/internal/restartabletest" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" @@ -55,7 +56,7 @@ func TestRestartableGetVolumeSnapshotter(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -80,7 +81,7 @@ func TestRestartableGetVolumeSnapshotter(t *testing.T) { } func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -111,7 +112,7 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { } func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -140,7 +141,7 @@ func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { } func TestRestartableVolumeSnapshotterInit(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) p.Test(t) defer p.AssertExpectations(t) @@ -196,7 +197,7 @@ func TestRestartableVolumeSnapshotterDelegatedFunctions(t *testing.T) { }, } - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindVolumeSnapshotter, func(key process.KindAndName, p process.RestartableProcess) interface{} { @@ -205,44 +206,44 @@ func TestRestartableVolumeSnapshotterDelegatedFunctions(t *testing.T) { sharedPluginProcess: p, } }, - func() mockable { + func() restartabletest.Mockable { return new(providermocks.VolumeSnapshotter) }, - restartableDelegateTest{ - function: "CreateVolumeFromSnapshot", - inputs: []interface{}{"snapshotID", "volumeID", "volumeAZ", to.Int64Ptr(10000)}, - expectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{"volumeID", errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "CreateVolumeFromSnapshot", + Inputs: []interface{}{"snapshotID", "volumeID", "volumeAZ", to.Int64Ptr(10000)}, + ExpectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{"volumeID", errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "GetVolumeID", - inputs: []interface{}{pv}, - expectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{"volumeID", errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "GetVolumeID", + Inputs: []interface{}{pv}, + ExpectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{"volumeID", errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "SetVolumeID", - inputs: []interface{}{pv, "volumeID"}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{pvToReturn, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "SetVolumeID", + Inputs: []interface{}{pv, "volumeID"}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{pvToReturn, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "GetVolumeInfo", - inputs: []interface{}{"volumeID", "volumeAZ"}, - expectedErrorOutputs: []interface{}{"", (*int64)(nil), errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{"volumeType", to.Int64Ptr(10000), errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "GetVolumeInfo", + Inputs: []interface{}{"volumeID", "volumeAZ"}, + ExpectedErrorOutputs: []interface{}{"", (*int64)(nil), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{"volumeType", to.Int64Ptr(10000), errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "CreateSnapshot", - inputs: []interface{}{"volumeID", "volumeAZ", map[string]string{"a": "b"}}, - expectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{"snapshotID", errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "CreateSnapshot", + Inputs: []interface{}{"volumeID", "volumeAZ", map[string]string{"a": "b"}}, + ExpectedErrorOutputs: []interface{}{"", errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{"snapshotID", errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "DeleteSnapshot", - inputs: []interface{}{"snapshotID"}, - expectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "DeleteSnapshot", + Inputs: []interface{}{"snapshotID"}, + ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action.go b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go similarity index 52% rename from pkg/plugin/clientmgmt/restartable_restore_item_action.go rename to pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go index fdd266d0bf..a6b595544d 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action.go +++ b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "github.com/pkg/errors" @@ -22,36 +22,56 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) +// AdaptedRestoreItemAction is a restore item action adapted to the v1 RestoreItemAction API +type AdaptedRestoreItemAction struct { + Kind common.PluginKind + + // Get returns a restartable RestoreItemAction for the given name and process, wrapping if necessary + GetRestartable func(name string, restartableProcess process.RestartableProcess) riav1.RestoreItemAction +} + +func AdaptedRestoreItemActions() []AdaptedRestoreItemAction { + return []AdaptedRestoreItemAction{ + { + Kind: common.PluginKindRestoreItemAction, + GetRestartable: func(name string, restartableProcess process.RestartableProcess) riav1.RestoreItemAction { + return NewRestartableRestoreItemAction(name, restartableProcess) + }, + }, + } +} + // RestartableRestoreItemAction is a restore item action for a given implementation (such as "pod"). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method -// call, the restartableRestoreItemAction asks its restartableProcess to restart itself if needed (e.g. if the +// call, the RestartableRestoreItemAction asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. -type restartableRestoreItemAction struct { - key process.KindAndName - sharedPluginProcess process.RestartableProcess +type RestartableRestoreItemAction struct { + Key process.KindAndName + SharedPluginProcess process.RestartableProcess config map[string]string } // NewRestartableRestoreItemAction returns a new RestartableRestoreItemAction. -func NewRestartableRestoreItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableRestoreItemAction { - r := &restartableRestoreItemAction{ - key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, - sharedPluginProcess: sharedPluginProcess, +func NewRestartableRestoreItemAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableRestoreItemAction { + r := &RestartableRestoreItemAction{ + Key: process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, } return r } -// getRestoreItemAction returns the restore item action for this restartableRestoreItemAction. It does *not* restart the +// getRestoreItemAction returns the restore item action for this RestartableRestoreItemAction. It does *not* restart the // plugin process. -func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreItemAction, error) { - plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) +func (r *RestartableRestoreItemAction) getRestoreItemAction() (riav1.RestoreItemAction, error) { + plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key) if err != nil { return nil, err } - restoreItemAction, ok := plugin.(velero.RestoreItemAction) + restoreItemAction, ok := plugin.(riav1.RestoreItemAction) if !ok { return nil, errors.Errorf("%T is not a RestoreItemAction!", plugin) } @@ -59,9 +79,9 @@ func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreIte return restoreItemAction, nil } -// getDelegate restarts the plugin process (if needed) and returns the restore item action for this restartableRestoreItemAction. -func (r *restartableRestoreItemAction) getDelegate() (velero.RestoreItemAction, error) { - if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { +// getDelegate restarts the plugin process (if needed) and returns the restore item action for this RestartableRestoreItemAction. +func (r *RestartableRestoreItemAction) getDelegate() (riav1.RestoreItemAction, error) { + if err := r.SharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } @@ -69,7 +89,7 @@ func (r *restartableRestoreItemAction) getDelegate() (velero.RestoreItemAction, } // AppliesTo restarts the plugin's process if needed, then delegates the call. -func (r *restartableRestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { +func (r RestartableRestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { delegate, err := r.getDelegate() if err != nil { return velero.ResourceSelector{}, err @@ -79,7 +99,7 @@ func (r *restartableRestoreItemAction) AppliesTo() (velero.ResourceSelector, err } // Execute restarts the plugin's process if needed, then delegates the call. -func (r *restartableRestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (r *RestartableRestoreItemAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { delegate, err := r.getDelegate() if err != nil { return nil, err diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go similarity index 72% rename from pkg/plugin/clientmgmt/restartable_restore_item_action_test.go rename to pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go index b91d08bfa6..55a0db2781 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action_test.go +++ b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "testing" @@ -24,11 +24,13 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/internal/restartabletest" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restore/mocks" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/restoreitemaction/v1" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) func TestRestartableGetRestoreItemAction(t *testing.T) { @@ -50,13 +52,13 @@ func TestRestartableGetRestoreItemAction(t *testing.T) { }, { name: "happy path", - plugin: new(mocks.ItemAction), + plugin: new(mocks.RestoreItemAction), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) name := "pod" @@ -77,7 +79,7 @@ func TestRestartableGetRestoreItemAction(t *testing.T) { } func TestRestartableRestoreItemActionGetDelegate(t *testing.T) { - p := new(mockRestartableProcess) + p := new(restartabletest.MockRestartableProcess) defer p.AssertExpectations(t) // Reset error @@ -90,7 +92,7 @@ func TestRestartableRestoreItemActionGetDelegate(t *testing.T) { // Happy path p.On("ResetIfNeeded").Return(nil) - expected := new(mocks.ItemAction) + expected := new(mocks.RestoreItemAction) key := process.KindAndName{Kind: common.PluginKindRestoreItemAction, Name: name} p.On("GetByKindAndName", key).Return(expected, nil) @@ -106,13 +108,13 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) { }, } - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: pv, ItemFromBackup: pv, Restore: new(v1.Restore), } - output := &velero.RestoreItemActionExecuteOutput{ + output := &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: &unstructured.Unstructured{ Object: map[string]interface{}{ "color": "green", @@ -120,29 +122,29 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) { }, } - runRestartableDelegateTests( + restartabletest.RunRestartableDelegateTests( t, common.PluginKindRestoreItemAction, func(key process.KindAndName, p process.RestartableProcess) interface{} { - return &restartableRestoreItemAction{ - key: key, - sharedPluginProcess: p, + return &RestartableRestoreItemAction{ + Key: key, + SharedPluginProcess: p, } }, - func() mockable { - return new(mocks.ItemAction) + func() restartabletest.Mockable { + return new(mocks.RestoreItemAction) }, - restartableDelegateTest{ - function: "AppliesTo", - inputs: []interface{}{}, - expectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "AppliesTo", + Inputs: []interface{}{}, + ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, }, - restartableDelegateTest{ - function: "Execute", - inputs: []interface{}{input}, - expectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, - expectedDelegateOutputs: []interface{}{output, errors.Errorf("delegate error")}, + restartabletest.RestartableDelegateTest{ + Function: "Execute", + Inputs: []interface{}{input}, + ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{output, errors.Errorf("delegate error")}, }, ) } diff --git a/pkg/plugin/framework/action_resolver.go b/pkg/plugin/framework/action_resolver.go index b1d692fd36..5664d08c2c 100644 --- a/pkg/plugin/framework/action_resolver.go +++ b/pkg/plugin/framework/action_resolver.go @@ -26,6 +26,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -108,7 +109,7 @@ func NewBackupItemActionResolver(actions []biav1.BackupItemAction) BackupItemAct } } -func NewRestoreItemActionResolver(actions []velero.RestoreItemAction) RestoreItemActionResolver { +func NewRestoreItemActionResolver(actions []riav1.RestoreItemAction) RestoreItemActionResolver { return RestoreItemActionResolver{ actions: actions, } @@ -155,12 +156,12 @@ func (recv BackupItemActionResolver) ResolveActions(helper discovery.Helper, log } type RestoreItemResolvedAction struct { - velero.RestoreItemAction + riav1.RestoreItemAction resolvedAction } type RestoreItemActionResolver struct { - actions []velero.RestoreItemAction + actions []riav1.RestoreItemAction } func (recv RestoreItemActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]RestoreItemResolvedAction, error) { diff --git a/pkg/plugin/framework/restore_item_action_client.go b/pkg/plugin/framework/restore_item_action_client.go index 03907f390b..35bafa0893 100644 --- a/pkg/plugin/framework/restore_item_action_client.go +++ b/pkg/plugin/framework/restore_item_action_client.go @@ -28,9 +28,10 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) -var _ velero.RestoreItemAction = &RestoreItemActionGRPCClient{} +var _ riav1.RestoreItemAction = &RestoreItemActionGRPCClient{} // NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin. func NewRestoreItemActionPlugin(options ...common.PluginOption) *RestoreItemActionPlugin { @@ -72,7 +73,7 @@ func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, erro }, nil } -func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (c *RestoreItemActionGRPCClient) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { itemJSON, err := json.Marshal(input.Item.UnstructuredContent()) if err != nil { return nil, errors.WithStack(err) @@ -119,7 +120,7 @@ func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExe additionalItems = append(additionalItems, newItem) } - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: &updatedItem, AdditionalItems: additionalItems, SkipRestore: res.SkipRestore, diff --git a/pkg/plugin/framework/restore_item_action_server.go b/pkg/plugin/framework/restore_item_action_server.go index 5b45f4e91f..924929c975 100644 --- a/pkg/plugin/framework/restore_item_action_server.go +++ b/pkg/plugin/framework/restore_item_action_server.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts @@ -35,13 +36,13 @@ type RestoreItemActionGRPCServer struct { mux *common.ServerMux } -func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAction, error) { +func (s *RestoreItemActionGRPCServer) getImpl(name string) (riav1.RestoreItemAction, error) { impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.RestoreItemAction) + itemAction, ok := impl.(riav1.RestoreItemAction) if !ok { return nil, errors.Errorf("%T is not a restore item action", impl) } @@ -107,7 +108,7 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re return nil, common.NewGRPCError(errors.WithStack(err)) } - executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{ + executeOutput, err := impl.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &item, ItemFromBackup: &itemFromBackup, Restore: &restoreObj, diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index 6783e1c51b..0a0288b065 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -1,13 +1,16 @@ -// Code generated by mockery v2.1.0. DO NOT EDIT. +// Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( mock "github.com/stretchr/testify/mock" + item_snapshotterv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" + + restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" - biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" - isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" ) // Manager is an autogenerated mock type for the Manager type @@ -21,15 +24,15 @@ func (_m *Manager) CleanupClients() { } // GetBackupItemAction provides a mock function with given fields: name -func (_m *Manager) GetBackupItemAction(name string) (biav1.BackupItemAction, error) { +func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) { ret := _m.Called(name) - var r0 biav1.BackupItemAction - if rf, ok := ret.Get(0).(func(string) biav1.BackupItemAction); ok { + var r0 v1.BackupItemAction + if rf, ok := ret.Get(0).(func(string) v1.BackupItemAction); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(biav1.BackupItemAction) + r0 = ret.Get(0).(v1.BackupItemAction) } } @@ -44,15 +47,15 @@ func (_m *Manager) GetBackupItemAction(name string) (biav1.BackupItemAction, err } // GetBackupItemActions provides a mock function with given fields: -func (_m *Manager) GetBackupItemActions() ([]biav1.BackupItemAction, error) { +func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { ret := _m.Called() - var r0 []biav1.BackupItemAction - if rf, ok := ret.Get(0).(func() []biav1.BackupItemAction); ok { + var r0 []v1.BackupItemAction + if rf, ok := ret.Get(0).(func() []v1.BackupItemAction); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]biav1.BackupItemAction) + r0 = ret.Get(0).([]v1.BackupItemAction) } } @@ -112,16 +115,16 @@ func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { return r0, r1 } -// GetObjectStore provides a mock function with given fields: name -func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { +// GetItemSnapshotter provides a mock function with given fields: name +func (_m *Manager) GetItemSnapshotter(name string) (item_snapshotterv1.ItemSnapshotter, error) { ret := _m.Called(name) - var r0 velero.ObjectStore - if rf, ok := ret.Get(0).(func(string) velero.ObjectStore); ok { + var r0 item_snapshotterv1.ItemSnapshotter + if rf, ok := ret.Get(0).(func(string) item_snapshotterv1.ItemSnapshotter); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.ObjectStore) + r0 = ret.Get(0).(item_snapshotterv1.ItemSnapshotter) } } @@ -135,22 +138,22 @@ func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { return r0, r1 } -// GetRestoreItemAction provides a mock function with given fields: name -func (_m *Manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) { - ret := _m.Called(name) +// GetItemSnapshotters provides a mock function with given fields: +func (_m *Manager) GetItemSnapshotters() ([]item_snapshotterv1.ItemSnapshotter, error) { + ret := _m.Called() - var r0 velero.RestoreItemAction - if rf, ok := ret.Get(0).(func(string) velero.RestoreItemAction); ok { - r0 = rf(name) + var r0 []item_snapshotterv1.ItemSnapshotter + if rf, ok := ret.Get(0).(func() []item_snapshotterv1.ItemSnapshotter); ok { + r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.RestoreItemAction) + r0 = ret.Get(0).([]item_snapshotterv1.ItemSnapshotter) } } var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() } else { r1 = ret.Error(1) } @@ -158,22 +161,22 @@ func (_m *Manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, return r0, r1 } -// GetRestoreItemActions provides a mock function with given fields: -func (_m *Manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { - ret := _m.Called() +// GetObjectStore provides a mock function with given fields: name +func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { + ret := _m.Called(name) - var r0 []velero.RestoreItemAction - if rf, ok := ret.Get(0).(func() []velero.RestoreItemAction); ok { - r0 = rf() + var r0 velero.ObjectStore + if rf, ok := ret.Get(0).(func(string) velero.ObjectStore); ok { + r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]velero.RestoreItemAction) + r0 = ret.Get(0).(velero.ObjectStore) } } var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) } else { r1 = ret.Error(1) } @@ -181,16 +184,16 @@ func (_m *Manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { return r0, r1 } -// GetVolumeSnapshotter provides a mock function with given fields: name -func (_m *Manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +// GetRestoreItemAction provides a mock function with given fields: name +func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.RestoreItemAction, error) { ret := _m.Called(name) - var r0 velero.VolumeSnapshotter - if rf, ok := ret.Get(0).(func(string) velero.VolumeSnapshotter); ok { + var r0 restoreitemactionv1.RestoreItemAction + if rf, ok := ret.Get(0).(func(string) restoreitemactionv1.RestoreItemAction); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.VolumeSnapshotter) + r0 = ret.Get(0).(restoreitemactionv1.RestoreItemAction) } } @@ -204,47 +207,48 @@ func (_m *Manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, return r0, r1 } -// GetItemSnapshotter provides a mock function with given fields: name -func (_m *Manager) GetItemSnapshotter(name string) (isv1.ItemSnapshotter, error) { - ret := _m.Called(name) +// GetRestoreItemActions provides a mock function with given fields: +func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAction, error) { + ret := _m.Called() - var r0 isv1.ItemSnapshotter - if rf, ok := ret.Get(0).(func(string) isv1.ItemSnapshotter); ok { - r0 = rf(name) + var r0 []restoreitemactionv1.RestoreItemAction + if rf, ok := ret.Get(0).(func() []restoreitemactionv1.RestoreItemAction); ok { + r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(isv1.ItemSnapshotter) + r0 = ret.Get(0).([]restoreitemactionv1.RestoreItemAction) } } var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() } else { r1 = ret.Error(1) } return r0, r1 } -// GetItemSnapshotters provides a mock function with given fields: -func (_m *Manager) GetItemSnapshotters() ([]isv1.ItemSnapshotter, error) { - ret := _m.Called() - var r0 []isv1.ItemSnapshotter - if rf, ok := ret.Get(0).(func() []isv1.ItemSnapshotter); ok { - r0 = rf() +// GetVolumeSnapshotter provides a mock function with given fields: name +func (_m *Manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { + ret := _m.Called(name) + + var r0 velero.VolumeSnapshotter + if rf, ok := ret.Get(0).(func(string) velero.VolumeSnapshotter); ok { + r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]isv1.ItemSnapshotter) + r0 = ret.Get(0).(velero.VolumeSnapshotter) } } var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) } else { r1 = ret.Error(1) } return r0, r1 -} \ No newline at end of file +} diff --git a/pkg/restore/mocks/item_action.go b/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go similarity index 60% rename from pkg/restore/mocks/item_action.go rename to pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go index 8bdf3bbbc9..5bf33ed851 100644 --- a/pkg/restore/mocks/item_action.go +++ b/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,21 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ // Code generated by mockery v1.0.0. DO NOT EDIT. -package mocks + +package v1 import ( mock "github.com/stretchr/testify/mock" - - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) -// ItemAction is an autogenerated mock type for the ItemAction type -type ItemAction struct { +// RestoreItemAction is an autogenerated mock type for the RestoreItemAction type +type RestoreItemAction struct { mock.Mock } // AppliesTo provides a mock function with given fields: -func (_m *ItemAction) AppliesTo() (velero.ResourceSelector, error) { +func (_m *RestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { ret := _m.Called() var r0 velero.ResourceSelector @@ -49,20 +50,20 @@ func (_m *ItemAction) AppliesTo() (velero.ResourceSelector, error) { } // Execute provides a mock function with given fields: input -func (_m *ItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (_m *RestoreItemAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { ret := _m.Called(input) - var r0 *velero.RestoreItemActionExecuteOutput - if rf, ok := ret.Get(0).(func(*velero.RestoreItemActionExecuteInput) *velero.RestoreItemActionExecuteOutput); ok { + var r0 *riav1.RestoreItemActionExecuteOutput + if rf, ok := ret.Get(0).(func(*riav1.RestoreItemActionExecuteInput) *riav1.RestoreItemActionExecuteOutput); ok { r0 = rf(input) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*velero.RestoreItemActionExecuteOutput) + r0 = ret.Get(0).(*riav1.RestoreItemActionExecuteOutput) } } var r1 error - if rf, ok := ret.Get(1).(func(*velero.RestoreItemActionExecuteInput) error); ok { + if rf, ok := ret.Get(1).(func(*riav1.RestoreItemActionExecuteInput) error); ok { r1 = rf(input) } else { r1 = ret.Error(1) diff --git a/pkg/plugin/velero/restore_item_action.go b/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go similarity index 94% rename from pkg/plugin/velero/restore_item_action.go rename to pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go index ea758c93a5..a50f3021f0 100644 --- a/pkg/plugin/velero/restore_item_action.go +++ b/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "k8s.io/apimachinery/pkg/runtime" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // RestoreItemAction is an actor that performs an operation on an individual item being restored. @@ -27,7 +28,7 @@ type RestoreItemAction interface { // AppliesTo returns information about which resources this action should be invoked for. // A RestoreItemAction's Execute function will only be invoked on items that match the returned // selector. A zero-valued ResourceSelector matches all resources. - AppliesTo() (ResourceSelector, error) + AppliesTo() (velero.ResourceSelector, error) // Execute allows the ItemAction to perform arbitrary logic with the item being restored, // including mutating the item itself prior to restore. The item (unmodified or modified) @@ -56,7 +57,7 @@ type RestoreItemActionExecuteOutput struct { // AdditionalItems is a list of additional related items that should // be restored. - AdditionalItems []ResourceIdentifier + AdditionalItems []velero.ResourceIdentifier // SkipRestore tells velero to stop executing further actions // on this item, and skip the restore step. When this field's diff --git a/pkg/restore/add_pv_from_pvc_action.go b/pkg/restore/add_pv_from_pvc_action.go index 04c992357f..147ad553f4 100644 --- a/pkg/restore/add_pv_from_pvc_action.go +++ b/pkg/restore/add_pv_from_pvc_action.go @@ -24,6 +24,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type AddPVFromPVCAction struct { @@ -40,7 +41,7 @@ func (a *AddPVFromPVCAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *AddPVFromPVCAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *AddPVFromPVCAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing AddPVFromPVCAction") // use input.ItemFromBackup because we need to look at status fields, which have already been @@ -53,7 +54,7 @@ func (a *AddPVFromPVCAction) Execute(input *velero.RestoreItemActionExecuteInput // TODO: consolidate this logic in a helper function to share with backup_pv_action.go if pvc.Status.Phase != corev1api.ClaimBound || pvc.Spec.VolumeName == "" { a.logger.Info("PVC is not bound or its volume name is empty") - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -64,7 +65,7 @@ func (a *AddPVFromPVCAction) Execute(input *velero.RestoreItemActionExecuteInput } a.logger.Infof("Adding PV %s as an additional item to restore", pvc.Spec.VolumeName) - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{pv}, }, nil diff --git a/pkg/restore/add_pv_from_pvc_action_test.go b/pkg/restore/add_pv_from_pvc_action_test.go index b4ceb90a43..f8b5b5aaba 100644 --- a/pkg/restore/add_pv_from_pvc_action_test.go +++ b/pkg/restore/add_pv_from_pvc_action_test.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -89,7 +90,7 @@ func TestAddPVFromPVCActionExecute(t *testing.T) { action := &AddPVFromPVCAction{logger: velerotest.NewLogger()} - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: itemData}, ItemFromBackup: &unstructured.Unstructured{Object: itemFromBackupData}, } diff --git a/pkg/restore/add_pvc_from_pod_action.go b/pkg/restore/add_pvc_from_pod_action.go index 70f33d985d..b7bd60a34c 100644 --- a/pkg/restore/add_pvc_from_pod_action.go +++ b/pkg/restore/add_pvc_from_pod_action.go @@ -24,6 +24,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type AddPVCFromPodAction struct { @@ -40,7 +41,7 @@ func (a *AddPVCFromPodAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *AddPVCFromPodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *AddPVCFromPodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing AddPVCFromPodAction") var pod corev1api.Pod @@ -63,7 +64,7 @@ func (a *AddPVCFromPodAction) Execute(input *velero.RestoreItemActionExecuteInpu }) } - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: additionalItems, }, nil diff --git a/pkg/restore/add_pvc_from_pod_action_test.go b/pkg/restore/add_pvc_from_pod_action_test.go index b409e5d6ef..529751b379 100644 --- a/pkg/restore/add_pvc_from_pod_action_test.go +++ b/pkg/restore/add_pvc_from_pod_action_test.go @@ -28,6 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -100,7 +101,7 @@ func TestAddPVCFromPodActionExecute(t *testing.T) { action := &AddPVCFromPodAction{logger: velerotest.NewLogger()} - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: itemData}, } diff --git a/pkg/restore/admissionwebhook_config_action.go b/pkg/restore/admissionwebhook_config_action.go index 8fd5c1693e..68fa089888 100644 --- a/pkg/restore/admissionwebhook_config_action.go +++ b/pkg/restore/admissionwebhook_config_action.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // AdmissionWebhookConfigurationAction is a RestoreItemAction plugin applicable to mutatingwebhookconfiguration and @@ -46,7 +47,7 @@ func (a *AdmissionWebhookConfigurationAction) AppliesTo() (velero.ResourceSelect // Execute will reset the value of "sideEffects" attribute of each item in the "webhooks" list to "None" if they are invalid values for // v1, such as "Unknown" or "Some" -func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *AdmissionWebhookConfigurationAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ChangeStorageClassAction") defer a.logger.Info("Done executing ChangeStorageClassAction") @@ -59,7 +60,7 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemA logger := a.logger.WithField("resource_name", name) if apiVersion != "admissionregistration.k8s.io/v1" { logger.Infof("unable to handle api version: %s, skip", apiVersion) - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } webhooks, ok, err := unstructured.NestedSlice(item.UnstructuredContent(), "webhooks") if err != nil { @@ -67,7 +68,7 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemA } if !ok { logger.Info("webhooks is not set, skip") - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } newWebhooks := make([]interface{}, 0) for i, entry := range webhooks { @@ -85,5 +86,5 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemA newWebhooks = append(newWebhooks, obj) } item.UnstructuredContent()["webhooks"] = newWebhooks - return velero.NewRestoreItemActionExecuteOutput(item), nil + return riav1.NewRestoreItemActionExecuteOutput(item), nil } diff --git a/pkg/restore/admissionwebhook_config_action_test.go b/pkg/restore/admissionwebhook_config_action_test.go index c6c31d2219..a6548ae586 100644 --- a/pkg/restore/admissionwebhook_config_action_test.go +++ b/pkg/restore/admissionwebhook_config_action_test.go @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -163,7 +163,7 @@ func TestNewAdmissionWebhookConfigurationActionExecute(t *testing.T) { t.Run(tt.name, func(t *testing.T) { o := map[string]interface{}{} json.Unmarshal([]byte(tt.itemJSON), &o) - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: o, }, diff --git a/pkg/restore/apiservice_action.go b/pkg/restore/apiservice_action.go index 7f817a59e0..0d9568c0b8 100644 --- a/pkg/restore/apiservice_action.go +++ b/pkg/restore/apiservice_action.go @@ -21,6 +21,7 @@ import ( "k8s.io/kube-aggregator/pkg/controllers/autoregister" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type APIServiceAction struct { @@ -42,10 +43,10 @@ func (a *APIServiceAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *APIServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *APIServiceAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing APIServiceAction") defer a.logger.Info("Done executing APIServiceAction") a.logger.Infof("Skipping restore of APIService as it is managed by Kubernetes") - return velero.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil } diff --git a/pkg/restore/apiservice_action_test.go b/pkg/restore/apiservice_action_test.go index 81f4a6171c..1f9baeae92 100644 --- a/pkg/restore/apiservice_action_test.go +++ b/pkg/restore/apiservice_action_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -40,7 +40,7 @@ func TestAPIServiceActionExecuteSkipsRestore(t *testing.T) { require.NoError(t, err) action := NewAPIServiceAction(velerotest.NewLogger()) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredAPIService}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredAPIService}, }) diff --git a/pkg/restore/change_pvc_node_selector.go b/pkg/restore/change_pvc_node_selector.go index d281d318f7..23a3c20c90 100644 --- a/pkg/restore/change_pvc_node_selector.go +++ b/pkg/restore/change_pvc_node_selector.go @@ -28,6 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ChangePVCNodeSelectorAction updates/reset PVC's node selector @@ -61,23 +62,23 @@ func (p *ChangePVCNodeSelectorAction) AppliesTo() (velero.ResourceSelector, erro // Execute updates the pvc's selected-node annotation: // a) if node mapping found in the config map for the plugin // b) if node mentioned in annotation doesn't exist -func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (p *ChangePVCNodeSelectorAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { p.logger.Info("Executing ChangePVCNodeSelectorAction") defer p.logger.Info("Done executing ChangePVCNodeSelectorAction") typeAcc, err := meta.TypeAccessor(input.Item) if err != nil { - return &velero.RestoreItemActionExecuteOutput{}, err + return &riav1.RestoreItemActionExecuteOutput{}, err } metadata, err := meta.Accessor(input.Item) if err != nil { - return &velero.RestoreItemActionExecuteOutput{}, err + return &riav1.RestoreItemActionExecuteOutput{}, err } annotations := metadata.GetAnnotations() if annotations == nil { - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } log := p.logger.WithFields(map[string]interface{}{ @@ -90,7 +91,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe node, ok := annotations["volume.kubernetes.io/selected-node"] if !ok { log.Debug("PVC doesn't have node selector") - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } // fetch node mapping from configMap @@ -105,7 +106,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe annotations["volume.kubernetes.io/selected-node"] = newNode metadata.SetAnnotations(annotations) log.Infof("Updating selected-node to %s from %s", newNode, node) - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } // configMap doesn't have node-mapping @@ -125,7 +126,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe } } - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } func getNewNodeFromConfigMap(client corev1client.ConfigMapInterface, node string) (string, error) { diff --git a/pkg/restore/change_pvc_node_selector_test.go b/pkg/restore/change_pvc_node_selector_test.go index 8be3051bac..92a6f4b311 100644 --- a/pkg/restore/change_pvc_node_selector_test.go +++ b/pkg/restore/change_pvc_node_selector_test.go @@ -31,7 +31,7 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // TestChangePVCNodeSelectorActionExecute runs the ChangePVCNodeSelectorAction's Execute @@ -146,7 +146,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pvc) require.NoError(t, err) - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredMap, }, diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index a02b7c484e..3349cc7925 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -31,6 +31,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ChangeStorageClassAction updates a PV or PVC's storage class name @@ -64,7 +65,7 @@ func (a *ChangeStorageClassAction) AppliesTo() (velero.ResourceSelector, error) // Execute updates the item's spec.storageClassName if a mapping is found // in the config map for the plugin. -func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *ChangeStorageClassAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ChangeStorageClassAction") defer a.logger.Info("Done executing ChangeStorageClassAction") @@ -76,7 +77,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut if config == nil || len(config.Data) == 0 { a.logger.Debug("No storage class mappings found") - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } obj, ok := input.Item.(*unstructured.Unstructured) @@ -128,7 +129,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut if err != nil { return nil, err } else if !exists { - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } log.Infof("Updating item's storage class name to %s", newStorageClass) @@ -137,7 +138,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut return nil, errors.Wrap(err, "unable to set item's spec.storageClassName") } } - return velero.NewRestoreItemActionExecuteOutput(obj), nil + return riav1.NewRestoreItemActionExecuteOutput(obj), nil } func (a *ChangeStorageClassAction) isStorageClassExist(log *logrus.Entry, storageClass *string, cm *corev1.ConfigMap) (exists bool, newStorageClass string, err error) { diff --git a/pkg/restore/change_storageclass_action_test.go b/pkg/restore/change_storageclass_action_test.go index 65de052db2..12a663075f 100644 --- a/pkg/restore/change_storageclass_action_test.go +++ b/pkg/restore/change_storageclass_action_test.go @@ -32,7 +32,7 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // TestChangeStorageClassActionExecute runs the ChangeStorageClassAction's Execute @@ -245,7 +245,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pvOrPvcOrSTS) require.NoError(t, err) - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredMap, }, diff --git a/pkg/restore/clusterrolebinding_action.go b/pkg/restore/clusterrolebinding_action.go index 851b13f098..eed52ba89c 100644 --- a/pkg/restore/clusterrolebinding_action.go +++ b/pkg/restore/clusterrolebinding_action.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ClusterRoleBindingAction handle namespace remappings for role bindings @@ -41,10 +42,10 @@ func (a *ClusterRoleBindingAction) AppliesTo() (velero.ResourceSelector, error) }, nil } -func (a *ClusterRoleBindingAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *ClusterRoleBindingAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { namespaceMapping := input.Restore.Spec.NamespaceMapping if len(namespaceMapping) == 0 { - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil } clusterRoleBinding := new(rbac.ClusterRoleBinding) @@ -63,5 +64,5 @@ func (a *ClusterRoleBindingAction) Execute(input *velero.RestoreItemActionExecut return nil, errors.WithStack(err) } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/clusterrolebinding_action_test.go b/pkg/restore/clusterrolebinding_action_test.go index cea1c57871..a68334565a 100644 --- a/pkg/restore/clusterrolebinding_action_test.go +++ b/pkg/restore/clusterrolebinding_action_test.go @@ -28,6 +28,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -89,7 +90,7 @@ func TestClusterRoleBindingActionExecute(t *testing.T) { require.NoError(t, err) action := NewClusterRoleBindingAction(test.NewLogger()) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: roleBindingUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: roleBindingUnstructured}, Restore: &api.Restore{ diff --git a/pkg/restore/crd_v1_preserve_unknown_fields_action.go b/pkg/restore/crd_v1_preserve_unknown_fields_action.go index f67d47910d..a9d8768056 100644 --- a/pkg/restore/crd_v1_preserve_unknown_fields_action.go +++ b/pkg/restore/crd_v1_preserve_unknown_fields_action.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // CRDV1PreserveUnknownFieldsAction will take a CRD and inspect it for the API version and the PreserveUnknownFields value. @@ -45,7 +46,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) AppliesTo() (velero.ResourceSelector, }, nil } -func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { c.logger.Info("Executing CRDV1PreserveUnknownFieldsAction") name, _, err := unstructured.NestedString(input.Item.UnstructuredContent(), "name") @@ -62,7 +63,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *velero.RestoreItemActi // We don't want to "fix" anything in beta CRDs at the moment, just v1 versions with preserveunknownfields = true if version != "apiextensions.k8s.io/v1" { - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -102,7 +103,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *velero.RestoreItemActi return nil, errors.Wrap(err, "unable to convert crd to runtime.Unstructured") } - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: &unstructured.Unstructured{Object: res}, }, nil } diff --git a/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go b/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go index 77045b596d..3840918ad5 100644 --- a/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go +++ b/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -48,6 +48,6 @@ func TestExecuteForACRDWithAnIntOnAFloat64FieldShouldWork(t *testing.T) { a := NewCRDV1PreserveUnknownFieldsAction(test.NewLogger()) - _, err = a.Execute(&velero.RestoreItemActionExecuteInput{Item: &u}) + _, err = a.Execute(&riav1.RestoreItemActionExecuteInput{Item: &u}) require.NoError(t, err) } diff --git a/pkg/restore/init_restorehook_pod_action.go b/pkg/restore/init_restorehook_pod_action.go index f994d811de..cb72e14d4f 100644 --- a/pkg/restore/init_restorehook_pod_action.go +++ b/pkg/restore/init_restorehook_pod_action.go @@ -24,6 +24,7 @@ import ( "github.com/vmware-tanzu/velero/internal/hook" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // InitRestoreHookPodAction is a RestoreItemAction plugin applicable to pods that runs @@ -45,7 +46,7 @@ func (a *InitRestoreHookPodAction) AppliesTo() (velero.ResourceSelector, error) } // Execute implements the RestoreItemAction plugin interface method. -func (a *InitRestoreHookPodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *InitRestoreHookPodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Infof("Executing InitRestoreHookPodAction") // handle any init container restore hooks for the pod restoreHooks, err := hook.GetRestoreHooksFromSpec(&input.Restore.Spec.Hooks) @@ -60,5 +61,5 @@ func (a *InitRestoreHookPodAction) Execute(input *velero.RestoreItemActionExecut } a.logger.Infof("Returning from InitRestoreHookPodAction") - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: postHooksItem.UnstructuredContent()}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: postHooksItem.UnstructuredContent()}), nil } diff --git a/pkg/restore/init_restorehook_pod_action_test.go b/pkg/restore/init_restorehook_pod_action_test.go index c69d3c23f9..058865660a 100644 --- a/pkg/restore/init_restorehook_pod_action_test.go +++ b/pkg/restore/init_restorehook_pod_action_test.go @@ -29,7 +29,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/kuberesource" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -125,7 +125,7 @@ func TestInitContainerRestoreHookPodActionExecute(t *testing.T) { unstructuredPod, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.obj) require.NoError(t, err) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredPod}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredPod}, Restore: tc.restore, diff --git a/pkg/restore/job_action.go b/pkg/restore/job_action.go index fbaf30b249..4e20f9cc92 100644 --- a/pkg/restore/job_action.go +++ b/pkg/restore/job_action.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type JobAction struct { @@ -40,7 +41,7 @@ func (a *JobAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *JobAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *JobAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { job := new(batchv1api.Job) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), job); err != nil { return nil, errors.WithStack(err) @@ -56,5 +57,5 @@ func (a *JobAction) Execute(input *velero.RestoreItemActionExecuteInput) (*veler return nil, errors.WithStack(err) } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/job_action_test.go b/pkg/restore/job_action_test.go index 606dd089d8..0b15bc47f0 100644 --- a/pkg/restore/job_action_test.go +++ b/pkg/restore/job_action_test.go @@ -27,7 +27,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -138,7 +138,7 @@ func TestJobActionExecute(t *testing.T) { unstructuredJob, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredJob}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredJob}, Restore: nil, diff --git a/pkg/restore/pod_action.go b/pkg/restore/pod_action.go index d4bdc13847..35d9d75220 100644 --- a/pkg/restore/pod_action.go +++ b/pkg/restore/pod_action.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type PodAction struct { @@ -43,7 +44,7 @@ func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *PodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *PodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { pod := new(v1.Pod) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), pod); err != nil { return nil, errors.WithStack(err) @@ -86,7 +87,7 @@ func (a *PodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*veler if err != nil { return nil, errors.WithStack(err) } - restoreExecuteOutput := velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) + restoreExecuteOutput := riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) if pod.Spec.PriorityClassName != "" { a.logger.Infof("Adding priorityclass %s to AdditionalItems", pod.Spec.PriorityClassName) restoreExecuteOutput.AdditionalItems = []velero.ResourceIdentifier{ diff --git a/pkg/restore/pod_action_test.go b/pkg/restore/pod_action_test.go index f1aa83c1a3..c8121df673 100644 --- a/pkg/restore/pod_action_test.go +++ b/pkg/restore/pod_action_test.go @@ -28,6 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -229,7 +230,7 @@ func TestPodActionExecute(t *testing.T) { unstructuredPod, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredPod}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredPod}, Restore: nil, diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index f2d8df9a1d..bf7116b8b9 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -66,7 +67,7 @@ func (a *ResticRestoreAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *ResticRestoreAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ResticRestoreAction") defer a.logger.Info("Done executing ResticRestoreAction") @@ -99,7 +100,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu volumeSnapshots := podvolume.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) if len(volumeSnapshots) == 0 { log.Debug("No restic backups found for pod") - return velero.NewRestoreItemActionExecuteOutput(input.Item), nil + return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil } log.Info("Restic backups for pod found") @@ -171,7 +172,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu return nil, errors.Wrap(err, "unable to convert pod to runtime.Unstructured") } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } func getCommand(log logrus.FieldLogger, config *corev1.ConfigMap) []string { diff --git a/pkg/restore/restic_restore_action_test.go b/pkg/restore/restic_restore_action_test.go index b218f4b88d..660c1fc66c 100644 --- a/pkg/restore/restic_restore_action_test.go +++ b/pkg/restore/restic_restore_action_test.go @@ -35,7 +35,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/buildinfo" velerofake "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -278,7 +278,7 @@ func TestResticRestoreActionExecute(t *testing.T) { unstructuredPodFromBackup = unstructuredPod } - input := &velero.RestoreItemActionExecuteInput{ + input := &riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredPod, }, diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 930a28a6a5..da276e15b6 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -59,6 +59,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -86,7 +87,7 @@ type Request struct { type Restorer interface { // Restore restores the backup data from backupReader, returning warnings and errors. Restore(req Request, - actions []velero.RestoreItemAction, + actions []riav1.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, ) (Result, Result) @@ -162,7 +163,7 @@ func NewKubernetesRestorer( // respectively, summarizing info about the restore. func (kr *kubernetesRestorer) Restore( req Request, - actions []velero.RestoreItemAction, + actions []riav1.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, ) (Result, Result) { @@ -1150,7 +1151,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } ctx.log.Infof("Executing item action for %v", &groupResource) - executeOutput, err := action.RestoreItemAction.Execute(&velero.RestoreItemActionExecuteInput{ + executeOutput, err := action.RestoreItemAction.Execute(&riav1.RestoreItemActionExecuteInput{ Item: obj, ItemFromBackup: itemFromBackup, Restore: ctx.restore, diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 41b0743eb4..5b0e489346 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -48,6 +48,7 @@ import ( velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" uploadermocks "github.com/vmware-tanzu/velero/pkg/podvolume/mocks" "github.com/vmware-tanzu/velero/pkg/test" @@ -1147,17 +1148,17 @@ func (a *recordResourcesAction) AppliesTo() (velero.ResourceSelector, error) { return a.selector, nil } -func (a *recordResourcesAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *recordResourcesAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { metadata, err := meta.Accessor(input.Item) if err != nil { - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: a.additionalItems, }, err } a.ids = append(a.ids, kubeutil.NamespaceAndName(metadata)) - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: a.additionalItems, }, nil @@ -1318,7 +1319,7 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { h.AddItems(t, r) } - actions := []velero.RestoreItemAction{} + actions := []riav1.RestoreItemAction{} for action := range tc.actions { actions = append(actions, action) } @@ -1353,12 +1354,12 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { // function body at runtime. type pluggableAction struct { selector velero.ResourceSelector - executeFunc func(*velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) + executeFunc func(*riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) } -func (a *pluggableAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *pluggableAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { if a.executeFunc == nil { - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -1383,7 +1384,7 @@ func TestRestoreActionModifications(t *testing.T) { // method modifies the item being passed in by calling the 'modify' function on it. modifyingActionGetter := func(modify func(*unstructured.Unstructured)) *pluggableAction { return &pluggableAction{ - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { obj, ok := input.Item.(*unstructured.Unstructured) if !ok { return nil, errors.Errorf("unexpected type %T", input.Item) @@ -1392,7 +1393,7 @@ func TestRestoreActionModifications(t *testing.T) { res := obj.DeepCopy() modify(res) - return &velero.RestoreItemActionExecuteOutput{ + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: res, }, nil }, @@ -1405,7 +1406,7 @@ func TestRestoreActionModifications(t *testing.T) { backup *velerov1api.Backup apiResources []*test.APIResource tarball io.Reader - actions []velero.RestoreItemAction + actions []riav1.RestoreItemAction want []*test.APIResource }{ { @@ -1414,7 +1415,7 @@ func TestRestoreActionModifications(t *testing.T) { backup: defaultBackup().Result(), tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).Done(), apiResources: []*test.APIResource{test.Pods()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(map[string]string{"updated": "true"}) }), @@ -1431,7 +1432,7 @@ func TestRestoreActionModifications(t *testing.T) { backup: defaultBackup().Result(), tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result()).Done(), apiResources: []*test.APIResource{test.Pods()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(nil) }), @@ -1446,7 +1447,7 @@ func TestRestoreActionModifications(t *testing.T) { backup: defaultBackup().Result(), tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).Done(), apiResources: []*test.APIResource{test.Pods()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(map[string]string{"updated": "true"}) }).addSelector(velero.ResourceSelector{ @@ -1518,7 +1519,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) { backup *velerov1api.Backup tarball io.Reader apiResources []*test.APIResource - actions []velero.RestoreItemAction + actions []riav1.RestoreItemAction want map[*test.APIResource][]string }{ { @@ -1527,11 +1528,11 @@ func TestRestoreActionAdditionalItems(t *testing.T) { backup: defaultBackup().Result(), tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).Done(), apiResources: []*test.APIResource{test.Pods()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - return &velero.RestoreItemActionExecuteOutput{ + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.Pods, Namespace: "ns-2", Name: "pod-2"}, @@ -1550,10 +1551,10 @@ func TestRestoreActionAdditionalItems(t *testing.T) { backup: defaultBackup().Result(), tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).Done(), apiResources: []*test.APIResource{test.Pods()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - return &velero.RestoreItemActionExecuteOutput{ + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.Pods, Namespace: "ns-2", Name: "pod-2"}, @@ -1575,10 +1576,10 @@ func TestRestoreActionAdditionalItems(t *testing.T) { AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). Done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - return &velero.RestoreItemActionExecuteOutput{ + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, @@ -1601,10 +1602,10 @@ func TestRestoreActionAdditionalItems(t *testing.T) { AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). Done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - return &velero.RestoreItemActionExecuteOutput{ + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, @@ -1627,10 +1628,10 @@ func TestRestoreActionAdditionalItems(t *testing.T) { AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). Done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, - actions: []velero.RestoreItemAction{ + actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - return &velero.RestoreItemActionExecuteOutput{ + executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + return &riav1.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, diff --git a/pkg/restore/rolebinding_action.go b/pkg/restore/rolebinding_action.go index c402075a61..55820c21f9 100644 --- a/pkg/restore/rolebinding_action.go +++ b/pkg/restore/rolebinding_action.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // RoleBindingAction handle namespace remappings for role bindings @@ -41,10 +42,10 @@ func (a *RoleBindingAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *RoleBindingAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *RoleBindingAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { namespaceMapping := input.Restore.Spec.NamespaceMapping if len(namespaceMapping) == 0 { - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil } roleBinding := new(rbac.RoleBinding) @@ -63,5 +64,5 @@ func (a *RoleBindingAction) Execute(input *velero.RestoreItemActionExecuteInput) return nil, errors.WithStack(err) } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/rolebinding_action_test.go b/pkg/restore/rolebinding_action_test.go index 8995df8c6d..2a62cf6d0d 100644 --- a/pkg/restore/rolebinding_action_test.go +++ b/pkg/restore/rolebinding_action_test.go @@ -28,6 +28,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -89,7 +90,7 @@ func TestRoleBindingActionExecute(t *testing.T) { require.NoError(t, err) action := NewRoleBindingAction(test.NewLogger()) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: roleBindingUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: roleBindingUnstructured}, Restore: &api.Restore{ diff --git a/pkg/restore/service_account_action.go b/pkg/restore/service_account_action.go index 252d9fc576..424985a26e 100644 --- a/pkg/restore/service_account_action.go +++ b/pkg/restore/service_account_action.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -43,7 +44,7 @@ func (a *ServiceAccountAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ServiceAccountAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *ServiceAccountAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ServiceAccountAction") defer a.logger.Info("Done executing ServiceAccountAction") @@ -75,5 +76,5 @@ func (a *ServiceAccountAction) Execute(input *velero.RestoreItemActionExecuteInp return nil, errors.Wrap(err, "unable to convert serviceaccount to runtime.Unstructured") } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/service_account_action_test.go b/pkg/restore/service_account_action_test.go index 0bc976a0fa..f8bc76fcc8 100644 --- a/pkg/restore/service_account_action_test.go +++ b/pkg/restore/service_account_action_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -90,7 +91,7 @@ func TestServiceAccountActionExecute(t *testing.T) { require.NoError(t, err) action := NewServiceAccountAction(test.NewLogger()) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: saUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: saUnstructured}, Restore: nil, diff --git a/pkg/restore/service_action.go b/pkg/restore/service_action.go index 0b22cf0959..d21690182e 100644 --- a/pkg/restore/service_action.go +++ b/pkg/restore/service_action.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -47,7 +48,7 @@ func (a *ServiceAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (a *ServiceAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { service := new(corev1api.Service) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), service); err != nil { return nil, errors.WithStack(err) @@ -72,7 +73,7 @@ func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*v return nil, errors.WithStack(err) } - return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } func deleteNodePorts(service *corev1api.Service) error { diff --git a/pkg/restore/service_action_test.go b/pkg/restore/service_action_test.go index 59fef00a15..ccdef0729a 100644 --- a/pkg/restore/service_action_test.go +++ b/pkg/restore/service_action_test.go @@ -29,7 +29,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -377,7 +377,7 @@ func TestServiceActionExecute(t *testing.T) { unstructuredSvc, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ + res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredSvc}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredSvc}, Restore: test.restore, From 1ab7ebd80e6acb2fa8c62b2316e2d91c21394755 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Wed, 21 Sep 2022 10:24:19 +0800 Subject: [PATCH 309/366] add change log Signed-off-by: Niu Lechuan --- changelogs/unreleased/5362-niulechuan | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/5362-niulechuan diff --git a/changelogs/unreleased/5362-niulechuan b/changelogs/unreleased/5362-niulechuan new file mode 100644 index 0000000000..d528bfa8bb --- /dev/null +++ b/changelogs/unreleased/5362-niulechuan @@ -0,0 +1 @@ +Added backupController's UT to test the prepareBackupRequest() method BackupStorageLocation processing logic From 80430542df8760ff49074fd0685f102ba775758e Mon Sep 17 00:00:00 2001 From: danfengl Date: Sun, 18 Sep 2022 12:22:47 +0000 Subject: [PATCH 310/366] Add schedule backup timing E2E test Signed-off-by: danfengl --- changelogs/unreleased/5355-danfengliu | 1 + test/e2e/backups/schedule.go | 166 ++++++++++++++++++++++++++ test/e2e/e2e_suite_test.go | 1 + test/e2e/util/common/common.go | 2 +- test/e2e/util/k8s/common.go | 2 +- test/e2e/util/k8s/namespace.go | 2 +- test/e2e/util/velero/velero_utils.go | 72 ++++++++--- 7 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 changelogs/unreleased/5355-danfengliu create mode 100644 test/e2e/backups/schedule.go diff --git a/changelogs/unreleased/5355-danfengliu b/changelogs/unreleased/5355-danfengliu new file mode 100644 index 0000000000..e365367f33 --- /dev/null +++ b/changelogs/unreleased/5355-danfengliu @@ -0,0 +1 @@ +Add E2E test for schedule backup \ No newline at end of file diff --git a/test/e2e/backups/schedule.go b/test/e2e/backups/schedule.go new file mode 100644 index 0000000000..8cabe529c6 --- /dev/null +++ b/test/e2e/backups/schedule.go @@ -0,0 +1,166 @@ +package backups + +import ( + "context" + "fmt" + "math/rand" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/test" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" + . "github.com/vmware-tanzu/velero/test/e2e/util/velero" +) + +type ScheduleBackup struct { + TestCase + ScheduleName string + ScheduleArgs []string + Period int //Limitation: The unit is minitue only and 60 is divisible by it + randBackupName string + verifyTimes int +} + +var ScheduleBackupTest func() = TestFunc(&ScheduleBackup{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1"}}}) + +func (n *ScheduleBackup) Init() error { + n.Client = TestClientInstance + n.Period = 3 + n.verifyTimes = 5 // More verify times more confidence + n.TestMsg = &TestMSG{ + Desc: "Set up a scheduled backup defined by a Cron expression", + FailedMSG: "Failed to schedule a backup", + Text: "should backup periodly according to the schedule", + } + return nil +} + +func (n *ScheduleBackup) StartRun() error { + + n.ScheduleName = n.ScheduleName + "schedule-" + UUIDgen.String() + n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() + + n.ScheduleArgs = []string{ + "schedule", "create", "--namespace", VeleroCfg.VeleroNamespace, n.ScheduleName, + "--include-namespaces", strings.Join(*n.NSIncluded, ","), + "--schedule=*/" + fmt.Sprintf("%v", n.Period) + " * * * *", + } + + return nil +} +func (n *ScheduleBackup) CreateResources() error { + n.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + for _, ns := range *n.NSIncluded { + By(fmt.Sprintf("Creating namespaces %s ......\n", ns), func() { + Expect(CreateNamespace(n.Ctx, n.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) + }) + configmaptName := n.NSBaseName + fmt.Printf("Creating configmap %s in namespaces ...%s\n", configmaptName, ns) + _, err := CreateConfigMap(n.Client.ClientGo, ns, configmaptName, nil) + Expect(err).To(Succeed(), fmt.Sprintf("failed to create configmap in the namespace %q", ns)) + Expect(WaitForConfigMapComplete(n.Client.ClientGo, ns, configmaptName)).To(Succeed(), + fmt.Sprintf("ailed to ensure secret completion in namespace: %q", ns)) + } + return nil +} + +func (n *ScheduleBackup) Backup() error { + // Wait until the beginning of the given period to create schedule, it will give us + // a predictable period to wait for the first scheduled backup, and verify no immediate + // scheduled backup was created between schedule creation and first scheduled backup. + By(fmt.Sprintf("Creating schedule %s ......\n", n.ScheduleName), func() { + for i := 0; i < n.Period*60/30; i++ { + time.Sleep(30 * time.Second) + now := time.Now().Minute() + triggerNow := now % n.Period + if triggerNow == 0 { + Expect(VeleroCmdExec(n.Ctx, VeleroCfg.VeleroCLI, n.ScheduleArgs)).To(Succeed()) + break + } + } + }) + return nil +} +func (n *ScheduleBackup) Destroy() error { + By(fmt.Sprintf("Schedule %s is created without any delay\n", n.ScheduleName), func() { + creationTimestamp, err := GetSchedule(context.Background(), VeleroCfg.VeleroNamespace, n.ScheduleName) + Expect(err).To(Succeed()) + + creationTime, err := time.Parse(time.RFC3339, strings.Replace(creationTimestamp, "'", "", -1)) + Expect(err).To(Succeed()) + fmt.Printf("Schedule %s created at %s\n", n.ScheduleName, creationTime) + now := time.Now() + diff := creationTime.Sub(now) + Expect(diff.Minutes() < 1).To(Equal(true)) + }) + + By(fmt.Sprintf("No immediate backup is created by schedule %s\n", n.ScheduleName), func() { + for i := 0; i < n.Period; i++ { + time.Sleep(1 * time.Minute) + now := time.Now() + fmt.Printf("Get backup for #%d time at %v\n", i, now) + //Ignore the last minute in the period avoiding met the 1st backup by schedule + if i != n.Period-1 { + backupsInfo, err := GetScheduledBackupsCreationTime(context.Background(), VeleroCfg.VeleroCLI, "default", n.ScheduleName) + Expect(err).To(Succeed()) + Expect(len(backupsInfo) == 0).To(Equal(true)) + } + } + }) + + By("Delay one more minute to make sure the new backup was created in the given period", func() { + time.Sleep(1 * time.Minute) + }) + + By(fmt.Sprintf("Get backups every %d minute, and backups count should increase 1 more step in the same pace\n", n.Period), func() { + for i := 0; i < n.verifyTimes; i++ { + fmt.Printf("Start to sleep %d minute #%d time...\n", n.Period, i+1) + time.Sleep(time.Duration(n.Period) * time.Minute) + bMap := make(map[string]string) + backupsInfo, err := GetScheduledBackupsCreationTime(context.Background(), VeleroCfg.VeleroCLI, "default", n.ScheduleName) + Expect(err).To(Succeed()) + Expect(len(backupsInfo) == i+2).To(Equal(true)) + for index, bi := range backupsInfo { + bList := strings.Split(bi, ",") + fmt.Printf("Backup %d: %v\n", index, bList) + bMap[bList[0]] = bList[1] + _, err := time.Parse("2006-01-02 15:04:05 -0700 MST", bList[1]) + Expect(err).To(Succeed()) + } + if i == n.verifyTimes-1 { + backupInfo := backupsInfo[rand.Intn(len(backupsInfo))] + n.randBackupName = strings.Split(backupInfo, ",")[0] + } + } + }) + + n.BackupName = strings.Replace(n.randBackupName, " ", "", -1) + + By("Delete all namespaces", func() { + Expect(CleanupNamespacesWithPoll(n.Ctx, n.Client, n.NSBaseName)).To(Succeed(), "Could cleanup retrieve namespaces") + }) + + n.RestoreArgs = []string{ + "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, + "--from-backup", n.BackupName, + "--wait", + } + + return nil +} + +func (n *ScheduleBackup) Verify() error { + By("Namespaces were restored", func() { + for _, ns := range *n.NSIncluded { + configmap, err := GetConfigmap(n.Client.ClientGo, ns, n.NSBaseName) + fmt.Printf("Restored configmap is %v\n", configmap) + Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list configmap in namespace: %q\n", ns)) + } + + }) + return nil +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 9e509b3024..0f06f6a193 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -111,6 +111,7 @@ var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup dele var _ = Describe("[Backups][Deletion][Snapshot] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) var _ = Describe("[Backups][TTL] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", TTLTest) var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) +var _ = Describe("[Backups][Schedule] Backup will be created periodly by schedule defined by a Cron expression", ScheduleBackupTest) var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) diff --git a/test/e2e/util/common/common.go b/test/e2e/util/common/common.go index 17e942cab5..f7a43fc71b 100644 --- a/test/e2e/util/common/common.go +++ b/test/e2e/util/common/common.go @@ -38,7 +38,7 @@ func GetListBy2Pipes(ctx context.Context, cmdline1, cmdline2, cmdline3 OsCommand _ = c2.Wait() _ = c3.Wait() - fmt.Println(&b2) + //fmt.Println(&b2) scanner := bufio.NewScanner(&b2) var ret []string for scanner.Scan() { diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 394a8d506f..9032d36f22 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -63,7 +63,7 @@ func WaitForPods(ctx context.Context, client TestClient, namespace string, pods checkPod, err := client.ClientGo.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) if err != nil { //Should ignore "etcdserver: request timed out" kind of errors, try to get pod status again before timeout. - fmt.Println(errors.Wrap(err, fmt.Sprintf("Failed to verify pod %s/%s is %s, try again...", namespace, podName, corev1api.PodRunning))) + fmt.Println(errors.Wrap(err, fmt.Sprintf("Failed to verify pod %s/%s is %s, try again...\n", namespace, podName, corev1api.PodRunning))) return false, nil } // If any pod is still waiting we don't need to check any more so return and wait for next poll interval diff --git a/test/e2e/util/k8s/namespace.go b/test/e2e/util/k8s/namespace.go index cbdc644eb5..767aba7ba5 100644 --- a/test/e2e/util/k8s/namespace.go +++ b/test/e2e/util/k8s/namespace.go @@ -100,7 +100,7 @@ func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseNam if err != nil { return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name) } - fmt.Printf("Delete namespace %s", checkNamespace.Name) + fmt.Printf("Delete namespace %s\n", checkNamespace.Name) } } return nil diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index e7bc0404e4..3e3fc67f08 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -431,17 +431,19 @@ func VeleroScheduleCreate(ctx context.Context, veleroCLI string, veleroNamespace func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { cmd := exec.CommandContext(ctx, veleroCLI, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + var errBuf, outBuf bytes.Buffer + cmd.Stderr = io.MultiWriter(os.Stderr, &errBuf) + cmd.Stdout = io.MultiWriter(os.Stdout, &outBuf) fmt.Printf("velero cmd =%v\n", cmd) err := cmd.Run() - if strings.Contains(fmt.Sprint(cmd.Stdout), "Failed") { + retAll := outBuf.String() + " " + errBuf.String() + if strings.Contains(strings.ToLower(retAll), "failed") { return errors.New(fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) } if err != nil { return err } - return err + return nil } func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error { @@ -871,6 +873,43 @@ func GetBackupsFromBsl(ctx context.Context, veleroCLI, bslName string) ([]string return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) } +func GetScheduledBackupsCreationTime(ctx context.Context, veleroCLI, bslName, scheduleName string) ([]string, error) { + var creationTimes []string + backups, err := GetBackupsCreationTime(ctx, veleroCLI, bslName) + if err != nil { + return nil, err + } + for _, b := range backups { + if strings.Contains(b, scheduleName) { + creationTimes = append(creationTimes, b) + } + } + return creationTimes, nil +} +func GetBackupsCreationTime(ctx context.Context, veleroCLI, bslName string) ([]string, error) { + args1 := []string{"get", "backups"} + createdTime := "$1,\",\" $5,$6,$7,$8" + if strings.TrimSpace(bslName) != "" { + args1 = append(args1, "-l", "velero.io/storage-location="+bslName) + } + CmdLine1 := &common.OsCommandLine{ + Cmd: veleroCLI, + Args: args1, + } + + CmdLine2 := &common.OsCommandLine{ + Cmd: "awk", + Args: []string{"{print " + createdTime + "}"}, + } + + CmdLine3 := &common.OsCommandLine{ + Cmd: "tail", + Args: []string{"-n", "+2"}, + } + + return common.GetListBy2Pipes(ctx, *CmdLine1, *CmdLine2, *CmdLine3) +} + func GetAllBackups(ctx context.Context, veleroCLI string) ([]string, error) { return GetBackupsFromBsl(ctx, veleroCLI, "") } @@ -976,7 +1015,6 @@ func GetSnapshotCheckPoint(client TestClient, VeleroCfg VerleroConfig, expectCou } func GetBackupTTL(ctx context.Context, veleroNamespace, backupName string) (string, error) { - checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", "get", "backup", "-n", veleroNamespace, backupName, "-o=jsonpath='{.spec.ttl}'") fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) @@ -984,15 +1022,8 @@ func GetBackupTTL(ctx context.Context, veleroNamespace, backupName string) (stri if err != nil { fmt.Print(stdout) fmt.Print(stderr) - return "", errors.Wrap(err, "failed to verify") + return "", errors.Wrap(err, fmt.Sprintf("failed to run command %s", checkSnapshotCmd)) } - // lines := strings.Split(stdout, "\n") - // complete := true - // for _, curLine := range lines { - // fmt.Println(curLine) - - // } - // return complete, nil return stdout, err } @@ -1019,10 +1050,23 @@ func DeleteBackups(ctx context.Context, client TestClient) error { return fmt.Errorf("failed to list backup object in %s namespace with err %v", VeleroCfg.VeleroNamespace, err) } for _, backup := range backupList.Items { - fmt.Printf("Backup %s is going to be deleted...", backup.Name) + fmt.Printf("Backup %s is going to be deleted...\n", backup.Name) if err := VeleroBackupDelete(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup.Name); err != nil { return err } } return nil } + +func GetSchedule(ctx context.Context, veleroNamespace, scheduleName string) (string, error) { + checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", + "get", "schedule", "-n", veleroNamespace, scheduleName, "-o=jsonpath='{.metadata.creationTimestamp}'") + fmt.Printf("Cmd =%v\n", checkSnapshotCmd) + stdout, stderr, err := veleroexec.RunCommand(checkSnapshotCmd) + if err != nil { + fmt.Print(stdout) + fmt.Print(stderr) + return "", errors.Wrap(err, fmt.Sprintf("failed to run command %s", checkSnapshotCmd)) + } + return stdout, err +} From fdc23832cc6dac142b696a8c6675ed788f45ad94 Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 21 Sep 2022 02:26:00 +0000 Subject: [PATCH 311/366] Fix issue of fail to get command output for test verification When running velero backup/restore command, if the command result is "PartiallyFailed", it won't reture error as design, but we do need to know the debug information to figure out the reason, so the command output is needed to get the command result, then further action will be taken. Signed-off-by: danfengl --- test/e2e/util/velero/velero_utils.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index e7bc0404e4..9b68024a8f 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -431,17 +431,19 @@ func VeleroScheduleCreate(ctx context.Context, veleroCLI string, veleroNamespace func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { cmd := exec.CommandContext(ctx, veleroCLI, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + var errBuf, outBuf bytes.Buffer + cmd.Stderr = io.MultiWriter(os.Stderr, &errBuf) + cmd.Stdout = io.MultiWriter(os.Stdout, &outBuf) fmt.Printf("velero cmd =%v\n", cmd) err := cmd.Run() - if strings.Contains(fmt.Sprint(cmd.Stdout), "Failed") { + retAll := outBuf.String() + " " + errBuf.String() + if strings.Contains(strings.ToLower(retAll), "failed") { return errors.New(fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) } if err != nil { return err } - return err + return nil } func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error { From b6088356e6a6d25a781cecb4d33dde2f55c6906d Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 8 Sep 2022 18:09:18 -0400 Subject: [PATCH 312/366] plugin versioning v1 refactor for VolumeSnapshotter Signed-off-by: Scott Seago --- changelogs/unreleased/5318-sseago | 1 + pkg/backup/backup.go | 4 +- pkg/backup/backup_test.go | 31 ++++---- pkg/backup/item_backupper.go | 10 +-- pkg/controller/backup_deletion_controller.go | 6 +- pkg/plugin/clientmgmt/manager.go | 24 ++++--- pkg/plugin/clientmgmt/manager_test.go | 7 +- .../v1}/restartable_volume_snapshotter.go | 71 ++++++++++++------- .../restartable_volume_snapshotter_test.go | 36 +++++----- .../framework/volume_snapshotter_server.go | 8 +-- pkg/plugin/mocks/manager.go | 10 +-- .../v1/VolumeSnapshotter.go} | 0 .../v1}/volume_snapshotter.go | 4 +- pkg/restore/pv_restorer_test.go | 10 +-- pkg/restore/restore.go | 3 +- pkg/restore/restore_test.go | 27 +++---- 16 files changed, 142 insertions(+), 110 deletions(-) create mode 100644 changelogs/unreleased/5318-sseago rename pkg/plugin/clientmgmt/{ => volumesnapshotter/v1}/restartable_volume_snapshotter.go (66%) rename pkg/plugin/clientmgmt/{ => volumesnapshotter/v1}/restartable_volume_snapshotter_test.go (93%) rename pkg/plugin/velero/mocks/{volume_snapshotter.go => volumesnapshotter/v1/VolumeSnapshotter.go} (100%) rename pkg/plugin/velero/{ => volumesnapshotter/v1}/volume_snapshotter.go (97%) diff --git a/changelogs/unreleased/5318-sseago b/changelogs/unreleased/5318-sseago new file mode 100644 index 0000000000..edb2f09acf --- /dev/null +++ b/changelogs/unreleased/5318-sseago @@ -0,0 +1 @@ +plugin versioning v1 refactor for VolumeSnapshotter diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 2843cb54ba..71b1f62f04 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -44,8 +44,8 @@ import ( velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/framework" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -165,7 +165,7 @@ func getResourceHook(hookSpec velerov1api.BackupResourceHookSpec, discoveryHelpe } type VolumeSnapshotterGetter interface { - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) } // Backup backs up the items specified in the Backup, placing them in a gzip-compressed tar file diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index e218d2636a..ddabffa4df 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -48,6 +48,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" @@ -1831,10 +1832,10 @@ func TestBackupActionAdditionalItems(t *testing.T) { } // volumeSnapshotterGetter is a simple implementation of the VolumeSnapshotterGetter -// interface that returns velero.VolumeSnapshotters from a map if they exist. -type volumeSnapshotterGetter map[string]velero.VolumeSnapshotter +// interface that returns vsv1.VolumeSnapshotters from a map if they exist. +type volumeSnapshotterGetter map[string]vsv1.VolumeSnapshotter -func (vsg volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +func (vsg volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) { snapshotter, ok := vsg[name] if !ok { return nil, errors.New("volume snapshotter not found") @@ -1859,7 +1860,7 @@ type volumeInfo struct { snapshotErr bool } -// fakeVolumeSnapshotter is a test fake for the velero.VolumeSnapshotter interface. +// fakeVolumeSnapshotter is a test fake for the vsv1.VolumeSnapshotter interface. type fakeVolumeSnapshotter struct { // PVVolumeNames is a map from PV name to volume ID, used as the basis // for the GetVolumeID method. @@ -1982,7 +1983,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "", "type-1", 100, false), }, want: []*volume.Snapshot{ @@ -2015,7 +2016,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabels("failure-domain.beta.kubernetes.io/zone", "zone-1")).Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "zone-1", "type-1", 100, false), }, want: []*volume.Snapshot{ @@ -2049,7 +2050,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabels("topology.kubernetes.io/zone", "zone-1")).Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "zone-1", "type-1", 100, false), }, want: []*volume.Snapshot{ @@ -2083,7 +2084,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabelsMap(map[string]string{"failure-domain.beta.kubernetes.io/zone": "zone-1-deprecated", "topology.kubernetes.io/zone": "zone-1-ga"})).Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "zone-1-ga", "type-1", 100, false), }, want: []*volume.Snapshot{ @@ -2117,7 +2118,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "", "type-1", 100, true), }, want: []*volume.Snapshot{ @@ -2149,7 +2150,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "", "type-1", 100, false), }, want: nil, @@ -2164,7 +2165,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "", "type-1", 100, false), }, want: nil, @@ -2182,7 +2183,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{}, + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{}, want: nil, }, { @@ -2198,7 +2199,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-1").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter), }, want: nil, @@ -2218,7 +2219,7 @@ func TestBackupWithSnapshots(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter).WithVolume("pv-1", "vol-1", "", "type-1", 100, false), "another": new(fakeVolumeSnapshotter).WithVolume("pv-2", "vol-2", "", "type-2", 100, false), }, @@ -2686,7 +2687,7 @@ func TestBackupWithRestic(t *testing.T) { ), }, vsl: newSnapshotLocation("velero", "default", "default"), - snapshotterGetter: map[string]velero.VolumeSnapshotter{ + snapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "default": new(fakeVolumeSnapshotter). WithVolume("pv-1", "vol-1", "", "type-1", 100, false). WithVolume("pv-2", "vol-2", "", "type-1", 100, false), diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index df5d48cd26..87b092f3d4 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -41,7 +41,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/kuberesource" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/volume" @@ -58,7 +58,7 @@ type itemBackupper struct { volumeSnapshotterGetter VolumeSnapshotterGetter itemHookHandler hook.ItemHookHandler - snapshotLocationVolumeSnapshotters map[string]velero.VolumeSnapshotter + snapshotLocationVolumeSnapshotters map[string]vsv1.VolumeSnapshotter } const ( @@ -357,7 +357,7 @@ func (ib *itemBackupper) executeActions( // volumeSnapshotter instantiates and initializes a VolumeSnapshotter given a VolumeSnapshotLocation, // or returns an existing one if one's already been initialized for the location. -func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) (velero.VolumeSnapshotter, error) { +func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) (vsv1.VolumeSnapshotter, error) { if bs, ok := ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name]; ok { return bs, nil } @@ -372,7 +372,7 @@ func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeS } if ib.snapshotLocationVolumeSnapshotters == nil { - ib.snapshotLocationVolumeSnapshotters = make(map[string]velero.VolumeSnapshotter) + ib.snapshotLocationVolumeSnapshotters = make(map[string]vsv1.VolumeSnapshotter) } ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name] = bs @@ -447,7 +447,7 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie var ( volumeID, location string - volumeSnapshotter velero.VolumeSnapshotter + volumeSnapshotter vsv1.VolumeSnapshotter ) for _, snapshotLocation := range ib.backupRequest.SnapshotLocations { diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 4144244041..826470d703 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -39,7 +39,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -282,7 +282,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque if snapshots, err := backupStore.GetBackupVolumeSnapshots(backup.Name); err != nil { errs = append(errs, errors.Wrap(err, "error getting backup's volume snapshots").Error()) } else { - volumeSnapshotters := make(map[string]velero.VolumeSnapshotter) + volumeSnapshotters := make(map[string]vsv1.VolumeSnapshotter) for _, snapshot := range snapshots { log.WithField("providerSnapshotID", snapshot.Status.ProviderSnapshotID).Info("Removing snapshot associated with backup") @@ -392,7 +392,7 @@ func volumeSnapshottersForVSL( namespace, vslName string, client client.Client, pluginManager clientmgmt.Manager, -) (velero.VolumeSnapshotter, error) { +) (vsv1.VolumeSnapshotter, error) { vsl := &velerov1api.VolumeSnapshotLocation{} if err := client.Get(ctx, types.NamespacedName{ Namespace: namespace, diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index e5442ffd32..b94d986fa6 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -27,11 +27,13 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" + vsv1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1" riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" ) // Manager manages the lifecycles of plugins. @@ -40,7 +42,7 @@ type Manager interface { GetObjectStore(name string) (velero.ObjectStore, error) // GetVolumeSnapshotter returns the VolumeSnapshotter plugin for name. - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) // GetBackupItemActions returns all v1 backup item action plugins. GetBackupItemActions() ([]biav1.BackupItemAction, error) @@ -161,17 +163,21 @@ func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) { } // GetVolumeSnapshotter returns a restartableVolumeSnapshotter for name. -func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +func (m *manager) GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(common.PluginKindVolumeSnapshotter, name) - if err != nil { - return nil, err + for _, adaptedVolumeSnapshotter := range vsv1cli.AdaptedVolumeSnapshotters() { + restartableProcess, err := m.getRestartableProcess(adaptedVolumeSnapshotter.Kind, name) + // Check if plugin was not found + if errors.As(err, &pluginNotFoundErrType) { + continue + } + if err != nil { + return nil, err + } + return adaptedVolumeSnapshotter.GetRestartable(name, restartableProcess), nil } - - r := NewRestartableVolumeSnapshotter(name, restartableProcess) - - return r, nil + return nil, fmt.Errorf("unable to get valid VolumeSnapshotter for %q", name) } // GetBackupItemActions returns all backup item actions as restartableBackupItemActions. diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index 9796dcac87..17b51a52b2 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -30,6 +30,7 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" + vsv1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/test" @@ -176,9 +177,9 @@ func TestGetVolumeSnapshotter(t *testing.T) { return m.GetVolumeSnapshotter(name) }, func(name string, sharedPluginProcess process.RestartableProcess) interface{} { - return &restartableVolumeSnapshotter{ - key: process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name}, - sharedPluginProcess: sharedPluginProcess, + return &vsv1cli.RestartableVolumeSnapshotter{ + Key: process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name}, + SharedPluginProcess: sharedPluginProcess, } }, true, diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go b/pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter.go similarity index 66% rename from pkg/plugin/clientmgmt/restartable_volume_snapshotter.go rename to pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter.go index 1252306ea3..00a6742347 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go +++ b/pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "github.com/pkg/errors" @@ -22,25 +22,44 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" ) +// AdaptedVolumeSnapshotter is a volume snapshotter adapted to the v1 VolumeSnapshotter API +type AdaptedVolumeSnapshotter struct { + Kind common.PluginKind + + // Get returns a restartable VolumeSnapshotter for the given name and process, wrapping if necessary + GetRestartable func(name string, restartableProcess process.RestartableProcess) vsv1.VolumeSnapshotter +} + +func AdaptedVolumeSnapshotters() []AdaptedVolumeSnapshotter { + return []AdaptedVolumeSnapshotter{ + { + Kind: common.PluginKindVolumeSnapshotter, + GetRestartable: func(name string, restartableProcess process.RestartableProcess) vsv1.VolumeSnapshotter { + return NewRestartableVolumeSnapshotter(name, restartableProcess) + }, + }, + } +} + // RestartableVolumeSnapshotter is a volume snapshotter for a given implementation (such as "aws"). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method // call, the restartableVolumeSnapshotter asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. -type restartableVolumeSnapshotter struct { - key process.KindAndName - sharedPluginProcess process.RestartableProcess +type RestartableVolumeSnapshotter struct { + Key process.KindAndName + SharedPluginProcess process.RestartableProcess config map[string]string } // NewRestartableVolumeSnapshotter returns a new restartableVolumeSnapshotter. -func NewRestartableVolumeSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *restartableVolumeSnapshotter { +func NewRestartableVolumeSnapshotter(name string, sharedPluginProcess process.RestartableProcess) *RestartableVolumeSnapshotter { key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} - r := &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: sharedPluginProcess, + r := &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: sharedPluginProcess, } // Register our reinitializer so we can reinitialize after a restart with r.config. @@ -50,8 +69,8 @@ func NewRestartableVolumeSnapshotter(name string, sharedPluginProcess process.Re } // reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). -func (r *restartableVolumeSnapshotter) Reinitialize(dispensed interface{}) error { - volumeSnapshotter, ok := dispensed.(velero.VolumeSnapshotter) +func (r *RestartableVolumeSnapshotter) Reinitialize(dispensed interface{}) error { + volumeSnapshotter, ok := dispensed.(vsv1.VolumeSnapshotter) if !ok { return errors.Errorf("%T is not a VolumeSnapshotter!", dispensed) } @@ -60,13 +79,13 @@ func (r *restartableVolumeSnapshotter) Reinitialize(dispensed interface{}) error // getVolumeSnapshotter returns the volume snapshotter for this restartableVolumeSnapshotter. It does *not* restart the // plugin process. -func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnapshotter, error) { - plugin, err := r.sharedPluginProcess.GetByKindAndName(r.key) +func (r *RestartableVolumeSnapshotter) getVolumeSnapshotter() (vsv1.VolumeSnapshotter, error) { + plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key) if err != nil { return nil, err } - volumeSnapshotter, ok := plugin.(velero.VolumeSnapshotter) + volumeSnapshotter, ok := plugin.(vsv1.VolumeSnapshotter) if !ok { return nil, errors.Errorf("%T is not a VolumeSnapshotter!", plugin) } @@ -74,9 +93,9 @@ func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnap return volumeSnapshotter, nil } -// getDelegate restarts the plugin process (if needed) and returns the volume snapshotter for this restartableVolumeSnapshotter. -func (r *restartableVolumeSnapshotter) getDelegate() (velero.VolumeSnapshotter, error) { - if err := r.sharedPluginProcess.ResetIfNeeded(); err != nil { +// getDelegate restarts the plugin process (if needed) and returns the volume snapshotter for this RestartableVolumeSnapshotter. +func (r *RestartableVolumeSnapshotter) getDelegate() (vsv1.VolumeSnapshotter, error) { + if err := r.SharedPluginProcess.ResetIfNeeded(); err != nil { return nil, err } @@ -85,7 +104,7 @@ func (r *restartableVolumeSnapshotter) getDelegate() (velero.VolumeSnapshotter, // Init initializes the volume snapshotter instance using config. If this is the first invocation, r stores config for future // reinitialization needs. Init does NOT restart the shared plugin process. Init may only be called once. -func (r *restartableVolumeSnapshotter) Init(config map[string]string) error { +func (r *RestartableVolumeSnapshotter) Init(config map[string]string) error { if r.config != nil { return errors.Errorf("already initialized") } @@ -103,12 +122,12 @@ func (r *restartableVolumeSnapshotter) Init(config map[string]string) error { // init calls Init on volumeSnapshotter with config. This is split out from Init() so that both Init() and reinitialize() may // call it using a specific VolumeSnapshotter. -func (r *restartableVolumeSnapshotter) init(volumeSnapshotter velero.VolumeSnapshotter, config map[string]string) error { +func (r *RestartableVolumeSnapshotter) init(volumeSnapshotter vsv1.VolumeSnapshotter, config map[string]string) error { return volumeSnapshotter.Init(config) } // CreateVolumeFromSnapshot restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) CreateVolumeFromSnapshot(snapshotID string, volumeType string, volumeAZ string, iops *int64) (volumeID string, err error) { +func (r *RestartableVolumeSnapshotter) CreateVolumeFromSnapshot(snapshotID string, volumeType string, volumeAZ string, iops *int64) (volumeID string, err error) { delegate, err := r.getDelegate() if err != nil { return "", err @@ -117,7 +136,7 @@ func (r *restartableVolumeSnapshotter) CreateVolumeFromSnapshot(snapshotID strin } // GetVolumeID restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) GetVolumeID(pv runtime.Unstructured) (string, error) { +func (r *RestartableVolumeSnapshotter) GetVolumeID(pv runtime.Unstructured) (string, error) { delegate, err := r.getDelegate() if err != nil { return "", err @@ -126,7 +145,7 @@ func (r *restartableVolumeSnapshotter) GetVolumeID(pv runtime.Unstructured) (str } // SetVolumeID restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) { +func (r *RestartableVolumeSnapshotter) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) { delegate, err := r.getDelegate() if err != nil { return nil, err @@ -135,7 +154,7 @@ func (r *restartableVolumeSnapshotter) SetVolumeID(pv runtime.Unstructured, volu } // GetVolumeInfo restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) GetVolumeInfo(volumeID string, volumeAZ string) (string, *int64, error) { +func (r *RestartableVolumeSnapshotter) GetVolumeInfo(volumeID string, volumeAZ string) (string, *int64, error) { delegate, err := r.getDelegate() if err != nil { return "", nil, err @@ -144,7 +163,7 @@ func (r *restartableVolumeSnapshotter) GetVolumeInfo(volumeID string, volumeAZ s } // CreateSnapshot restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) CreateSnapshot(volumeID string, volumeAZ string, tags map[string]string) (snapshotID string, err error) { +func (r *RestartableVolumeSnapshotter) CreateSnapshot(volumeID string, volumeAZ string, tags map[string]string) (snapshotID string, err error) { delegate, err := r.getDelegate() if err != nil { return "", err @@ -153,7 +172,7 @@ func (r *restartableVolumeSnapshotter) CreateSnapshot(volumeID string, volumeAZ } // DeleteSnapshot restarts the plugin's process if needed, then delegates the call. -func (r *restartableVolumeSnapshotter) DeleteSnapshot(snapshotID string) error { +func (r *RestartableVolumeSnapshotter) DeleteSnapshot(snapshotID string) error { delegate, err := r.getDelegate() if err != nil { return err diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go b/pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter_test.go similarity index 93% rename from pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go rename to pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter_test.go index d7d0dfc3d6..787191ec05 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter_test.go +++ b/pkg/plugin/clientmgmt/volumesnapshotter/v1/restartable_volume_snapshotter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clientmgmt +package v1 import ( "testing" @@ -28,7 +28,7 @@ import ( "github.com/vmware-tanzu/velero/internal/restartabletest" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" - providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" + providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/volumesnapshotter/v1" ) func TestRestartableGetVolumeSnapshotter(t *testing.T) { @@ -64,9 +64,9 @@ func TestRestartableGetVolumeSnapshotter(t *testing.T) { key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) - r := &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: p, + r := &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: p, } a, err := r.getVolumeSnapshotter() if tc.expectedError != "" { @@ -87,9 +87,9 @@ func TestRestartableVolumeSnapshotterReinitialize(t *testing.T) { name := "aws" key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} - r := &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: p, + r := &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: p, config: map[string]string{ "color": "blue", }, @@ -120,9 +120,9 @@ func TestRestartableVolumeSnapshotterGetDelegate(t *testing.T) { p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() name := "aws" key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} - r := &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: p, + r := &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: p, } a, err := r.getDelegate() assert.Nil(t, a) @@ -148,9 +148,9 @@ func TestRestartableVolumeSnapshotterInit(t *testing.T) { // getVolumeSnapshottererror name := "aws" key := process.KindAndName{Kind: common.PluginKindVolumeSnapshotter, Name: name} - r := &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: p, + r := &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: p, } p.On("GetByKindAndName", key).Return(nil, errors.Errorf("GetByKindAndName error")).Once() @@ -201,9 +201,9 @@ func TestRestartableVolumeSnapshotterDelegatedFunctions(t *testing.T) { t, common.PluginKindVolumeSnapshotter, func(key process.KindAndName, p process.RestartableProcess) interface{} { - return &restartableVolumeSnapshotter{ - key: key, - sharedPluginProcess: p, + return &RestartableVolumeSnapshotter{ + Key: key, + SharedPluginProcess: p, } }, func() restartabletest.Mockable { diff --git a/pkg/plugin/framework/volume_snapshotter_server.go b/pkg/plugin/framework/volume_snapshotter_server.go index e0073db9aa..99bdea03a8 100644 --- a/pkg/plugin/framework/volume_snapshotter_server.go +++ b/pkg/plugin/framework/volume_snapshotter_server.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" ) // VolumeSnapshotterGRPCServer implements the proto-generated VolumeSnapshotterServer interface, and accepts @@ -34,13 +34,13 @@ type VolumeSnapshotterGRPCServer struct { mux *common.ServerMux } -func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (velero.VolumeSnapshotter, error) { +func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (vsv1.VolumeSnapshotter, error) { impl, err := s.mux.GetHandler(name) if err != nil { return nil, err } - volumeSnapshotter, ok := impl.(velero.VolumeSnapshotter) + volumeSnapshotter, ok := impl.(vsv1.VolumeSnapshotter) if !ok { return nil, errors.Errorf("%T is not a volume snapshotter", impl) } diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index 0a0288b065..c99e1bf288 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -11,6 +11,8 @@ import ( v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + + volumesnapshotterv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" ) // Manager is an autogenerated mock type for the Manager type @@ -231,15 +233,15 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct } // GetVolumeSnapshotter provides a mock function with given fields: name -func (_m *Manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.VolumeSnapshotter, error) { ret := _m.Called(name) - var r0 velero.VolumeSnapshotter - if rf, ok := ret.Get(0).(func(string) velero.VolumeSnapshotter); ok { + var r0 volumesnapshotterv1.VolumeSnapshotter + if rf, ok := ret.Get(0).(func(string) volumesnapshotterv1.VolumeSnapshotter); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.VolumeSnapshotter) + r0 = ret.Get(0).(volumesnapshotterv1.VolumeSnapshotter) } } diff --git a/pkg/plugin/velero/mocks/volume_snapshotter.go b/pkg/plugin/velero/mocks/volumesnapshotter/v1/VolumeSnapshotter.go similarity index 100% rename from pkg/plugin/velero/mocks/volume_snapshotter.go rename to pkg/plugin/velero/mocks/volumesnapshotter/v1/VolumeSnapshotter.go diff --git a/pkg/plugin/velero/volume_snapshotter.go b/pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotter.go similarity index 97% rename from pkg/plugin/velero/volume_snapshotter.go rename to pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotter.go index 1a4e38bc0b..2c52b753f9 100644 --- a/pkg/plugin/velero/volume_snapshotter.go +++ b/pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotter.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/restore/pv_restorer_test.go b/pkg/restore/pv_restorer_test.go index c4fb0a9db8..f54ff0f5b5 100644 --- a/pkg/restore/pv_restorer_test.go +++ b/pkg/restore/pv_restorer_test.go @@ -29,8 +29,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" - providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" + providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/volumesnapshotter/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/volume" ) @@ -187,7 +187,7 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var ( volumeSnapshotter = new(providermocks.VolumeSnapshotter) - volumeSnapshotterGetter = providerToVolumeSnapshotterMap(map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter = providerToVolumeSnapshotterMap(map[string]vsv1.VolumeSnapshotter{ tc.expectedProvider: volumeSnapshotter, }) locationsInformer = informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 0).Velero().V1().VolumeSnapshotLocations() @@ -217,9 +217,9 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) { } } -type providerToVolumeSnapshotterMap map[string]velero.VolumeSnapshotter +type providerToVolumeSnapshotterMap map[string]vsv1.VolumeSnapshotter -func (g providerToVolumeSnapshotterMap) GetVolumeSnapshotter(provider string) (velero.VolumeSnapshotter, error) { +func (g providerToVolumeSnapshotterMap) GetVolumeSnapshotter(provider string) (vsv1.VolumeSnapshotter, error) { if bs, ok := g[provider]; !ok { return nil, errors.New("volume snapshotter not found for provider") } else { diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index da276e15b6..8705ae2127 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -60,6 +60,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -70,7 +71,7 @@ import ( ) type VolumeSnapshotterGetter interface { - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) } type Request struct { diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 5b0e489346..f1575bc185 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -49,6 +49,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" uploadermocks "github.com/vmware-tanzu/velero/pkg/podvolume/mocks" "github.com/vmware-tanzu/velero/pkg/test" @@ -1876,10 +1877,10 @@ func assertRestoredItems(t *testing.T, h *harness, want []*test.APIResource) { } // volumeSnapshotterGetter is a simple implementation of the VolumeSnapshotterGetter -// interface that returns velero.VolumeSnapshotters from a map if they exist. -type volumeSnapshotterGetter map[string]velero.VolumeSnapshotter +// interface that returns vsv1.VolumeSnapshotters from a map if they exist. +type volumeSnapshotterGetter map[string]vsv1.VolumeSnapshotter -func (vsg volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +func (vsg volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error) { snapshotter, ok := vsg[name] if !ok { return nil, errors.New("volume snapshotter not found") @@ -1888,7 +1889,7 @@ func (vsg volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (velero.Vol return snapshotter, nil } -// volumeSnapshotter is a test fake for the velero.VolumeSnapshotter interface +// volumeSnapshotter is a test fake for the vsv1.VolumeSnapshotter interface type volumeSnapshotter struct { // a map from snapshotID to volumeID snapshotVolumes map[string]string @@ -2052,7 +2053,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-volume"}, }, @@ -2101,7 +2102,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-volume"}, }, @@ -2155,7 +2156,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ // the volume snapshotter fake is not configured with any snapshotID -> volumeID // mappings as a way to verify that the snapshot is not restored, since if it were // restored, we'd get an error of "snapshot not found". @@ -2207,7 +2208,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ // the volume snapshotter fake is not configured with any snapshotID -> volumeID // mappings as a way to verify that the snapshot is not restored, since if it were // restored, we'd get an error of "snapshot not found". @@ -2258,7 +2259,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-volume"}, }, @@ -2319,7 +2320,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-volume"}, }, @@ -2467,7 +2468,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-pvname"}, pvName: map[string]string{"new-pvname": "new-pvname"}, @@ -2539,7 +2540,7 @@ func TestRestorePersistentVolumes(t *testing.T) { }, }, }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ // the volume snapshotter fake is not configured with any snapshotID -> volumeID // mappings as a way to verify that the snapshot is not restored, since if it were // restored, we'd get an error of "snapshot not found". @@ -2591,7 +2592,7 @@ func TestRestorePersistentVolumes(t *testing.T) { volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{ builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "default").Provider("provider-1").Result(), }, - volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{ + volumeSnapshotterGetter: map[string]vsv1.VolumeSnapshotter{ "provider-1": &volumeSnapshotter{ snapshotVolumes: map[string]string{"snapshot-1": "new-volume"}, pvName: map[string]string{"new-volume": "volumesnapshotter-renamed-source-pv"}, From c81f0db886b0b5aba39b3d44d269b80422885617 Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Fri, 23 Sep 2022 09:13:36 +0800 Subject: [PATCH 313/366] rename pvbr param (#5370) Signed-off-by: Lyndon-Li --- changelogs/unreleased/5370-lyndon | 1 + config/crd/v1/bases/velero.io_backups.yaml | 12 +- config/crd/v1/bases/velero.io_schedules.yaml | 12 +- config/crd/v1/crds/crds.go | 4 +- pkg/apis/velero/v1/backup.go | 10 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 5 + pkg/backup/backup.go | 40 +-- pkg/backup/backup_test.go | 2 +- pkg/backup/item_backupper.go | 4 +- pkg/builder/backup_builder.go | 6 + pkg/cmd/cli/backup/create.go | 40 +-- pkg/cmd/cli/install/install.go | 138 ++++----- pkg/cmd/cli/schedule/create.go | 26 +- pkg/cmd/server/server.go | 15 +- pkg/controller/backup_controller.go | 21 +- pkg/controller/backup_controller_test.go | 285 ++++++++++++------ pkg/install/deployment.go | 42 +-- pkg/install/deployment_test.go | 4 +- pkg/install/resources.go | 56 ++-- pkg/podvolume/util.go | 10 +- pkg/podvolume/util_test.go | 48 +-- pkg/restic/common.go | 4 - test/e2e/basic/namespace-mapping.go | 2 +- test/e2e/basic/resources-check/namespaces.go | 2 +- .../resources-check/namespaces_annotation.go | 2 +- test/e2e/basic/resources-check/rbac.go | 2 +- .../e2e/orderedresources/ordered_resources.go | 2 +- test/e2e/resource-filtering/base.go | 2 +- test/e2e/resource-filtering/exclude_label.go | 2 +- .../resource-filtering/exclude_namespaces.go | 4 +- .../resource-filtering/exclude_resources.go | 4 +- .../resource-filtering/include_namespaces.go | 4 +- .../resource-filtering/include_resources.go | 4 +- test/e2e/resource-filtering/label_selector.go | 2 +- test/e2e/util/velero/velero_utils.go | 8 +- 35 files changed, 491 insertions(+), 334 deletions(-) create mode 100644 changelogs/unreleased/5370-lyndon diff --git a/changelogs/unreleased/5370-lyndon b/changelogs/unreleased/5370-lyndon new file mode 100644 index 0000000000..11351c3174 --- /dev/null +++ b/changelogs/unreleased/5370-lyndon @@ -0,0 +1 @@ +Pod Volume Backup/Restore Refactor: Rename parameters in CRDs and commands to remove "Restic" word \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index eebfedf705..e204e1157b 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -42,9 +42,17 @@ spec: CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + defaultVolumesToFsBackup: + description: DefaultVolumesToFsBackup specifies whether pod volume + file system backup should be used for all volumes by default. + nullable: true + type: boolean defaultVolumesToRestic: - description: DefaultVolumesToRestic specifies whether restic should - be used to take a backup of all pod volumes by default. + description: "DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. \n Deprecated: + this field is no longer used and will be removed entirely in future. + Use DefaultVolumesToFsBackup instead." + nullable: true type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index 246bafb3f7..cd2010c504 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -72,9 +72,17 @@ spec: for CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + defaultVolumesToFsBackup: + description: DefaultVolumesToFsBackup specifies whether pod volume + file system backup should be used for all volumes by default. + nullable: true + type: boolean defaultVolumesToRestic: - description: DefaultVolumesToRestic specifies whether restic should - be used to take a backup of all pod volumes by default. + description: "DefaultVolumesToRestic specifies whether restic + should be used to take a backup of all pod volumes by default. + \n Deprecated: this field is no longer used and will be removed + entirely in future. Use DefaultVolumesToFsBackup instead." + nullable: true type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 14cd3c51ba..44ff941276 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,14 +30,14 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Msܸrw\xfd\x8a.\xe5\xe0\xf7\xaa4\xa3u吔n^\xd9[Q\xedƫ\xb2\xf4\x9cC*\a\f\xd93\x83\x15\t\xf0\x01\xe0ȓT\xfe{\xaa\x1b\xe0\xe7\x80$F+\xbd\xb7/e\\\xec!\x81F\xa3\xd1\xe8/4[\x17\xab\xd5\xeaBT\xf2+\x1a+\xb5\xba\x01QI\xfc\xe6P\xd1/\xbb~\xfaW\xbb\x96\xfa\xfa\xf0\xfe\xe2I\xaa\xfc\x06nk\xebt\xf9\x05\xad\xaeM\x86\x1fq+\x95tR\xab\x8b\x12\x9dȅ\x137\x17\x00B)\xed\x04=\xb6\xf4\x13 \xd3\xca\x19]\x14hV;T\xeb\xa7z\x83\x9bZ\x169\x1a\x06\xdeL}\xf8a\xfd/\xeb\x1f.\x002\x83<\xfcQ\x96h\x9d(\xab\x1bPuQ\\\x00(Q\xe2\rlD\xf6TWv}\xc0\x02\x8d^K}a+\xcch\xae\x9d\xd1uu\x03\xdd\v?$\xe0\xe1\xd7\xf0#\x8f\xe6\a\x85\xb4\xee\xe7\xde\xc3_\xa4u\xfc\xa2*j#\x8av&~f\xa5\xdaՅ0\xcd\xd3\v\x00\x9b\xe9\no\xe03MQ\x89\f\xf3\v\x80\xb0\x1c\x9er\x15\x10>\xbc\xf7\x10\xb2=\x96\xc2\xe3\x02\xa0+T\x1f\xee\xef\xbe\xfe\xf3\xc3\xe01@\x8e63\xb2rL\x14\x8f\x18H\v\x02\xbe\xf2\xb2\xc0\x04\xf2\x83\xdb\v\a\x06+\x83\x16\x95\xb3\xe0\xf6\b\x99\xa8\\m\x10\xf4\x16~\xae7h\x14:\xb4-h\x80\xac\xa8\xadC\x03\xd6\t\x87 \x1c\b\xa8\xb4T\x0e\xa4\x02'K\x84?}\xb8\xbf\x03\xbd\xf9\r3gA\xa8\x1c\x84\xb5:\x93\xc2a\x0e\a]\xd4%\xfa\xb1\u007f^\xb7P+\xa3+4N6t\xf6\xad\xc7U\xbd\xa7\xa3\xe5\xbd#\n\xf8^\x90\x13;\xa1_F\xa0\"\xe6\x81h\xb4\x1e\xb7\x97\xb6[.s\xc8\x000P'\xa1\x02\xf2kx@C`\xc0\xeeu]\xe4ą\a4D\xb0L\xef\x94\xfc\xef\x16\xb6\x05\xa7y\xd2B8\f\f\xd05\xa9\x1c\x1a%\n8\x88\xa2\xc6+&I)\x8e`\x90f\x81Z\xf5\xe0q\x17\xbb\x86\u007f\xd7\x06A\xaa\xad\xbe\x81\xbds\x95\xbd\xb9\xbe\xdeIל\xa6L\x97e\xad\xa4;^\xf3\xc1\x90\x9b\xdaic\xafs<`qm\xe5n%L\xb6\x97\x0e3\xda\xc8kQ\xc9\x15\xa3\xae\xf8D\xad\xcb\xfc\x9f\x1a\x06\xb0\xef\x06\xb8\xba#1\xa3uF\xaa]\xef\x05s\xfd\xcc\x0e\xd0\x01\xf0\xfc\xe5\x87\xfaUt\x84\xa6GD\x9d/\x9f\x1e\x1e\xfb\xbc'\xed\x98\xfaL\xf7\x1eCv[@\x04\x93j\x8b\xc6o\xe2\xd6\xe8\x92a\xa2\xca=\xf71\xeb\x16\x12\u0558\xfc\xb6ޔ\xd2Ѿ\xff\xb5FKL\xae\xd7p\xcb\"\x066\bu\x95\x13g\xae\xe1N\xc1\xad(\xb1\xb8\x15\x16\xdf|\x03\x88\xd2vE\x84Mۂ\xbet\x1cw\xf6T\xeb\xbdhd\xd9\xc4~y\x81\xf0Pa6804Jne\xc6\xc7\x02\xb6\xdat\xf2\u008b\xab\xf5\x00d\xfc\xc8Rˬ|P\xa2\xb2{\xedH\xfe\xeaڍ{\x8c\x10\xba}\xb8\x1b\rh\x90\t\xa8\xb1X\xa9-\xe6tΞ\x85t\x84\xde\tL @\xf0\x95%L\x03\x8f%Mm\xc1\xd5F\xf1)\xfd\x82\"?>\xea\xbfX\x84\xbcffmt\xc5\x15lp\xab\rF\xe0\x1a\xa4\xf1\xd4\x19\x8d!\xc2XFI\xd7n\r\x8f{$2\x8a\xbap\x81聾\xf7?@)U\xedp}\x02mb\x83=Q\x18\x8c_\x81}\xd4_\xd0:\x99-\x10\xefctP\x8f\x80\xcf{t{4t\xf0\xf8\x05˲\xc8\"7\x1d\x89\x9dxB\x10a\xdbY&\x16\x05T\xba\x11\xdf\x166\xc7\x06٩\x05n\xb4.P\x8c\xc5+~ˊ:Ǽ\xd5w'\xcc3Zݧ\x93\x01l\v\b\xa9Hܐ\xf6%\xf4T\xf7\x964Zdq\xc2 Ё\x97\xca\xc3ce\xb5\xc7(gS\x93\x0e\xcb\bn\xb3\xdb\alc\x88M\x817\xe0L}\xcaH~\xac0F\x1c'\xe8\xd2\xd8E\xa9di\xfb\a\xf1[Ȍ\x15w+d\x992^͋(k\xff\x81\x89\xb2\xd7\xfai\x89\x10\xffF}:\x85\x01\x19\x9b\x97\xb0\xc1\xbd8Hm\xc2҃\xfe\xde \xe07\xccj\x871\xfe\x17\x0er\xb9ݢ!8\xd5^X\xb4\xdef\x98&ȴ\f\x04\x96\x1a\x93\x9by\xb2\x8en#\x89Sy\xe5S\xa8Ӂ\x1e\x9f\xab\xa6\x11\xa2$\xa6\xc8\xdeS\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf1:]\x0f\xccm\xf2\t\xce^\x8f4\x98\xd3N\ft\x8aV\b\xda@I\x8a\xf4\xb4\xebX\xf5wmj\xd9\x1bA\xd2I{\x165u\x816L\x95\xb3\xb2\xead\xc0\xd5$\xe8vG\xbc\x11V\x88\r\x16`\xb1\xc0\xcci\x13'\xc7\xd2&\xfb\x96\"\xd7&\xa8\x18\x91pC\xe5\xd7-l\x06$\xb0f\xdc\xcbl\xef\xed#\xe2 \x86\x03\xb9F˧\\TUq\x9cZ$,\xed|\x98d\xee\xa0wm\xe1ȏ\xe1\xc5\x0e\u007f\xd7\x12dc\xd7\x16\xa4䐲-;\x80ӳ\xcb\xfe\xffI\xd8F쿀i\xefN\x86\xbe.\xd3\x12I%\xf9Aw[\xc0\xb2r\xc7+\x90\xaey\xba\x04\x91\x8c\x95n\xfe\u007f\xe0\x8d9\x9f\xe3\xef\xc6#_\x95\xe3gwe\t\"\xedJ;\xfd?র\xb2x\b\xba\"yC~鏺\x02\xb9m7$\xbf\x82\xad,\x1c\x9a\xd1\xce\xfc\xae\xf3\xf2\x1a\xc4H\xd1w\xd4J\xe1\xb2\xfd\xa7ody\xd9.R\x97H\x97\xf1`o\xbf6\xf6\xfcP1/\xc0\x05\xf6\xec\xa5\xc1\xd2G\f\x1e\x99\x9a\xdd\x13\xb6\xa8>|\xfe\x88\xf9\x1cy \x8d\xf3N\x16\xf2a\x84l\u007f\xea`\x94\xa7.#\x98>\xad\u007f\xe3cAW \xe0\t\x8f\xdeb\x11\nhs\x04M4\xe1\xe9\x9c\x12\x87\x83R\xccdOxd0!ʴ8:\x95\x15|{\xc2cJ\xb7\x11\x01\t'iC\xf4\x8c(I\x0f\x98\x10\x1c\x94H'\x1epİ\x91Eˋ\x83tAҴ\x86\xf6/Xf\xbbm\xbdh+o\xec;뷈N\xc1^V\x89\v%5\a\x16\xf9\xb441ï\xa2\x90y;\x91\xe7\xfb;5m\r\x0f\xdbg\xed\xee\xd4\x15|\xfa&m\b\xdb~\xd4h?k\xc7Oބ\x9c\x1e\xf1\x17\x10\xd3\x0f\xe4㥼\xd8&:\xf4\x83\x8f\t\xcc\xed\u06dd\xf7\xf0\xda\xed\x91\x16\xee\x14\xf9-\x81\x1e\x1cJ\xf6\xd3\xcd\xeb\x87a+k\xcb\xd1E\xa5ՊU\xe5:6\x93'v\"Hm\x06;r\x8aZ;\xa9\x9f0\x11\xec#i\x12?\xde\a\xc7\v\x91a\xde\x04\xc78\xa4+\x1c\xeed\x06%\x9aݜ\xe2跊\xe4{\x1a\n\x89R\u05f739,M\xb57-\x88\xee|\x19\x99\x15\x9d܄^\xcdf/v\x9d\x88\xe4Nw]^\x11\xabX\xb6?\x16\xa9+\xf2\x9c/\xe1Dq\u007f\x86\xc4?c/Nu\xbfG\xcck\xc8Rp\x90\xf1\u007fH\xcd1C\xff/TB\x9a\x843\xfc\x81\xef\xd4\n\x1c\x8c\rQ\xac\xfe44\x83\xb4@\xfb{\x10\xc5\xe9\x1dAdq\x9ad\v\x16^\x91\xeb\xed\x89\xc5r\x05\xcf{m\xbdN\xddJ\x8c\x86T\x87MZ\xb8|\xc2\xe3\xe5Չ\x1c\xb8\xbcS\x97^\xc1\x9f-nZkA\xab\xe2\b\x97<\xf6\xf2\xf7\x18A\x89\x9c\x98ԍ\xef.SMe\xf2%\x1bK\x80\x06\xb6\x17vd\xe6\xcea\x9dć\x95\xb6\x91k\x88\tT\xee\xb5u>\xb280Kωb\x81\xe7\xa1\x10\xbd\x02\xb1\xf5W\xa6\xda4\x97a$\xf6F\x01W\xda5;/ai\x1bۈ\x98\aJ\x8e\xd5ew\x82\xbd<\xbd\xf47d<\x89\xc8ظX\x84[\x19\x9d\xa1\xb5\xf3,\x92 \xad\x17\x82\x84m\x80Px\a\xc6\xdf4\xcd\a%\x9b\x96n\x90\x12\x91\xce4\xe5?}\xebE/\xe9\xf0\xd3\xef%\xe6;\x17/\xe03[\x96b|\xa5\x9a\x84\xe2\xad\x1f\xd9\x1c\x93\x00Ȼ\x06fW\xf3QO\xb7 \x03#\xfd\x11\xd4t)\xd5\x1dO\x00\xef_]\xad\xb7B\x12_b\xb8\xdf6c;\xa2\xb7\x0f\xf8\xf4\xa6ZD\x9a#\xf7\x06\a;w\x1a\xe7&C1\x11\xa4Ү\x1fN \xb8\x95\xce\xdfY\xd8Jc]\x1f\xd1T\xa6\xa8\x17N\u007f\xd7\xce\xf5\x9c\xd4'c^\xe48\xfd\xeaG\xf6\x02Y{\xfd\xdc\\LO^f\xc6\x1a_\n!\xc8-H\a\xa82]+\x0e\xbf\xd0Q\xe7)\xfc\x16x\x01\x9dL\xb24\x01A\rU]\xa6\x11`\xc5\\'\xd5l\x9c\xa6\xdf\xfd'!\x8b\xb7\xd867u\u007f\x1fk\x83mk.\xf2\xfb\x19\x06\xa5\xf8&˺\x04Q\x12\xe9Sݞ\xad\xbf\xfe\x1f\xecx\x9b\x04\xc0pY\x8d8M\x87\xaa*Х\x9eH\u007f\xddO\xc7\xc4\xca\x1c[\xc5\x1c\xb8@+\x10\xb0\x15\xb2\xa8M\xa2\x84<\x8b\xb6\xe7\xf8\x1aAX\xbc\x9e\x13\x916\xf9\x8aI\x91\x10\x88M4\x16\xe7\xa5ue\xd2M\xc5{\x83i\xe6\xd9RP\xba1\xcf*#\x89\x97\xf4k[h\x81ń:~7\xd1N\xdaw\x13m\xa1}7\xd1&\xdbw\x13m\xb9}7\xd1B\xfbn\xa25\xed\xbb\x89\xf6\xddD\x9b\xeb6'\xad\x970\xf2\x9f*L\xbc\\\xc4\"\xe1zz\x0e\xc5\x19\xf8!\x9b\xe2\xd6\u007f\xb6\x90\x9aay\x17\x1f\x15ɫ\r\xdfC\xac\xf8S\x8e\x18\atI\x17\x9d*iS.\xe9\x804\xec\xed3\xaf\x17\x920\x93\xd2)\xe3ٷ)\t?Ki>\xc3<\xd36ͦI4\xd5\xcd$\x11:4\x9f\x84\x90\xd9\xdb\xcf!\x19\xe6밝\xdb`\xfaw\xcfAMH\xc5YH\xc0\x99O̝\xa3\xd7\xc8\xf5\x18\x12\xcc\f\x12F\xff0\xf4ZȒ\x99\u038d\t7A\xe8\xc4\xe1\xfdz\xf8\xc6\xe9\x90)\x03\xcf\xd2\xed#Kyޣ\xe2;,\xb5맽6\xfc\x16\xbe\xcd\x19\xd3\x11\xb4\x01%\v&\xe7\f\xb7\x0e\xc8\v\xbfVޅ;\xfb\\λ\x1fi\xb94/Π\x19f\xc8L\x88\xe8s\xaf\x8c\xd2\x13\x85\xd3sd\xe6\x93Z\xceɌ\x19\xe7\xbdL\x02]·I\xf1\x1c\x17r_^\x90\xf1\x92\x98\xed\xf8\xbb/\xc6RrZ^\x94ɲ\x98\x10\x98\x98\xbf2\xccL\x99\ayF\xd6J\x12q\x963T\xce\xceK\ty \xb3\xebH\xceF\x89\xe4\x99\xcc\x02\x9e\xccA\x99\xcb.Y\x88J\x9df\x9e\xa4\xe7\x94̂\xe6|\x93\xe5L\x92\xd7\xcb\x17}\r\x1bxZ\xd4,f\x83,\xda\xc8\xf3\xf8-\xe6{\x9c\x93\xe5\xb1H\xb1\x17ft\xb4\x19\x1b\x13\xf3\x9e\x9b\xc71\xccӘ\x00\x9a\x92\xbd1\x91\x9d1\x01q6g#5'c\x02\xf6\x82ڝ咙\x97\xf1/HaQ\xbf\x15\u007f+\x8ez\xe9´\x19\x98\x8bK\x16\xfa\xaf\xa3\ued17\x8d\xd54o~\xc6,O\xe9\xf6盟e]8Y\x15\x1c\xce?\xc8<\xea4\xba=\x1e\xe1Y\x16\x05\x89\xd5\xdf4\u007f\xe6\xb492\xa4_\xbf\xb4\xec\xb9\x1e\x19\xd1\xc2\xc23\x16\x05\x88\x18s\x9d\xac<\xf3\x1fAgz\x85$\xf3\xe9\xc0\x85O>÷\xd2W\x9e\x83\xf9K\xaeX\xc4\xd3\xed\xb1$(ͷ\xa3g\xb8\x1f\xf3\x06\xa2\xb7e\xf9\xd9_k4G\xd0\a4\x9dŰ\xf0\x1d\x81?h\xb6.\xbaĭ ?\xfc\xa7\xf7#ù;p\xf0Ay\x15\x16\x05;\u0091\xe1Й/ڽ&\xf1F~\xc0D\xd7x\xe0C\xb7\xa3#\xef\x97l\xcf\xd4$\xfc\xb7u\x1d\xcew\x1e\x16\xd5\xf6\x9b8\x10/w!f@\xa6&է]@-&ѿ\x95+\xb1\xe4L$[QiI\xf2o\x91\x1c\u007fFR\xfc\x19N\xc5ynE2\x99R\x92\xdf\xdfĹxC\xf7\xe2-\x1c\x8c\x97\xb9\x18\v GI\xed)\xe9\xeaI\x97\xab\xc9\xf7\v)\x97\xa3\xcbW\x00\xf3i\xe8\t\xe9\xe7\t\x97\x03K\x98&\xa4\x99\x9f\x97^\x9e@\xc37r>\xde\xc8\xfdx\v\a\xe4m]\x90E'd\x91sf_\xbf8\xba\xacM\x8ef6\x18\x9f\xcaj\xb3L6\xf2\x17\x86s\x8e\xbe\xa8mj\xa4P\xaf\x81i\x1a\v)\xb7_\u007ff\xf0\xb3T\xb9\xdf\x0fb\xaa\x9e\x1e\xe7bJ\x9c\xff\xde\x1a\x15\x9d}\x16\a:\xbaT\xb0X\t\xc3ն6G\u007f1i\xd7\xf0Id\xfbaG\xd8\v\v[mʨ\xc1t\xd9\xde\xc8\\7\xa3\xe8\xc9\xe5\x1a\xe0'\xdd^z\xf5+*XYVő\xfc\x00\xb8\x1c\x0ey\x19\x03D\x99dž\xba>\xa1\xdc͂\xaf\xf70\xec\x1d\xb9\xbck\x8a\xddd\x85\xae\xf3\x16\xfa\xc4\xe6\tu\x84\xfb\xafl\x93p\x99\x90\xac+\x99\x12\xac\x8e\xc6\xe7\x1bWT\xf9\xf1\xf5/\xf3\xac\xd3F\xec\xf0\x17\xed\v6-Qb\xd8{P\xad+Ȋ\xe6r\xbd\xf9\xf6\"\xa6CC\xe9\xa8\x11\xb0.g&\x9c\x86\ue793\xb0\x8c\t\x91\x99\xf3\xe7\\\xb1\xb0\x98\xc7\xc7_\xfc\x02\x9c,q\xfd\xb1\xf6\x17\xa7\xabJ\x18\x8bD\xcdfa~І\xfe\xbb\xd7ϱ؆\x0ek\xfeq\x8c\xb7A\xce\xcb\xe1\xfbٳ\xb0?\f\xcaO5$Zbԯ\xf1Q=Ǭ\xb7I\xfe\x94G\x1d\xf2)8\xbd\n|\x1c\xb2\xe0\xefj^\xb7\xccϔԞ\xaaQ\xc6u\xb9\x96\xab\x94\xf9\xf2]\xa1&a\xc8\xee\xaa\r\xd7\xe8\t\xa5\xbd\xb8\xa6\xcd\xcb\n\x95\xf9d\x94A\x9d\xc8\xf9}\xba=\x1d\xc1\xd5\x00M\xde+T\xd6\x16\xcez\x16\xb6Mx\x89*\xd2\x0e\x9c\x1fɖ,A\xc3\x1c\xf0\x80\n\xb4\xe2\xfc\x16\xae~\xe3+V\x8e\xc7D\xa0\xf6\xa1\x84\x04\x9a\xba*\xb4ț\x13\xde\xe8\xacP\xe5\xf0\x91\xe5\x979\xa0ygg`rq\xb0\xad61\"\x9c\nL\xafXn \x17\x0eWQ\xa0I\xb2/\xcal\x99\x95CF\xb7\x1f\x9c#\xbf f+\x8f+\xcdM\x8dl\xf4\xaf\xd3N\x14\xa0\xear\xe3\x15\xbah:\xc4\xf6\xef\xa4ޜ\r\x19O3\xc7\xcb/L*\x87\xbb\x93\x98\xe2\xe9\xcan\x1b\xfe9{e\xedȩ\x95\xd9:\xcb\xd0\xdam]\x141Ӿ\xe5\xdc\xd7_&\xe7\xf2-\xd68\xe3N^\x04r\"`S\x88\xceg\x02\x96h\xad\xd85\xc5͞I\x03\xedP!\x1b>\xb1x\xa3w\f\xbḇai/\x1f\xc1\x12\x99\xabE\x98\xa0\xb9\xf9\xef\xf5z\x17\xb3\v\n\xbd\x83\xad,\xb8k\xa8_\x19T\xf3\x994\xf9VI\x93\xa2\xca?\xb5\x1d\x896\x1c|\xe6\x8d\xe8\xea\xbcb!w\x92\xf4 m\xd2N\x98\x8d\xd8\xe1*\xd3E\x81\x9cf~\x8a\xd7[\x1e\u0590\x9f\xf7\x05\x85]\\\xdaO\xfd\xbe!\xd2\xe1w\xdbW\xc6\x10\xbe@!\x97\xfdt\xd2`WG\xf7\x04!\xcd\x13\x9f\xa5\xba=\x15\xa2\x15gO1\xed\xf7m\x0eX\x90\xab\x1eNS\x80\xf6*\x18\x83qo\xb6\x14\xbfis\x05\xa5T\xf4\x0fY\xfc\x1c\x8ah\x06\x9f\x85?\u05ec[\xc0\xfb\x9e\xfa\xb4i\xd2=E\x8á\x982U㩱+\xf8\x8c\xa7\x96\x95\xcfvŜ\x83o\xb12\xbb\xd4\xe5N\xdd\x1b\xbd#\u007f8\xf2\xb2\x15^\x91w\xf7\xc28)\x8a\xe2\xe8'\x99\x9c=\xf2\xe2#\x92⚴^\xe2d\rX.Q6t\xeb\\o\xa9<'p\x9e\xeaF\xd7n J:Q\x14\x0f\xfb3\xb05|\xd6\x0e\x9b\x88\xae\x1c\xc2$\xe1\x8b֭p\xbb\xd5\xc6yO\u007f\xb5\x02\xb9\r\xd6P\x04.\x9d\t\xbe\x91\xf2UoA\xba\xeeR\xbe\xe3^vt\f\x1fB\xae\xf0T\x8a\xa3\xcfY\x14YF\xc66^['\x8a\x88|\xfb]9Plv\x12\xf7a\xfe\x97\x88\x1dvB\xf0\xbb~\xff\xf6\xc3\xf1V\xbb18O9\xce)\xf7\xb2=\xaa\xe9\x803\x8dQ\xc1\xb3\x91Α<\xed_ف#\tZ\x14`I\xa6L\x94\t\x9c\x93\xec\xfc\x9et\xef\xddt\bq\xe8ߴ\x9d\xa7TwX\x9c\xa6m\xd90\t&\x96\xe5\xbfY\x92\xb6\x19K[\x99\xed\x85\xda\x11S\x19]\xef\xf6\r_NhƩ\b\\MHAU\xd4;b\xf5p]\xe2j\xa3z!\x98p\x81\x92\xf7\xd0\x15\xd9\xd3$\xa6!$\xdcT^\xbf\x0e\x85\xffV[\xa3\xcbU\xd8\v\xbe\xe5\xb8\n\xa1\x11#5\xd9\xff\xe4\xc8O\x00\xed*l1\x1bT\x15*\x106\xe0\x93\xf0A\xd5\xfc\xb6\xce\xc5)\x9c0.իx\x18t^p(\x18r\x1c߇\x10\xf8\xf1\x1f\x96ݎk\xe0_\x81\x95\xaa)\xfa\xee\x03K\x9e\x15,\xf9\x19\x06\xd9W\x8f^`\x9dx\b\x03\u007f`\x88\xfe\xdf\xd6\x158\xb4\x1a\xe6S\x8aM\xf9u\xd4}\x94\x9dK\xa7\xbc\x83\x18\xec\xc0\b=\xfe$\xb7\xfeN-#\xac\xff\xfcwϺ=$\xd9,\xeff\xcd\x15\xb6DZ\xbb\x03>be0\x13Q\xc7\x03\xe0\xbe@\xb2#,\xe2\xd0\x12zw\x96\xc9{x\x99\x13\xf7\x9a\x1e\\\xf3\xf7\b^ǯ9\xbc\xccw{3\xc7\xeduW\xf7,\xb8\x06\xfa\xd2\x19\xfb\x8f\xd0-\xe2\xb9\x05\b\x11\xdf-\xb2\x8c֛[\xf4\xddz\xae[\x83\xe3D\xb5\xeb\x91;\xf7J\xce[T\x0f\x9c\xbd\x000\x85\xaa\xf1\x06>\xd1\x145+\xb0\xbc\x00\b\xdbqS\xae\u0082\x0f?z\b\xc5\x1e+\xe6\xd7\x02\xa0j\x94\xef\xef\xef\xbe\xfc\xff\x87\xc1c\x80\x12M\xa1ym\x1dR\xfc\u0080\x1b`\xf0\xc5m\vt@?\xd8=\xb3\xa0\xb1\xd6hPZ\x03v\x8fP\xb0\xda6\x1aAm\xe1\x97f\x83Z\xa2Eӂ\x06(Dc,j0\x96Y\x04f\x81A\xad\xb8\xb4\xc0%X^!\xfc\xe9\xfd\xfd\x1d\xa8\xcd\xefXX\x03L\x96\xc0\x8cQ\x05g\x16K8(\xd1T\xe8\xc7\xfey\xddB\xad\xb5\xaaQ[\x1e\xf1\xec[\x8f\xabzOG\xdb{G\x18\xf0\xbd\xa0$vB\xbf\x8d\x80E,\x03\xd2h?v\xcfM\xb7]\xc7!\x03\xc0@\x9d\x98\f\x8b_\xc3\x03j\x02\x03f\xaf\x1aQ\x12\x17\x1eP\x13\xc2\n\xb5\x93\xfc\xbfZ\xd8\x06\xacr\x93\nf10@\u05f8\xb4\xa8%\x13p`\xa2\xc1+\x87\x92\x8a\x1dA#\xcd\x02\x8d\xec\xc1s]\xcc\x1a\xfeUi\x04.\xb7\xea\x06\xf6\xd6\xd6\xe6\xe6\xfaz\xc7m\xdd'\xe9[\xb6\x11x\x03V7\xa7\xd3y4l\x94\x12\xc8\xc6Bx\x8c\x87\xcfh,/\x16\xb0p9F\x83\x1f\x95@\x82\x0e/\xdc\xde\x12x\xd8t\xbcf\xd9\x13\x02\x8b\xd8 \xe5 D\x0f\x89\x03\f\xc0\u007fH\xf8@\x92\xab yr\xbaZ\b\x92\x8b\xa3p\xd2R*\x10J\xeeP\xfb\xd9H+\x14\xceK\x80\r\xeeف+\x1d\xb6\xdeI#\xfc\x8aEc1uz\x99\x85\x92o\xb7\xa8\tN\xbdg\x06\x8d7\xfd\xa6\x112\xad\xca\xc0\t\xffIb\x9e\xec\xa3#$q\xaa\xdb\xf9\xd4\xd2I\x1c\x8d\xcfUl\xb4P\xd26\xee\xfc\x96\xfc\xc0ˆ\tw\x94\x99,\xfc~X\xbb\xae\x94H\x98!\xf2ɚ\xbd\xa0\x88+'J\fL\x03%\x11\x94\x86\x8a\xec\xa1Ӯc\v\xaekS\xdb\xde0\x92vʳ\xa8n\x04\x9a0\x95W/\x9d\f\xb8\x9a\x04\xddR\xc4\xdb҂mP\x80A\x81\x85U:\x8d\x8e%\"\xfb\x96#\xd7&\xb0\x98\x90pC\x1b\xa6\xdb\xd8\fHp\x06Ξ\x17{\xaf,\x88\x83\x1c\x1c(\x15\x1aw\xcaY]\x8b\xe3\xd4&a\x89\xf2a\x92\xb9\x83\u07b5\x85#?\x86\x97:\xfc]ː\x8d][\x90\x92C̶\xec\x00V\xcdn\xfb\xff&b\xa3\xd8\u007f\x01\xd3ޝ\f}]\xa6%\x94rrgﶀUm\x8fW\xc0m|\xba\x04\x91L\xadn\xfe\u007f`\u009c\xcf\xf1w㑯\xca\xf1\xb3TY\x82HTi\xa7\xff\a$\x8aS\x16\x0fAWd\x13\xe4\xd7\xfe\xa8+\xe0ۖ \xe5\x15\xf9M\x16\xf5\x882\xdft^^\x03\x199\xfa\x8eZ\xc5l\xb1\xff\xf8\x95,/\xd3\x05\\3\xf12\x1e\xec\xed\xd7h\xcf\x0f\x15\xf3\x02\\p\x01\x1a\xae\xb1\xf2\x81\x9fG\x87\xcd\ue273\xa8\xde\u007f\xfa\x80\xe5\x1cz \x8f\xf3N6\xf2~\xb4\xd8\xfe\xd4\xc1(\xcf\xddF0}Z\xffƇ\xf4\xae\x80\xc1\x13\x1e\xbd\xc5\xc2$\x10q\x18M4\xe1\xe9\x9c\"\xc7\xc5\x16\x1d\x93=\xe1с\t\xc1\xc2\xc5ѹ\xac\xe0\xdb\x13\x1es\xba\x8d\x10Hk\xe2&\x04A\t\x93\xf4\xc0!\xc2Ŗ\xf2\x91\a.\xf0\x1be\xd1\xf2\xe6 _\x90\xc4\x16q\xff\x82m\xb6d\xeb\x05\xcd\x1da\xdf\x19O\":\x05{^gn\x94\xd4\x1c\x18t\xa7%\x86~\xbf0\xc1\xcbv\"\xcf\xf7wr\xda\x1a\x1e\xb6O\xca\xde\xc9+\xf8\xf8\x95\x9b\x10}\xff\xa0\xd0|R\xd6=y\x13t\xfa\x85\xbf\x00\x99~\xa0;^ҋm\xc2C?\x86\x9c\xc1ܾ\xddy\x0f\xaf%\x0f7p'\xc9o\t\xf8p7\x02~\xbay\xfd0lUc\\\x90X*\xb9r\xaar\x9d\x9a\xc9#;\x13\xa4\xd2\x03\x8a\x9c.\xad\x9d\xd4O\x98\t\xf6\x914\x89\x1f\xef\xef8\x04+\xb0\x8c1N\x17\x99g\x16w\xbc\x80\n\xf5nNq\xf4[M\xf2=o\t\x99R\u05f739,O\xb5\xc7\x16Dw2\x047l+:\xb9\x19\xbd\"\xb1\x17\xbbN\x04䧻.\xefȩXg\u007f,b\x97\x95\xa5\xbbKe\xe2\xfe\f\x89\u007f\x06-Nu\xbf_\x98א\x15s!\xd2\xff&5\xe7\x18\xfa\u007f\xa0f\\g\x9c\xe1\xf7\xeejT\xe0`l\x88b\xf5\xa7\xa1\x19\xb8\x01\xa2\uf049ӫ\x9e\xc4\xe6\x14\xc9\x16\x14^\x91\xab\xed\x89\xc5r\x05\xcf{e\xbcNu\xa1\xd9E\x90\xdc\xc0\xe5\x13\x1e/\xafN\xe4\xc0坼\xf4\n\xfelq\xd3Z\vJ\x8a#\\\xba\xb1\x97\xdfb\x04erbV7w\x05\x9dk*\x93/\x19-\x01\x1a\xd8\u07bb\x92\x99;\xb7\xea,>\xac\x95I\xdc&M,\xe5^\x19\xeb#\x8b\x03\xb3\xf4\x9c(\x16x\x1e\n\xd1+`[\u007f\xf3\xadt\xbc\xd3$\xb17\n\xb8\x12\xd5̼\x84%2\xb6\x111\x0f\x94\x1c\xab\xcb\xee\x04{yz\xe9/:\xdd$\xacp\xc6\xc5\"\xdcZ\xab\x02\x8d\x99g\x91\fi\xbd\x10$l\x03\x84\xcc;0\xfe\xc2p>(\x19[\xbeAJH:Ӕ\xff\xf8\xb5\x17\xbd\xa4\xc3O\u007f/1߹\xeb\x02wf\xab\x8a\x8doƳ\x96x\xebG\xc6c\x12\x00y\xd7@\xef\x1aw\xd4\xf3-\xc8\xc0H\u007f\x045]qy\xe7&\x80\x1f_]\xad\xb7B\x12_b\xb8\xdfƱ\x1d\xd2\xdb\a\xee\xf4\xe6ZD\xcaE\xee5\x0e(w\x1a\xe7&C1\x13\xa4T\xb6\x1fN \xb8\xb5*\xdf\x19\xd8rml\u007f\xa1\xb9L\xd1,\x9c\xfe\xae\x9d\xeb9ɏZ\xbf\xc8q\xfa͏\xec\x05\xb2\xf6\xea9\xe6\x17L^Ŧ\x9a\xbb\x14B\xe0[\xe0\x16P\x16\xaa\x91.\xfcBG\xddM\xe1I\xe0\x05t6\xca\xf2\x04\x045\x94M\x95\x87\x80\x95\xe3:.g\xe34\xfd\xee?3.ނlv*\r#\xd5\x06d\x8b\xf9\x18\xfdD\x91\x8a}\xe5US\x01\xab\b\xf5\xb9n\xcf\xd6gq\f(\xde\xe6r8\xb8N\x8dXE\x87\xaa\x16hsO\xa4\xcfڠcbx\x89\xadb\x0e\\\xa0$0\xd82.&.\xcfO\xdbY\xb8=\xc7\xd7\b\xc2\xe2\xf5\x9c\x88\xbc\xc9W\x0e\x15\x19\x81\xd8Lcq^Z\xd7:\xdfT\xbcטg\x9e-\x05\xa5\xa3yVkN\xbc\xa4^\xdbB\v,\xc6\xe4\xf1\xbb\x89vҾ\x9bh\v\xed\xbb\x896پ\x9bh\xcb\xed\xbb\x89\x16\xdaw\x13-\xb6\xef&\xdaw\x13m\xaeۜ\xb4^Z\x91\xff\xe2d\xe2\xe5\xe2*2\xae\xa7\xe7\x968\x03?dS\xdc\xfa\xafOr3,\xefң\x12Y\xc1᳖\x95\xfb\"'\xc5\x01]\xd2E\xa7JڔK: \x91\xbd}\x02\xfdB\x12\xe67d\xdf\xe6$\xfc,\xa5\xf9\f\xf3L\xdb4\x9b\x98h\xaa\xe2$\t<\xc4/{\xc8\xec\xed\xe7\x90\f\xf3u\x9c\x9d\x1bW\xfaw\xcfA\xcdH\xc5YH\xc0\x99O̝\xc3\xd7\xc8\xf5\x18\"L\x0f\x12F\xff0\xf8ZȒ\x99\u038d\t7Ah\xd9\xe1\xc7\xf5\xf0\x8dU!S\x06\x9e\xb9\xdd'\xb6\xf2\xbcG\xe9\xee\xb0䮟\xf6\x1a\xf9-|b5\xc6#(\r\x92\v\x87\xce\x19n\x1d\xa0\x17~\xab\xbd\vw\xf6\xb9\x9cw?\xf2ri^\x9cA3̐\x99\x10\xd1\xe7^\x19\xe5'\n\xe7\xe7\xc8\xcc'\xb5\x9c\x93\x193\xce{\x99\x04\xba\x9c\x0f\x93\xe39.侼 \xe3%3\xdb\xf1\x9b/\xc6rrZ^\x94ɲ\x98\x10\x98\x99\xbf2\xccL\x99\ayF\xd6J\x16r\x963T\xce\xceK\ty \xb3\xfb\xc8\xceFI\xe4\x99\xcc\x02\x9e\xccA\x99\xcb.Y\x88J\x9df\x9e\xe4\xe7\x94̂v\xf9&˙$\xaf\x97/\xfa\x1a6\xf0\xb4\xa8Y\xcc\x06Y\xb4\x91\xe7\u05f7\x98\xefqN\x96\xc7\"\xc6^\x98\xd1\xd1flL\xcc{n\x1e\xc70Oc\x02hN\xf6\xc6Dv\xc6\x04\xc4ٜ\x8dܜ\x8c\t\xd8\vjw\x96Kf^\xa6?\x04\x86E\xfd&\xfeV\x1c\xf5ҍ)=0\x17\x97,\xf4\xdfF݉\x96\xd1j\x9a7?S\x96'\xb7\xfb\xf3\xcdϪ\x11\x96\xd7\u0085\xf3\x0f\xbcL:\x8dv\x8f\xc7\xf6\xb3\xceߕ\xfb\xccist\x90~\xfbܲ\xe7zdD3\x03\xcf(\x04\xb0\x14s\x9d\xec\xbc\xf0߲\x17j\x85$\xf3\xe9\xc0\x85\x0fV\xc3'\xefW\x9e\x83ݗ\\\xa9\x88\xa7\xddcEP◯g\xb8\x1f\xf3\x06\xa2\xb7eݳ\xbf6\xa8\x8f\xa0\x0e\xa8;\x8ba\xe1;\x02\u007f\xd0L#\xbaĭ ?|\x05\x85\x91\xe1\xdc\x1d8x/\xbd\nK\x82\x1d\xad\xd1\xc1\xa13/ZZ\x93x#?`\xa2k:\xf0\xa1\xdaщ\xf7K\xb6gn\x12\xfeۺ\x0e\xe7;\x0f\x8bj\xfbM\x1c\x88\x97\xbb\x103 s\x93\xea\xf3.\xa0\x16\x93\xe8\xdfʕXr&\xb2\xad\xa8\xbc$\xf9\xb7H\x8e?#)\xfe\f\xa7\xe2<\xb7\"\x1bM9\xc9\xefo\xe2\\\xbc\xa1{\xf1\x16\x0e\xc6\xcb\\\x8c\x05\x90\xa3\xa4\xf6\x9ct\xf5\xac\xcb\xd5\xec\xfb\x85\x9c\xcb\xd1\xe5+\x80\xf94\xf4\x8c\xf4\xf3\x8cˁ\xa5\x95f\xa4\x99\x9f\x97^\x9e\x81\xc37r>\xde\xc8\xfdx\v\a\xe4m]\x90E'd\x91sf_\xbf8\xba\xact\x89z6\x18\x9f\xcbj\xb3L6\xf2\x17\x86s\x8e\xbe\xa8\x8d\x15^\xa8\xd7\xc04M\x85\x94ۯ?\v\xf8\x85\xcb\xd2Ӄ\x98\xaa\xa7\xc7]M,\x97\xff\xde\x1a\x15\x9d}\x96\x06:\xbaT0X3튦m\x8e\xfebҬ\xe1#+\xf6Î\xb0g\x06\xb6JWI\x83鲽\x91\xb9\x8e\xa3\xe8\xc9\xe5\x1a\xe0g\xd5^z\xf5+*\x18^\xd5\xe2H~\x00\\\x0e\x87\xbc\x8c\x01\x92\xcccBy\xa6P\xaff\xc1\xd7{\x18\xf6N\\\xde\xc5R=\x85PM\xd9B\x9f \x1e\x93G\xb8\xff\xe2l\x12W&\xa4\xe8J\xa6\x04\xab#\xfa|\xe3\x8a*?\xbd\xfee\x9e\xb1J\xb3\x1d\xfe\xaa|ݭ%L\f{\x0f\x8a\xae\x05Y\x11/\xd7\xe3\xb7\x17)\x1d\x1a*\x80\x8d\x80u93'՟h\x95)!2s\xfe\xac\x15\v\x9by|\xfc\xd5o\xc0\xf2\n\xd7\x1f\x1a\u007fq\xba\xaa\x996H،\x1b\xf3\x836\xf4߽zN\xc56T\xd8\xf3O\xe3ukty9\xee~\xf6\xac\xd5\x1f\x06U\xc4\"\x8a\x96\x18\xf5KzT\xcf1\xeb\x11ɟ\xf2\xa4C>\x05\xa7WHх,\xdcw5\xaf[\xe6gJjO\x95\x9as\xe5Ֆ\x8b\xcd\xf9*l\xa1\xb4d\xc8\xeej\xb4\xab\xd1\x13*\xb4\xb9\x9a6/\xab7\xe7\x93Q\x06\xe5>\xe7\xe9t{:\xc2\x15u\xd4e\xaf\xde\\[\xf6뙙6\xe1%\xa9H;p~\xa4\xb3d\t\x1a\x96\x80\a\x94\xa0\xa4\xcboq\xd5o|\xe1\xd1\xf1\x98\x04\xd4>\x94\x90@\xd3\xd4B\xb12\x9e\xf0\xa8\xb3B\xb1\xcaG'\xbf\xf4\x01\xf5;3\x03\xb3-\xe4\x96@©\xc0\xf4\x8a\xe5\x06Jfq\x95\x04\x9a%\xfb\x92\xccV\x18>dt\xf3\xdeZ\xf2\vR\xb6\xf2\xb8`\xe0\xd4Ȩ\u007f\xad\xb2L\x80l\xaa\x8dW\xe8,vH\xd1\xef\xa4l\xa0\t\x19O3\xc7\xcbo\x8cK\x8b\xbb\x93\x98\xe2\xe9\xcen#\xff\x9c\xbd\xb3v\xe4\xd4\xceLS\x14h̶\x11\"eڷ\x9c\xfb\xfa\xdbt\xb9|\x8b5\xce\\'/\x02]\"`,\xa3\xe73\x01+4\x86\xedbq\xb3g\xd2@;\x94\xe8\f\x9fT\xbc\xd1;\x86]\xe6ذ\xb4\x97\x8f`\xb1\xc26,L\x10o\xfe{\xbdޥ\xec\x02\xa1v\xbe\x00\"\x8f\xe5_\xa3j>\x13'_k\xaesT\xf9Ƕ#\xe1\xc6\x05\x9f\x1d!\xbar\xbd(\xf8\x8e\x93\x1e$\"\xed\x98ް\x1d\xae\n%\x04\xba4\xf3\xd3u\xbd\xe5a\r\xf9y\x9f\x91\x99ŭ\xfd\xdc\xef\x1b\"\x1d\x9eھ2\x06\xf3\xe5\x15]\xf5V\xcb5v\xe5\x90O\x16\xa4\xdc\xc4g\xa9n\x8f\x85d\xe1\xe0ӕ\xf6\xfb\xc6\x03\x16䪇\x13\xeb\b_\x05c0\xed\xcdV\xecw\xa5\xaf\xa0\xe2\x92\xfe!\x8b߅\"\xe2\xe0\xb3\xd6\xefj\xd6-\xac\xfb\x9e\xfa\xb4i\xd2=E\x8a\xf1@L\x99\xaa\xe9\xd4\xd8\x15|\xc2S\xcb\xcag\xbbb\xe9\x82o\xa9j\xc9\xd4\xe5N\xdek\xb5#\u007f8\xf1\xb2\x15^\x89w\xf7L[΄8\xfaI&gO\xbc\xf8\x80\xa4\xb8&\xad\x974Z\xc3*\x970\x1b\xbau\xae7\x97\x9e\x13\\\x9e\xeaF5v J:Q\x94\x0e\xfb;`k\xf8\xa4,ƈ.\x1f\xc2$\xe1\x8bƮp\xbbU\xdazO\u007f\xb5\x02\xbe\r\xd6P\x02.\x9d\tw#\xe5\x8b\x17\x03\xb7ݥ|ǽ\xce\xd1\xd1\xee\x10\xba\nO\x15;\xfa\x9cEV\x14dl㵱L$\xe4\xdb7\xe5@9\xb3\x93\xb8\x0f˿$\xec\xb0\x13\x84\xdf\xf5\xfb\xb7\x1f\x8e\xb7\xdá\xf3\x98s9\xe5^\xb6'5\x1d\xb8Lc\x94𬹵$O\xfbWv`I\x82\n\x01\x86d\xcaD\x99\xc09\xc9\xeeޓ\uef5b\x0e!\x0e\xfd\x9b\xb6\xf3\x94\xea\x0e\x9bSD\x96\x8dC\xc1Ķ\xfc7K\xdcıD\xcab\xcf䎘J\xabf\xb7\x8f|9\xa1\x19\xa7\"p\r-\nj\xd1\xec\x88\xd5\xc3u\x89m\xb4\xec\x85`\xc2\x05J\xd9[.+\x9e&W\x1aB±\x80\xfeu(\xfc\xb7\xdajU\xad\x02-\xdc-\xc7U\b\x8dh\xae\xc8\xfe'G~\x02hWa˱A]\xa3\x04f\xc2z2>\xa8\x9a'\xeb\\\x9c\xc22ms\xbd\x8a\x87A\xe7\x05\x87\xc2AN\xaf\xf7!\x04~\xfc\x87e\xb7\xe3\x9f2\xb8\x02\xc3e\xac\xdd\xef\x03K\x9e\x15\f\xf9\x19\x1a\x9d\xaf\x9e\xbc\xc0:\xf1\x10\x06\xfe\xc0p\xf9\u007f[W\xe0\xd0j\x98\x8f96\xe5\x97Q\xf7Qv\xae+Q\xddv\tv`\x02\x1f\u007f\xe2[\u007f\xa7VЪ\xff\xfcwϺ=d\xd9,\xeff\xcd\x15g\x89\xb4v\xc7BA\xea{\x81dG\x18ġ%\xf4\xee,\x93\xf7\xf02'\xee5=\xb8\xf8\xb3\x12\xaf\xe3\xd7\x1c^滽\x99\xe3\xf6\xba\xbb{f\xae\x94\xfd\xd2\x19\xfb\xb7\xd0-\xe1\xb9\x05\b\t\xdf-\xb1\x8d֛[\xf4\xddz\xae[\\\xe3D\xb5\xeb\x91;\xf7J\xce[R\x0f\x9c\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb3\x05&4ڣ\xd1eq\x05\xcd\aߩ\xc2\xc4\xcf\xe2\xae\xea\xcfU\x99\xb4\xee\x97N\xf5'i\x1d\u007f*\xb2҈\xac5\x1e\xd7Z\xa9\x1e\xcbL\x98\xa6\xfe\f\xc0&\xba\xc0+\xf8LC\x15\"\xc1\xf4\f\xa0\x9a\x18\x0f\xbd\x02\x91\xa6L*\x91}5R94\xd7:+\xf3@\xa2\x15\xa4h\x13#\vǤ\xb8s\u0095\x16\xf4\x16\xdc\x0e\xdb\xe3P\xf9\xcdj\xf5U\xb8\xdd\x15\xac-\xb7[\x17;a\xc3WO\"\x0f\xa0\xaar\a\xc2\xcd:#\xd5\xe3\xd0h\x1f\xe0\xdah\x05\xf8\xbd0h\teH\x99\xb3\xea\x11\x9ew\xa8\xc0i0\xa5bT\xfe!\x92\xa7\xb2\x18@\xa4\xc0d\xddó¤[9\x87\xcb\xfd\x0e!\x13ց\x939\x82\xa8\x06\x84ga\x19\x87\xad6\xe0v\xd2\xceӄ\x80t\xb0\xf5\xe8|\xeaW{\x84R\xe1\xb0B\xa7\x05*H\xf5\xfaH\";0?\x9a\x1a\xf6\x00?|\xfe\x88\xe9\x1c\xc9 ^R\x8f&\xf5\xa1\xe7\xe9\xb4Q\xe0\tF\x81lM\x8aݴ:\xc6\xf3\x1b\xa3\x17 \xe0\t\x0f\u07b3\x1a\f.\x87\n\xb1V\xd4 \r\xf2.-\xab\x91'<0\xa8j\xdb5\n\xde\x12Q\xf1\xe5\t\x0f\xb1M{D%\xfc\xaa\r\x1fO]\xaa\xe0Y\xc4,\xa5\xa6\xd4D\xad\xd6\x0e8\x1d7YX\xa6\x94B\t\x14?q\xda5\xc3:g\rOxxg=\xfbh\xd5\xecd\xb1\x80\x02\xa4\xb0\xc1\"\xaf\xb0\xb0\xc9\xfe 2\x99փ\xf1:Y\x00\xf1V]\xc0g\xed蟛\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xb8\xe6UI\xec'q\"\x81}g^\x96ʛ\x05\xa2ˢ\xf1\x1b\x1c\u0604\x92\x88\xd6l\x93\x16n\x15\xc5g\x9e>KشÀ\x9cG+/-o\xd3+\xadVl\xa6\xc3h\v\x80\xb6\xf1\xaaX\xa5M\x87S\x17\v!\x0e\xa2X\xa1wO\xd6\xca\u007f9:\xe0\x98*\x06\x8bL$\x98\x86\xedJ>M\x11\x0e\x1fe\x029\x9aG\x84\x82\xecF\xbcP-\xd0侜 \x85\xf1\xaeE(\x95Y\x188\x1c\x18*+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1c\x9dL7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xeb_fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03g:d\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\x91\xe5\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca9\x05KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x93wr\xe1\xe7f\x11-Ӆ\xb6#\xc7N#h}\xd5\xd6\xf9\r\xc0\x8e\xbb=\xb0C\x18\x13\xfdU\xbb\x86 \xb6\x0e\rX\xa7M8\xe5&\xb5\xdb\xdb '\xce\xdby\xde\x13\xab\xeb\xddH\x0f\x98\x82\xcc\xf3FCx\x9d~\ue3ff\xe9\xff\xf30\x13v\x96\x18vat\x82\xd6\u038bR\xa4\xe5\x98ٰ\xad7k\x85\x0f\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6{kߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\u0380\xcbs\xd1ϸ\x88F\xf7\xda\xf7\x0e\v\xb0\x02\xe6\x03&\xf3X\xb2RY\xe67W\"\xf9Ws/mi6Z\x9d?4oU~ \am\xd9\x05\x81\xd9|\xb3\x18\xa4!&\xcbl8\u007fl\x06\xea\x92ܲ\xd8\x18<\"\x8f,>{,\x8e<\xc0װcsƢ\xbd\xb6\xd8\xfc\xb0\xd7\xc9\n\x8b\xcc\x05kex͂<1\x03,\x9a`q\xd9^\xd19^\xaḓyjMdv\r\xe7k͂\x1c\xca\xe7\x8a\xc9Ҋ\xc25:7\xabθ\x9a\xdfI\xfc\xa1\x8c\xac\x97\xcf\xfd~I?\u007f:\xbf**\xab**\x16\x98\xc79*oji\xb6T\x14U\x97fF\xd5YO\x13\x03G\xe5C\x1d\xe7:MMe6\vj<\xc3i\n\xecP\xeeSD^\xd3\x04\xc8v\xc6\xd3b7`V\x9af\x1a\f?\x95\x10ʼ\xad\xcd\xfe/$\xf0G'\xadM\xc7\x05\x8e\x89J\xbe\xf4\xba\x10\xef\x83\xd77\xe4V\x8f\xc7x\xde\xd9>\xc1\xad\x1e\x01y\xbb\x85\xbc̜,\xb2\xd6[\x05n\x87\ax\x96YF\xea\xfc7\xcdW/7\a\x86\xf6\xe5[-\xc0c \xbb\x01\x82\xb0\xf0\x8cYF\xff\x1eQ!\xf1/\x83$z\x85dsƷ\xc1\xab\v\xf2ճ\"\x17>\u05cf沈=\xcb\tR\xb8W\u007fB\xf85\xed\xecz\x1f\x9d\xeb~/\xd1\x1c@\xef\xd1\xd4^ͨ\x985\xf7\x95\xaa\xa5iˬQ%\x95N\xf2\xef\xd3tU\xcb\xf8j\xa8\x174|P\xde\xcc\xf6qeX\xa4C\x9a\xe0hJuR,4\x06B\xe9\x1a\xc2H\xff\x18_z\xc9\x05\x9e\xd7\b\x95^\"X\x8ar+^#`z\xad\x90iiд\xe4\xe82\xea\x02\xcek\x84NK\x82\xa7E\x1e`\xfc\x05\x9b\u05faX\xf3\nA\xd4\xc9a\xd4\"\xd2\xc5^\x9cY\x1cLE\xcco\xe6\xa2̑\xc7\x15\x01r\xf4\x82\xccp@\x15\x01\xf1\xe8b\xcclH\x15\xb3\x0e\xfaA\xd7\x0f_s\x89>\xc6_t\x96\x14{\x04\x1fw\xcc3\u007f}%\xf2\xdaJ\xe4!P\f\xf6\x91\xd7S\x96_K\x89\xa4\xf3\x89\xc1\xd6\xe4Б\xd7O\x16\x85['\x06\\\x93\x10\xa7\xae\x9bL\x87\\\xd3\xdbi\xfdk&'\xb8\x13\x11\x126\xdb\xe4\x87O\x04\xb4I\xd1\xcc\x1e\xae,\x11\xcdY\xa1\xec\xc5D\xdd\xf1{\xaf\x0e\x847\xb1\xa8U\xfb\xe0f\x8c;\xba\xbe\x05\x9f\xc0/R\xa5\x9e7$\x84-\xff\x82_O\xe4{2\xb5\xe33.F\x8d\xb7\xd9;4\xb2X\bR\xa3\x1c\x14\xf1\xe1\xb6]ÍHvu\xc3\x11\x88<\xf2NX\xd8j\x93\v\a\xe7\xf5i\xdce\xe8I5\xe7k\x80\x9fu}\x10\xdaz\xe5f\x04\xae\x95y\x91\x1d(\xfa\x81\xf3.\xa0\x1f\x13\x9dQ\xf1\xb3\xd5KpՃh\x11\x11\xf0]\xb7\xc7\xd0K\x84\xd5shI\xa6˴\x1ea\x82\xddB\x1d\xe0\xeb\x03{S\xfc\bT\xd2<\x96U\xf9J!\x12\uef655\x02r\xecA\xc1E$\x1b?\x1c\xb6N\x1b\U000487f4\u007f\xf31\x86f\xdd\x1e\x9dg?+]\x15RE\xaa\xbb_\xa3\x92\xec\xe7\xd6\a\xd8d\x90U\xab\xad9K'lǔ\xd8\xcc:w.\x8b\x98\xdc\xfd\xfd'?!'s\\\u007f,\xfdA\xfd\xaa\x10\xc6\"Q:L\xd4wڌ۹\x9d~\x86LWt\xf8G\u007f\x1e\x069c\x8ds\x02N\x9a;\xf3\xf4a ]\x8c\xc8?\f\xf7l\x05\xb2-&N\x1d\xed\xeb\xed(,a\xadN$\xeb\"\xde\x12\xe2D\xb1\xd7{*nʢL\xa8\x8c\xd2\xe2\x97g\x85\xe6[X\xa8\xf6VyNͼ\xc4\xf9\xebQ\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@ֿR\x19\xf6\xb6\xa4\xad\xdf&=&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf89\xd0U\xfdB\xe9Y\x04e\xfd+\x9c1\x8f\xcf\xfa\xe7:\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xda\xf3\xb3\xcds\xde3\xbcl\x1e\xf8n\xa2\xfd\xd9\xe7\xc4\a\xf8W? ;\xfa\xa2\xaa\xb7\xae\xfe\xb9\xef\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c{l\x97\xda\xd4I\xbe\x15\xa1\xb9cx\x9f\xf0n\f\xf5\xe1\xac\xcd\x15|\xc6\xe7\x81\xda\x1bE\x938>S\U000e9658\xf2\x1e\xc1\xd0\xd3ۓS\xdc\u05fd8/v@[t\xd5\\\xafy/\xe1FdY\v\xa2ρ\x1db\xeb?˭\xdf\xc0IhN\xffr\xd4bTqM*\xad1\x855\xb8\xa4\x8e*-\x9a=\xa6-!\xa9lx\xbb\xa6\xdc4\xcfE\xc2\x1f\u007f\x9e5\xabR$\t\x16\xaeJ\xecj\xff\x99\x83\xf3s\xfe\x11\xfe\x8a\x01\xffL\xb4\U0008edbd\x82\xff\xfa\xef3\xa8\f\xf0C\xf8S\x05T\xf9\xbf\x01\x00\x00\xff\xff\x16'-~\x14b\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=]s\x1c7l\xef\xfa\x15\x18\xf5\xc1\xed\x8c\xee\x14O\x1f\xdaћ++SM\\\xdbc)zi\xfb\xc0\xdb\xc5\xe9\x18\xed\x92\x1b\x92{\xf25\x93\xff\xde\x01\xc8\xfd\xbc\xfd\xe0\x9e\xa5i\x9a\x11_\x12\xed\x91 \b\x80\xf8 A\xf8l\xb5Z\x9d\x89B>\xa0\xb1R\xab+\x10\x85\xc4\xef\x0e\x15\xfde\xd7O\xffj\xd7R_\xeeߟ=I\x95^\xc1ui\x9dο\xa1եI\xf0#n\xa5\x92Nju\x96\xa3\x13\xa9p\xe2\xea\f@(\xa5\x9d\xa0ϖ\xfe\x04H\xb4rFg\x19\x9a\xd5#\xaa\xf5S\xb9\xc1M)\xb3\x14\r\x03\xaf\xa6\xde\xff\xb4\xfe\x97\xf5Og\x00\x89A\x1e~/s\xb4N\xe4\xc5\x15\xa82\xcb\xce\x00\x94\xc8\xf1\nl\xb2ô\xccЮ\xf7\x98\xa1\xd1k\xa9\xcfl\x81\t\xcd\xf6htY\\A\xf3\x83\x1f\x140\xf1\xab\xb8\v\xe3\xf9S&\xad\xfb\xa5\xf3\xf9\x93\xb4\x8e\u007f*\xb2҈\xac5\x1f\u007f\xb5R=\x96\x990\xcd\xf73\x00\x9b\xe8\x02\xaf\xe03MU\x88\x04\xd33\x80\xb00\x9ez\x05\"M\x99T\"\xfbj\xa4rh\xaeuV\xe6\x15\x89V\x90\xa2M\x8c,\x1c\x93\xe2\xce\tWZ\xd0[p;l\xcfC\xed7\xab\xd5W\xe1vW\xb0\xb6\xdco]섭~\xf5$\xf2\x00\xc2'w ܬ3R=\x0e\xcd\xf6\x01\xae\x8dV\x80\xdf\v\x83\x96P\x86\x949\xab\x1e\xe1y\x87\n\x9c\x06S*F\xe5\xdfD\xf2T\x16\x03\x88\x14\x98\xac{x\x06L\xba\x1f\xe7p\xb9\xdf!d\xc2:p2G\x10aBx\x16\x96q\xd8j\x03n'\xedh\x1ao`%2؋\xac\xc4\v\x10*\x85\\\x1c\xc0 \xcd\x02\xa5j\xc1\xe3.v\r\xff\xa1\r\x82T[}\x05;\xe7\n{uy\xf9(]\xa5b\x13\x9d祒\xeep\xc9\xdaRnJ\xa7\x8d\xbdLq\x8f٥\x95\x8f+a\x92\x9dt\x98\xb8\xd2\xe0\xa5(\xe4\x8aQW\xacf\xd7y\xfa\x0f\x15G\xed\xbb\x0e\xaeG\xfb\xcd7V\x84\x13\x1c \x8d\xe8\x05\xc6\x0f\xf5\xabh\bM\x9f\x88:\xdfn\xee\xee\xdb\xc2$m\x9f\xfaL\xf7\x96\x845, \x82I\xb5Ű\xa3\xb7F\xe7\f\x13UZh\xa9\x1c\xff\x91d\x12U\x9f\xfc\xb6\xdc\xe4\xd2\x11\xdf\u007f/\xd1:\xe2\xd5\x1a\xae\xd9\xee\x90\x1c\x96\x05\xed\xc0t\r\xb7\n\xaeE\x8eٵ\xb0\xf8\xea\f J\xdb\x15\x116\x8e\x05m\x93\xd9\xef\xec\xa9\xd6\xfa\xa12o#\xfc\xaa\xf6\xf8]\x81Ig\xcb\xd08\xb9\x95\to\f֞\xb5\n\xe8iP߆w-\xff\xc2j\xaa\xff\xb5\x87\x87\xd7eլh\xc9~\xb8\x1ds\xb81c$W\x1e\x1a\xe9\x14\xa5\xfb\xdc\x1d҂-J\x04(3\x98t\xb5^\xac};\x82\tAխGp<\xe2*\xff\x84yAjc\x06\xc5\xfbЍP$\xfa\xa4\xb5;U\x19\xfeJ\xcd\xea\xa0]\xe1H\xb9\xf1t;$\xbe\xede\x1a\xb4\xd7\x11Wa\x92\xb3\xd4\x12+\xef\x94(\xecN;\xb2q\xbatC\xbdz\v\xb8\xbe\xbb\xed\rjq\x9e\xb0b\x1bΌv\x1a\x9e\x85<\xe6\xb4o$\x97\xd7w\xb7\xf0@.\x11V0\xc1[rp\xa5Q\xac\x8e\xbf\xa1H\x0f\xf7\xfaW\x8b\x90\x96\xac\x95*\xbb|1\x02x\x83[\xda\xf4\x06\t\x06\r@ch\x0fXFM\x97n\xcd\x0eG\x8a[Qf.(9i\xe1\xfdO\x90KU:<\xe6;L\xf3\xde\x13\x89\xc1\xf9\xd5\xd8{\xfd\xb3\xf5\x8c\x8c \xe9Ǒ\xa1\x03[\xaa\xd0)\xec\xb9\xdf\x18Ue\x86`\x0f\xd6a\x0e\x9b\x00\xa5\xb6\xd5\xcc\x15\xd6\aY\x16\xc0X\xd8\x1c*܇\xd7M^\xb8\xd8dx\x05Δ\xc3\xd3Nm\xdd!\xda|C\xebd\x12A\x99\xf3>i\xfc\xc8\x01\xc2\x18\xfea\x84(=\n\x90\x91\x17O\xe4h\x06\n\x91\xb7\x90e-\xe2\xceS\x05\xe0\xbf\x14|$\x03\x97\x90ٹ\n\xe6Lb\xc6&TiȴzD\xe3g$W\xe1Yf\x19oi\xcc\xf5\xbe\xe3d\xb5\x1b\xd9\x16\x83\x19\x19Iؖdv\xd6@\xb2?*#RY\x87\"]\x9f\xbf\x16\xf3\xf0{\x92\x95)\xa6u\x983\xa8Kz\x8c\xbb9\x1a\xc4\x01\xa1\x90\x8a43\x85_DtU\xff:B\r\xf65\x85A2\x18 \x95\x87I\xa4!E\xb3\x19Q\xd2Ԥ\xc3|\x04\xcfٝ\xbc\x80j\xc2\x18q\x98\xa0Y\x154/!Y=&\xb8b\x99L\x90\x88U;\\L5&\xcd\xc8\xfa\xfe\x1f\x12l\xa7\xf5S\f\x91\xfe\x9d\xfa5\x8e%$|6\x01\x1b܉\xbd\xd4\xc6\xf6\xa3\x13\xfc\x8eI\xe9Fw\x9bp\x90\xca\xed\x16\r\xc1›\x8e\xbf\xa7\x885mV\xa9\x99i\xc6\x1f\xad\xaba:1\x8f\xa91\xb6\x14v_F\xa1\x02#NV\x8fuC*\xf72-E\xc6jB\xa8įO\xd4\xf8\x8d)\xb7\x19\x818\xc2\xdf+\xa3j\x15ĥ\x8eW\xaa\x15\x92ۗk3f\xb7|;\x063N\x86\x8d`gr̅k\x9a)3\xb4\x01\x15o\xfe\x1a\xbds\xd1p\xca\at\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02\xdfb\xf5\xe7\be\a4i\xd7ߚU\xa2M#\x87l'\x93\x9d7V$e\f\vR\x8d\x965\x86(\x8a\xec0\xb5h\x88\x91\x8c0ٜ\xd2hZ\x84\xfa\xe8\xc3\x1dS$M\x8b\xd4\xc1M\x9b\xd1\xc6]\xaa\xd7b\xf3F\xf4\x0e\x9aꇄ\xfd\xf6h\xf8\xcb\v;\x91[\xa2]\xc3\xed\x160/\xdc\xe1\x02\xa4\xab\xbe\xc6@%W\xb1\xc1\xe3oƸ\xd3v\xcbm\u007f\xf4\x8b\xef\x96\x17\xe1Z\x8d\xc6߄il\xac\ue0adZİO\xed\x91\x17 \xb75\xc3\xd2\v\x8a!\x1d\xb2/5\x87h\xcbљ\xe5\xdcK\x12(\xd6\xf6R˅Kv7\xf51PĈ\x1e\xad\xfa\x00\xbc_^\xc50̃\b\x90P;\x15|\x82)\r\xe6\xfed\xf4\x9e\xf7G\xf3\x85=\xc0\x0f\x9f?b:G2\x88\x97ԣE}\xe8y:m\x14x\x81Q [\x8bb7\xad\x8e\xf1\xfc\xf9\xf7\x05\bx\u0083\xf7\xac\x06\x83ˡF\xac\x155H\x83|\x18\xcfj\xe4\t\x0f\f*\x9c\xaeG\xc1[\"*\xbe=\xe1!\xb6k\x8f\xa8\x84_8\xd7\xf3ԥ\x0f\xbc\x8a\x98\xadԴ\x9a\xa8a\xef\x80\xd3q\x8b\x85eJ\xa9j\x15\xc5O\\vͰΕ\xd2\x13\x1e\xdeY\xcf>\xda5;Y,\xa0\x00)l\xb0\xc8;\xac\xbaKy\x10\x99L\xeb\xc9x\x9f,\x80x\xab.\xe0\xb3v\xf4\x9f\x9b\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xf8˫\x92\xd8/\xe2D\x02\xfb\xc1\xbc-\x957\vD\x97E\xf378\xb0\t%\x11\xad\xd9&-\xdc*\x8a\xcf<}\x96\xb0i\x87\x15r\x1e\xad\xbc\xb4|\x1b\xa3\xb4Z\xb1\x99\xaef[\x00\xb4\x8dW`\x956\x1dN],\x848\x88b@\uf7ac\x95\xff\xe5\xe8\x1ek\xaa\x19,2\x91`Z\x9dJ\xf3\xa5\x99p\xf8(\x13\xc8\xd1<\"\x14d7\xe2\x85j\x81&\xf7\xed\x04)\x8cw-\xaa\x16\xcc\xc2\xc0\x1d\xd0P[Ѯ\x8f\xecY\xb19\xaa\xfb\xc8\r\xd9t\xf7\xb8U\xb2yg\u007f(\x8a\xfa픎e\x96e!\xbf\x8e}\x10\x8f\xa4w?r\xc1\xc7\xd6\u007f\x90ye\xf1\xfe3\xce\x1a\ni\xec\x1a>pBK\x86\xed\xf1\xd5)ak\xaa(\x90\x84\x89\xb4@r\xb2\x17\x19\xb9\x0f\xa4\xbc\x15`\xe6\x9d\t\xbd=\xf2\xa0\xe2T\xcc\xf3N[o\xf3\xebc\xf5\xf3'<\x9c_\x1ci\xaf\xf3[u\x1e\a\x93t\xfe\x91Ҫ\xbd\x16\xad\xb2\x03\x9c\xf3o\xe7\xec\x98-\xd9\"'8o\v\xa4:\xba+\xa7\x8e,\t\x05(֮\xbc\x16\x1a\\'X\x90\v?\xb7\x8ah\x99.\xb4\x1d\xb9]\x1cA뫶\xce\x1f\x00v\xdc\xed\x81\x13\u0098\xe8/\x9c\x1a\x82\xd8:4`\x9d6U2\x03\xa9\xdd\xde\x019q\xde\xce\xf3\x9eX]\x9fFz\xc0\x14d\x9e7\x1a\xc2\xeb\xf4s\x9f\xe5@\xff?\x0f3ag\x89a\x17F'h\xed\xbc(EZ\x8e\x99\x03\xdb\xfa\xb0V\xf8\xe0m\x1b\xa5\x9ac\x8e\x92\xab\xb6\xcc\x15'Ҟ\x10\xd8\xdc|o\x9d;\x93\x1a\xa2\xbfcD\xf9\x14\x1c\x81\x13\x1d\xf3\\\xf4\x13k\xa2ѽ\xf6\xa3\xab\r\x18\x80\xf9\x80\xc9<\x96\xacT\x96\xf9\xcdA$\xffj\x8eG.\xd5-O\x04\xef_\xcdY\x81J\x95㩡\xccu5\xbeaH\xfd!6~\x85*=C\xf3]\x8d\xc1\x0eg\x8fo2\xe29\x05\xe4L+\xedڇ5a\xa6w\x16\xb6\xd2X\xd7 \xbc\x00\xaa\xb4|M\xfd\xba1\xa6\xba1\xe6\xe4\x10\xf3\x8b\x1f\xdd:V\xdc\xe9\xe7\x90Դ$\xb0\xae\x88\xbf\x13{\x04\xb9\x05\xe9\x00U\xa2K\xc5\a^\xa4.h\x9a\x05\x10=\x13\xbd1\x89\xb4\x99\xad\xc1\xaa\xcc\xe3\t\xb2b\xe9\x94j\xf6t\xac=\xe4g!\xe3N\xa7\xe04\xb6\xba\xa9ġ\xa1\xd6͆\n\x19D\xed\xec\xb5\\|\x97y\x99\x83ȉ-K\xe2ƭ\xcf=\xaaR\xdd<\xaf\x9f\x85t!\x83\xd8_\xac.Ӧ\x89\u038b\f\x1dVYE\x89VV\xa6X\xbb\x0f\x81\xff\x839ZcM\xc0VȬ4\vt\xf4b\xce,\x8dۂzz\xf9`,\x1e\x91\x15\x133\xf2\xd0}\x81\xd3\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-{ 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xb5}l\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3'\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe2\xa8|\xa8\xe3\\\xa7\xa9\xa5\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x87\xe1\x8a\x18U\x9b\xb7\xb5\xd9\xff\x85\x04\xfe袵\xe9\xb8\xc01Qɗ\xde\x10\xe2}\xe5\xf5\r\xb9\xd5\xe31\x9ew\xb6Op\xabG@\xden!/3'\x8b\xacU\x92\xc2\xed\xf0P?y\xffM\xf3\xd3\xcb́\xa1}\xf9V\v\xf0\x18\xc8n\x80 ,\xe8\a]?\xfc\xcc%\xfa\x1a\u007f\xd1]R\xec\x15|\xdc5\xcf\xfc\xf3\x95\xc8g+\x91\x97@1\xd8G>OY\xfe,%\x92\xce'\x06[\x93SG>?Y\x14n\x9d\x18pMB\x9czn2\x1drM\x1f\xa7\xf5\x9f\x99\x9c\xe0NDH\xd8l\x97\x1f\xbe\x11\xd0&E3{\xb9\xb2D4g\x85\xb2\x17\x13u\xe7\xefU\x1d\xa8\xaa{Q\xaf\xf6\xc5\xcd\x18wt\xfd\n>\x81_\xa4J=oH\b[\xfe\x05\x17\xc9\xe4w2\xb5\xe33.F\x8d\xb7ٻ4\xb2X\bR\xa3\x1c\x14\xf1\xe5\xb6]ÍHvu\xc7\x11\x88<\xf3NX\xd8j\x93\v\a\xe7\xf5m\xdce5\x92\xbe\x9c\xaf\x01~\xd6\xf5Eh\xab\xca\xcd\b\\+\xf3\";P\xf4\x03\xe7]@?&:\xa3\xe2gC\xc1\xbfP\xd1,\"\x02\xbe\xeb\x8e\x18*8\x19\n\xbb%\x99.\xd3z\x86\tv\vu\x80\xaf\x0f\xecMq\x11\xa8\xa4)\x96\x15|\xa5*\x12\xee\xd5\xd2\x1a\x019V7r\x11\xc9\xc6/\x87\xad\xd3F<\xe2'\xedK{\xc6Ь;\xa2S\xdd5\xe8\xaa*U$\xbc\xfd\x1a\x95d\xbf\xb6>\xc0&\x83\xec\xa8\xda a;\xa6\xc4f\xf6\xb9sY\xc4\xe2\xee\xef?\xf9\x059\x99\xe3\xfac\xe9/\xeaW\x850\x16\x89\xd2\xd5B\xfd\xa0\u0378\x9d\xdb\xe9g.\xd7\u05ee\xbf٪p\x8c\x9c\xb1\xc69\x01'\xadfߩpY\x91.F\xe4\x1f\x86G\xb6\x02\xd9\x16\x13\xa7\xae\xf6\xf5v\x14\x96\xb0V'\x92u\x11\x1f\tq\xa2\xd8땊\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9VmT{\xab\xc6\nlvH\xf8\xeb\xd1\xc0\xd1\xe2\x9aN\xb3\xfe\xebu\x1f\xb2{*\x10\xc8\xfab\xa4\xd5ٖ\xb4u\t\xdac\xd2\xcd\xec\xff\xf1\xbd?컮\x86\xab\xbe\xae\xeaB\xb4g\x11\x94\xf5\xc5Vcj\f\xfb\xaa\xac\x89(\\i\x82yMJ\xc3u\xf3\b\b\xfa\xb2r\xa7U\x19n\xaa\xb6\xcf\xf0\xb2\xa9\xe3\xdeD\xfb\xb3U\xe3\a\xf8W\xd7\t\x1e-\x9c뭫\xaf\xea\xbe\"\xf8\xa7\xb1sp\x1fp\x9d\xc1\xb9\x9a\xcaԧN\xf2\r\x84\xe6\x81U}»1ԇ\xb36W\xf0\x19\x9f\a\xbe\xde(Z\xc4\xf1\x9d\x9aO\xcdĔ\xcf\b\x86*\xacO.q_\x8f\xe2\xbc\xd8\x01m\xd1Us\xbd\uef44\x1b\xaeV[w\xf19\xb0Cl\xfdG\xb9\xf5\a8\t\xad韎z\x8c*\xaeI\xa55\xa6\xb0\x06\xb7\xd4\xd1G\x8bf\xcf\xe5a+!\t6\xbc\xfd\xa5\xdc4\xe5\"\xe1\x8f?Ϛ])\x92\x04\v\x17\x12\xbb\xda\xff\x9aŹ/\xf7Z\xfdc\x15\xfcg\xa2\x95w\xb4\xed\x15\xfc\xe7\u007f\x9fA0\xc0\x0fտHA\x1f\xff7\x00\x00\xff\xffX\x13X\x17\xfbc\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\u007f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\u007f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\u007f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\u007f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), } diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 232955a35d..f3b2725425 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -100,10 +100,18 @@ type BackupSpec struct { // DefaultVolumesToRestic specifies whether restic should be used to take a // backup of all pod volumes by default. + // + // Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead. // +optional - // + nullable + // +nullable DefaultVolumesToRestic *bool `json:"defaultVolumesToRestic,omitempty"` + // DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used + // for all volumes by default. + // +optional + // +nullable + DefaultVolumesToFsBackup *bool `json:"defaultVolumesToFsBackup,omitempty"` + // OrderedResources specifies the backup order of resources of specific Kind. // The map key is the Kind name and value is a list of resource names separated by commas. // Each resource name has format "namespace/resourcename". For cluster resources, simply use "resourcename". diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index adfb9c2526..8c488dd4f6 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -337,6 +337,11 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = new(bool) **out = **in } + if in.DefaultVolumesToFsBackup != nil { + in, out := &in.DefaultVolumesToFsBackup, &out.DefaultVolumesToFsBackup + *out = new(bool) + **out = **in + } if in.OrderedResources != nil { in, out := &in.OrderedResources, &out.OrderedResources *out = make(map[string]string, len(*in)) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 2843cb54ba..0ef13e27d3 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -71,15 +71,15 @@ type Backupper interface { // kubernetesBackupper implements Backupper. type kubernetesBackupper struct { - backupClient velerov1client.BackupsGetter - dynamicFactory client.DynamicFactory - discoveryHelper discovery.Helper - podCommandExecutor podexec.PodCommandExecutor - resticBackupperFactory podvolume.BackupperFactory - resticTimeout time.Duration - defaultVolumesToRestic bool - clientPageSize int - uploaderType string + backupClient velerov1client.BackupsGetter + dynamicFactory client.DynamicFactory + discoveryHelper discovery.Helper + podCommandExecutor podexec.PodCommandExecutor + resticBackupperFactory podvolume.BackupperFactory + resticTimeout time.Duration + defaultVolumesToFsBackup bool + clientPageSize int + uploaderType string } func (i *itemKey) String() string { @@ -104,20 +104,20 @@ func NewKubernetesBackupper( podCommandExecutor podexec.PodCommandExecutor, resticBackupperFactory podvolume.BackupperFactory, resticTimeout time.Duration, - defaultVolumesToRestic bool, + defaultVolumesToFsBackup bool, clientPageSize int, uploaderType string, ) (Backupper, error) { return &kubernetesBackupper{ - backupClient: backupClient, - discoveryHelper: discoveryHelper, - dynamicFactory: dynamicFactory, - podCommandExecutor: podCommandExecutor, - resticBackupperFactory: resticBackupperFactory, - resticTimeout: resticTimeout, - defaultVolumesToRestic: defaultVolumesToRestic, - clientPageSize: clientPageSize, - uploaderType: uploaderType, + backupClient: backupClient, + discoveryHelper: discoveryHelper, + dynamicFactory: dynamicFactory, + podCommandExecutor: podCommandExecutor, + resticBackupperFactory: resticBackupperFactory, + resticTimeout: resticTimeout, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, + clientPageSize: clientPageSize, + uploaderType: uploaderType, }, nil } @@ -205,7 +205,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, backupRequest.ResourceIncludesExcludes = collections.GetResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources) log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) - log.Infof("Backing up all pod volumes using Restic: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToRestic)) + log.Infof("Backing up all volumes using pod volume backup: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToFsBackup)) var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index e218d2636a..6c09af7f10 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2806,7 +2806,7 @@ func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLoca } func defaultBackup() *builder.BackupBuilder { - return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToRestic(false) + return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToFsBackup(false) } func toUnstructuredOrFail(t *testing.T, obj interface{}) map[string]interface{} { diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index df5d48cd26..b669dd79f6 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -146,10 +146,10 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // nil it on error since it's not valid pod = nil } else { - // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list + // Get the list of volumes to back up using pod volume backup from the pod's annotations. Remove from this list // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. - for _, volume := range podvolume.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { + for _, volume := range podvolume.GetVolumesByPod(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToFsBackup)) { if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index 73a994b465..f18ce49092 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -174,6 +174,12 @@ func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { return b } +// DefaultVolumesToFsBackup sets the Backup's "DefaultVolumesToFsBackup" flag. +func (b *BackupBuilder) DefaultVolumesToFsBackup(val bool) *BackupBuilder { + b.object.Spec.DefaultVolumesToFsBackup = &val + return b +} + // DefaultVolumesToRestic sets the Backup's "DefaultVolumesToRestic" flag. func (b *BackupBuilder) DefaultVolumesToRestic(val bool) *BackupBuilder { b.object.Spec.DefaultVolumesToRestic = &val diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 0289524f51..52aeab3d52 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -82,23 +82,23 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command { } type CreateOptions struct { - Name string - TTL time.Duration - SnapshotVolumes flag.OptionalBool - DefaultVolumesToRestic flag.OptionalBool - IncludeNamespaces flag.StringArray - ExcludeNamespaces flag.StringArray - IncludeResources flag.StringArray - ExcludeResources flag.StringArray - Labels flag.Map - Selector flag.LabelSelector - IncludeClusterResources flag.OptionalBool - Wait bool - StorageLocation string - SnapshotLocations []string - FromSchedule string - OrderedResources string - CSISnapshotTimeout time.Duration + Name string + TTL time.Duration + SnapshotVolumes flag.OptionalBool + DefaultVolumesToFsBackup flag.OptionalBool + IncludeNamespaces flag.StringArray + ExcludeNamespaces flag.StringArray + IncludeResources flag.StringArray + ExcludeResources flag.StringArray + Labels flag.Map + Selector flag.LabelSelector + IncludeClusterResources flag.OptionalBool + Wait bool + StorageLocation string + SnapshotLocations []string + FromSchedule string + OrderedResources string + CSISnapshotTimeout time.Duration client veleroclient.Interface } @@ -132,7 +132,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the backup") f.NoOptDefVal = "true" - f = flags.VarPF(&o.DefaultVolumesToRestic, "default-volumes-to-restic", "", "Use restic by default to backup all pod volumes") + f = flags.VarPF(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", "", "Use pod volume file system backup by default for volumes") f.NoOptDefVal = "true" } @@ -350,8 +350,8 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro if o.IncludeClusterResources.Value != nil { backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value) } - if o.DefaultVolumesToRestic.Value != nil { - backupBuilder.DefaultVolumesToRestic(*o.DefaultVolumesToRestic.Value) + if o.DefaultVolumesToFsBackup.Value != nil { + backupBuilder.DefaultVolumesToFsBackup(*o.DefaultVolumesToFsBackup.Value) } } diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index a908ff23f6..fe4bebea30 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -43,40 +43,40 @@ import ( // InstallOptions collects all the options for installing Velero into a Kubernetes cluster. type InstallOptions struct { - Namespace string - Image string - BucketName string - Prefix string - ProviderName string - PodAnnotations flag.Map - PodLabels flag.Map - ServiceAccountAnnotations flag.Map - VeleroPodCPURequest string - VeleroPodMemRequest string - VeleroPodCPULimit string - VeleroPodMemLimit string - ResticPodCPURequest string - ResticPodMemRequest string - ResticPodCPULimit string - ResticPodMemLimit string - RestoreOnly bool - SecretFile string - NoSecret bool - DryRun bool - BackupStorageConfig flag.Map - VolumeSnapshotConfig flag.Map - UseRestic bool - Wait bool - UseVolumeSnapshots bool - DefaultResticMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - Plugins flag.StringArray - NoDefaultBackupLocation bool - CRDsOnly bool - CACertFile string - Features string - DefaultVolumesToRestic bool - UploaderType string + Namespace string + Image string + BucketName string + Prefix string + ProviderName string + PodAnnotations flag.Map + PodLabels flag.Map + ServiceAccountAnnotations flag.Map + VeleroPodCPURequest string + VeleroPodMemRequest string + VeleroPodCPULimit string + VeleroPodMemLimit string + ResticPodCPURequest string + ResticPodMemRequest string + ResticPodCPULimit string + ResticPodMemLimit string + RestoreOnly bool + SecretFile string + NoSecret bool + DryRun bool + BackupStorageConfig flag.Map + VolumeSnapshotConfig flag.Map + UseRestic bool + Wait bool + UseVolumeSnapshots bool + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + Plugins flag.StringArray + NoDefaultBackupLocation bool + CRDsOnly bool + CACertFile string + Features string + DefaultVolumesToFsBackup bool + UploaderType string } // BindFlags adds command line values to the options struct. @@ -106,13 +106,13 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Generate resources, but don't send them to the cluster. Use with -o. Optional.") flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "Create restic daemonset. Optional.") flags.BoolVar(&o.Wait, "wait", o.Wait, "Wait for Velero deployment to be ready. Optional.") - flags.DurationVar(&o.DefaultResticMaintenanceFrequency, "default-restic-prune-frequency", o.DefaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default. Optional.") + flags.DurationVar(&o.DefaultRepoMaintenanceFrequency, "default-repo-maintain-frequency", o.DefaultRepoMaintenanceFrequency, "How often 'maintain' is run for backup repositories by default. Optional.") flags.DurationVar(&o.GarbageCollectionFrequency, "garbage-collection-frequency", o.GarbageCollectionFrequency, "How often the garbage collection runs for expired backups.(default 1h)") flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment") flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "Only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.") flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "File containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") flags.StringVar(&o.Features, "features", o.Features, "Comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled") - flags.BoolVar(&o.DefaultVolumesToRestic, "default-volumes-to-restic", o.DefaultVolumesToRestic, "Bool flag to configure Velero server to use restic by default to backup all pod volumes on all backups. Optional.") + flags.BoolVar(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", o.DefaultVolumesToFsBackup, "Bool flag to configure Velero server to use pod volume file system backup by default for all volumes on all backups. Optional.") flags.StringVar(&o.UploaderType, "uploader-type", o.UploaderType, fmt.Sprintf("The type of uploader to transfer the data of pod volumes, the supported values are '%s', '%s'", uploader.ResticType, uploader.KopiaType)) } @@ -135,11 +135,11 @@ func NewInstallOptions() *InstallOptions { ResticPodCPULimit: install.DefaultResticPodCPULimit, ResticPodMemLimit: install.DefaultResticPodMemLimit, // Default to creating a VSL unless we're told otherwise - UseVolumeSnapshots: true, - NoDefaultBackupLocation: false, - CRDsOnly: false, - DefaultVolumesToRestic: false, - UploaderType: uploader.ResticType, + UseVolumeSnapshots: true, + NoDefaultBackupLocation: false, + CRDsOnly: false, + DefaultVolumesToFsBackup: false, + UploaderType: uploader.ResticType, } } @@ -177,30 +177,30 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { } return &install.VeleroOptions{ - Namespace: o.Namespace, - Image: o.Image, - ProviderName: o.ProviderName, - Bucket: o.BucketName, - Prefix: o.Prefix, - PodAnnotations: o.PodAnnotations.Data(), - PodLabels: o.PodLabels.Data(), - ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), - VeleroPodResources: veleroPodResources, - ResticPodResources: resticPodResources, - SecretData: secretData, - RestoreOnly: o.RestoreOnly, - UseRestic: o.UseRestic, - UseVolumeSnapshots: o.UseVolumeSnapshots, - BSLConfig: o.BackupStorageConfig.Data(), - VSLConfig: o.VolumeSnapshotConfig.Data(), - DefaultResticMaintenanceFrequency: o.DefaultResticMaintenanceFrequency, - GarbageCollectionFrequency: o.GarbageCollectionFrequency, - Plugins: o.Plugins, - NoDefaultBackupLocation: o.NoDefaultBackupLocation, - CACertData: caCertData, - Features: strings.Split(o.Features, ","), - DefaultVolumesToRestic: o.DefaultVolumesToRestic, - UploaderType: o.UploaderType, + Namespace: o.Namespace, + Image: o.Image, + ProviderName: o.ProviderName, + Bucket: o.BucketName, + Prefix: o.Prefix, + PodAnnotations: o.PodAnnotations.Data(), + PodLabels: o.PodLabels.Data(), + ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), + VeleroPodResources: veleroPodResources, + ResticPodResources: resticPodResources, + SecretData: secretData, + RestoreOnly: o.RestoreOnly, + UseRestic: o.UseRestic, + UseVolumeSnapshots: o.UseVolumeSnapshots, + BSLConfig: o.BackupStorageConfig.Data(), + VSLConfig: o.VolumeSnapshotConfig.Data(), + DefaultRepoMaintenanceFrequency: o.DefaultRepoMaintenanceFrequency, + GarbageCollectionFrequency: o.GarbageCollectionFrequency, + Plugins: o.Plugins, + NoDefaultBackupLocation: o.NoDefaultBackupLocation, + CACertData: caCertData, + Features: strings.Split(o.Features, ","), + DefaultVolumesToFsBackup: o.DefaultVolumesToFsBackup, + UploaderType: o.UploaderType, }, nil } @@ -393,8 +393,8 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact } } - if o.DefaultVolumesToRestic && !o.UseRestic { - return errors.New("--use-restic is required when using --default-volumes-to-restic") + if o.DefaultVolumesToFsBackup && !o.UseRestic { + return errors.New("--use-restic is required when using --default-volumes-to-fs-backup") } switch { @@ -404,8 +404,8 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact return errors.New("Cannot use both --secret-file and --no-secret") } - if o.DefaultResticMaintenanceFrequency < 0 { - return errors.New("--default-restic-prune-frequency must be non-negative") + if o.DefaultRepoMaintenanceFrequency < 0 { + return errors.New("--default-repo-maintain-frequency must be non-negative") } if o.GarbageCollectionFrequency < 0 { diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index 00d84721c4..abd819d3bf 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -135,19 +135,19 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { }, Spec: api.ScheduleSpec{ Template: api.BackupSpec{ - IncludedNamespaces: o.BackupOptions.IncludeNamespaces, - ExcludedNamespaces: o.BackupOptions.ExcludeNamespaces, - IncludedResources: o.BackupOptions.IncludeResources, - ExcludedResources: o.BackupOptions.ExcludeResources, - IncludeClusterResources: o.BackupOptions.IncludeClusterResources.Value, - LabelSelector: o.BackupOptions.Selector.LabelSelector, - SnapshotVolumes: o.BackupOptions.SnapshotVolumes.Value, - TTL: metav1.Duration{Duration: o.BackupOptions.TTL}, - StorageLocation: o.BackupOptions.StorageLocation, - VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, - DefaultVolumesToRestic: o.BackupOptions.DefaultVolumesToRestic.Value, - OrderedResources: orders, - CSISnapshotTimeout: metav1.Duration{Duration: o.BackupOptions.CSISnapshotTimeout}, + IncludedNamespaces: o.BackupOptions.IncludeNamespaces, + ExcludedNamespaces: o.BackupOptions.ExcludeNamespaces, + IncludedResources: o.BackupOptions.IncludeResources, + ExcludedResources: o.BackupOptions.ExcludeResources, + IncludeClusterResources: o.BackupOptions.IncludeClusterResources.Value, + LabelSelector: o.BackupOptions.Selector.LabelSelector, + SnapshotVolumes: o.BackupOptions.SnapshotVolumes.Value, + TTL: metav1.Duration{Duration: o.BackupOptions.TTL}, + StorageLocation: o.BackupOptions.StorageLocation, + VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, + DefaultVolumesToFsBackup: o.BackupOptions.DefaultVolumesToFsBackup.Value, + OrderedResources: orders, + CSISnapshotTimeout: metav1.Duration{Duration: o.BackupOptions.CSISnapshotTimeout}, }, Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3d09f72132..c4cb302519 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -71,7 +71,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/restore" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -133,7 +132,7 @@ type serverConfig struct { formatFlag *logging.FormatFlag repoMaintenanceFrequency time.Duration garbageCollectionFrequency time.Duration - defaultVolumesToRestic bool + defaultVolumesToFsBackup bool uploaderType string } @@ -163,7 +162,7 @@ func NewCommand(f client.Factory) *cobra.Command { profilerAddress: defaultProfilerAddress, resourceTerminatingTimeout: defaultResourceTerminatingTimeout, formatFlag: logging.NewFormatFlag(), - defaultVolumesToRestic: restic.DefaultVolumesToRestic, + defaultVolumesToFsBackup: podvolume.DefaultVolumesToFsBackup, uploaderType: uploader.ResticType, } ) @@ -214,7 +213,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().StringVar(&config.pluginDir, "plugin-dir", config.pluginDir, "Directory containing Velero plugins") command.Flags().StringVar(&config.metricsAddress, "metrics-address", config.metricsAddress, "The address to expose prometheus metrics") command.Flags().DurationVar(&config.backupSyncPeriod, "backup-sync-period", config.backupSyncPeriod, "How often to ensure all Velero backups in object storage exist as Backup API objects in the cluster. This is the default sync period if none is explicitly specified for a backup storage location.") - command.Flags().DurationVar(&config.podVolumeOperationTimeout, "restic-timeout", config.podVolumeOperationTimeout, "How long backups/restores of pod volumes should be allowed to run before timing out.") + command.Flags().DurationVar(&config.podVolumeOperationTimeout, "fs-backup-timeout", config.podVolumeOperationTimeout, "How long pod volume file system backups/restores should be allowed to run before timing out.") command.Flags().BoolVar(&config.restoreOnly, "restore-only", config.restoreOnly, "Run in a mode where only restores are allowed; backups, schedules, and garbage-collection are all disabled. DEPRECATED: this flag will be removed in v2.0. Use read-only backup storage locations instead.") command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("List of controllers to disable on startup. Valid values are %s", strings.Join(controller.DisableableControllers, ","))) command.Flags().StringSliceVar(&config.restoreResourcePriorities, "restore-resource-priorities", config.restoreResourcePriorities, "Desired order of resource restores; any resource not in the list will be restored alphabetically after the prioritized resources.") @@ -227,9 +226,9 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().StringVar(&config.profilerAddress, "profiler-address", config.profilerAddress, "The address to expose the pprof profiler.") command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "How long to wait on persistent volumes and namespaces to terminate during a restore before timing out.") command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "How long to wait by default before backups can be garbage collected.") - command.Flags().DurationVar(&config.repoMaintenanceFrequency, "default-restic-prune-frequency", config.repoMaintenanceFrequency, "How often 'prune' is run for backup repositories by default.") + command.Flags().DurationVar(&config.repoMaintenanceFrequency, "default-repo-maintain-frequency", config.repoMaintenanceFrequency, "How often 'maintain' is run for backup repositories by default.") command.Flags().DurationVar(&config.garbageCollectionFrequency, "garbage-collection-frequency", config.garbageCollectionFrequency, "How often garbage collection is run for expired backups.") - command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "Backup all volumes with restic by default.") + command.Flags().BoolVar(&config.defaultVolumesToFsBackup, "default-volumes-to-fs-backup", config.defaultVolumesToFsBackup, "Backup all volumes with pod volume file system backup by default.") command.Flags().StringVar(&config.uploaderType, "uploader-type", config.uploaderType, "Type of uploader to handle the transfer of data of pod volumes") return command @@ -622,7 +621,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), s.config.podVolumeOperationTimeout, - s.config.defaultVolumesToRestic, + s.config.defaultVolumesToFsBackup, s.config.clientPageSize, s.config.uploaderType, ) @@ -639,7 +638,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.mgr.GetClient(), s.config.defaultBackupLocation, - s.config.defaultVolumesToRestic, + s.config.defaultVolumesToFsBackup, s.config.defaultBackupTTL, s.config.defaultCSISnapshotTimeout, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 25879d060e..c7e0ca944c 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -86,7 +86,7 @@ type backupController struct { newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupTracker BackupTracker defaultBackupLocation string - defaultVolumesToRestic bool + defaultVolumesToFsBackup bool defaultBackupTTL time.Duration defaultCSISnapshotTimeout time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister @@ -112,7 +112,7 @@ func NewBackupController( backupTracker BackupTracker, kbClient kbclient.Client, defaultBackupLocation string, - defaultVolumesToRestic bool, + defaultVolumesToFsBackup bool, defaultBackupTTL time.Duration, defaultCSISnapshotTimeout time.Duration, volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, @@ -138,7 +138,7 @@ func NewBackupController( backupTracker: backupTracker, kbClient: kbClient, defaultBackupLocation: defaultBackupLocation, - defaultVolumesToRestic: defaultVolumesToRestic, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, defaultBackupTTL: defaultBackupTTL, defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, snapshotLocationLister: volumeSnapshotLocationLister, @@ -263,7 +263,7 @@ func (c *backupController) processBackup(key string) error { } log.Debug("Preparing backup request") - request := c.prepareBackupRequest(original) + request := c.prepareBackupRequest(original, log) if len(request.Status.ValidationErrors) > 0 { request.Status.Phase = velerov1api.BackupPhaseFailedValidation } else { @@ -349,7 +349,7 @@ func patchBackup(original, updated *velerov1api.Backup, client velerov1client.Ba return res, nil } -func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkgbackup.Request { +func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup, logger logrus.FieldLogger) *pkgbackup.Request { request := &pkgbackup.Request{ Backup: backup.DeepCopy(), // don't modify items in the cache } @@ -373,8 +373,15 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // calculate expiration request.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(request.Spec.TTL.Duration)} - if request.Spec.DefaultVolumesToRestic == nil { - request.Spec.DefaultVolumesToRestic = &c.defaultVolumesToRestic + // TODO: post v1.10. Remove this code block after DefaultVolumesToRestic is removed from CRD + // For now, for CRs created by old versions, we need to respect the DefaultVolumesToRestic value if it is set true + if boolptr.IsSetToTrue(request.Spec.DefaultVolumesToRestic) { + logger.Warn("DefaultVolumesToRestic field will be deprecated, use DefaultVolumesToFsBackup instead. Automatically remap it to DefaultVolumesToFsBackup") + request.Spec.DefaultVolumesToFsBackup = request.Spec.DefaultVolumesToRestic + } + + if request.Spec.DefaultVolumesToFsBackup == nil { + request.Spec.DefaultVolumesToFsBackup = &c.defaultVolumesToFsBackup } // find which storage location to use diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 20e340df06..84aafd7647 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -280,7 +280,7 @@ func TestBackupLocationLabel(t *testing.T) { formatFlag: formatFlag, } - res := c.prepareBackupRequest(test.backup) + res := c.prepareBackupRequest(test.backup, logger) assert.NotNil(t, res) assert.Equal(t, test.expectedBackupLocation, res.Labels[velerov1api.StorageLocationLabel]) }) @@ -342,7 +342,7 @@ func TestDefaultBackupTTL(t *testing.T) { formatFlag: formatFlag, } - res := c.prepareBackupRequest(test.backup) + res := c.prepareBackupRequest(test.backup, logger) assert.NotNil(t, res) assert.Equal(t, test.expectedTTL, res.Spec.TTL) assert.Equal(t, test.expectedExpiration, *res.Status.Expiration) @@ -350,6 +350,121 @@ func TestDefaultBackupTTL(t *testing.T) { } } +func TestDefaultVolumesToResticDeprecation(t *testing.T) { + tests := []struct { + name string + backup *velerov1api.Backup + globalVal bool + expectGlobal bool + expectRemap bool + expectVal bool + }{ + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().Result(), + globalVal: true, + expectGlobal: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToFsBackup(false).Result(), + globalVal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().DefaultVolumesToRestic(false).Result(), + globalVal: false, + expectGlobal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToRestic(false).DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToRestic(false).DefaultVolumesToFsBackup(false).Result(), + globalVal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().DefaultVolumesToRestic(true).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToRestic(true).DefaultVolumesToFsBackup(false).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToRestic(true).DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + formatFlag := logging.FormatText + + var ( + clientset = fake.NewSimpleClientset(test.backup) + sharedInformers = informers.NewSharedInformerFactory(clientset, 0) + logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) + fakeClient = velerotest.NewFakeControllerRuntimeClient(t) + ) + + apiServer := velerotest.NewAPIServer(t) + discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) + require.NoError(t, err) + + c := &backupController{ + genericController: newGenericController("backup-test", logger), + discoveryHelper: discoveryHelper, + client: clientset.VeleroV1(), + lister: sharedInformers.Velero().V1().Backups().Lister(), + kbClient: fakeClient, + snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), + clock: &clock.RealClock{}, + formatFlag: formatFlag, + defaultVolumesToFsBackup: test.globalVal, + } + + res := c.prepareBackupRequest(test.backup, logger) + assert.NotNil(t, res) + assert.NotNil(t, res.Spec.DefaultVolumesToFsBackup) + if test.expectRemap { + assert.Equal(t, res.Spec.DefaultVolumesToRestic, res.Spec.DefaultVolumesToFsBackup) + } else if test.expectGlobal { + assert.False(t, res.Spec.DefaultVolumesToRestic == res.Spec.DefaultVolumesToFsBackup) + assert.Equal(t, &c.defaultVolumesToFsBackup, res.Spec.DefaultVolumesToFsBackup) + } else { + assert.False(t, res.Spec.DefaultVolumesToRestic == res.Spec.DefaultVolumesToFsBackup) + assert.False(t, &c.defaultVolumesToFsBackup == res.Spec.DefaultVolumesToFsBackup) + } + + assert.Equal(t, test.expectVal, *res.Spec.DefaultVolumesToFsBackup) + }) + } +} + func TestProcessBackupCompletions(t *testing.T) { defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Default(true).Bucket("store-1").Result() @@ -359,20 +474,20 @@ func TestProcessBackupCompletions(t *testing.T) { timestamp := metav1.NewTime(now) tests := []struct { - name string - backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation - defaultVolumesToRestic bool - expectedResult *velerov1api.Backup - backupExists bool - existenceCheckError error + name string + backup *velerov1api.Backup + backupLocation *velerov1api.BackupStorageLocation + defaultVolumesToFsBackup bool + expectedResult *velerov1api.Backup + backupExists bool + existenceCheckError error }{ // Completed { - name: "backup with no backup location gets the default", - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup with no backup location gets the default", + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -391,8 +506,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -405,10 +520,10 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup with a specific backup location keeps it", - backup: defaultBackup().StorageLocation("alt-loc").Result(), - backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(), - defaultVolumesToRestic: false, + name: "backup with a specific backup location keeps it", + backup: defaultBackup().StorageLocation("alt-loc").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(), + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -427,8 +542,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "alt-loc", - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: "alt-loc", + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -447,7 +562,7 @@ func TestProcessBackupCompletions(t *testing.T) { Bucket("store-1"). AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). Result(), - defaultVolumesToRestic: true, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -466,8 +581,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "read-write", - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: "read-write", + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -480,10 +595,10 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup with a TTL has expiration set", - backup: defaultBackup().TTL(10 * time.Minute).Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: false, + name: "backup with a TTL has expiration set", + backup: defaultBackup().TTL(10 * time.Minute).Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -502,9 +617,9 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - TTL: metav1.Duration{Duration: 10 * time.Minute}, - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + TTL: metav1.Duration{Duration: 10 * time.Minute}, + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -517,11 +632,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup without an existing backup will succeed", - backupExists: false, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup without an existing backup will succeed", + backupExists: false, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -540,8 +655,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -554,12 +669,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a false value for 'DefaultVolumesToRestic' keeps it", + name: "backup specifying a false value for 'DefaultVolumesToFsBackup' keeps it", backupExists: false, - backup: defaultBackup().DefaultVolumesToRestic(false).Result(), + backup: defaultBackup().DefaultVolumesToFsBackup(false).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: true, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -578,8 +693,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -592,12 +707,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a true value for 'DefaultVolumesToRestic' keeps it", + name: "backup specifying a true value for 'DefaultVolumesToFsBackup' keeps it", backupExists: false, - backup: defaultBackup().DefaultVolumesToRestic(true).Result(), + backup: defaultBackup().DefaultVolumesToFsBackup(true).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: false, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -616,8 +731,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -630,12 +745,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default true value", + name: "backup specifying no value for 'DefaultVolumesToFsBackup' gets the default true value", backupExists: false, backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: true, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -654,8 +769,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -668,12 +783,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default false value", + name: "backup specifying no value for 'DefaultVolumesToFsBackup' gets the default false value", backupExists: false, backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: false, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -692,8 +807,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -708,11 +823,11 @@ func TestProcessBackupCompletions(t *testing.T) { // Failed { - name: "backup with existing backup will fail", - backupExists: true, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup with existing backup will fail", + backupExists: true, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -731,8 +846,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -746,11 +861,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "error when checking if backup exists will cause backup to fail", - backup: defaultBackup().Result(), - existenceCheckError: errors.New("Backup already exists in object storage"), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "error when checking if backup exists will cause backup to fail", + backup: defaultBackup().Result(), + existenceCheckError: errors.New("Backup already exists in object storage"), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -769,8 +884,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -823,21 +938,21 @@ func TestProcessBackupCompletions(t *testing.T) { require.NoError(t, err) c := &backupController{ - genericController: newGenericController("backup-test", logger), - discoveryHelper: discoveryHelper, - client: clientset.VeleroV1(), - lister: sharedInformers.Velero().V1().Backups().Lister(), - kbClient: fakeClient, - snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), - defaultBackupLocation: defaultBackupLocation.Name, - defaultVolumesToRestic: test.defaultVolumesToRestic, - backupTracker: NewBackupTracker(), - metrics: metrics.NewServerMetrics(), - clock: clock.NewFakeClock(now), - newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - backupStoreGetter: NewFakeSingleObjectBackupStoreGetter(backupStore), - backupper: backupper, - formatFlag: formatFlag, + genericController: newGenericController("backup-test", logger), + discoveryHelper: discoveryHelper, + client: clientset.VeleroV1(), + lister: sharedInformers.Velero().V1().Backups().Lister(), + kbClient: fakeClient, + snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), + defaultBackupLocation: defaultBackupLocation.Name, + defaultVolumesToFsBackup: test.defaultVolumesToFsBackup, + backupTracker: NewBackupTracker(), + metrics: metrics.NewServerMetrics(), + clock: clock.NewFakeClock(now), + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeSingleObjectBackupStoreGetter(backupStore), + backupper: backupper, + formatFlag: formatFlag, } pluginManager.On("GetBackupItemActions").Return(nil, nil) diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index cf34f175f1..d14e09e94e 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -32,19 +32,19 @@ import ( type podTemplateOption func(*podTemplateConfig) type podTemplateConfig struct { - image string - envVars []corev1.EnvVar - restoreOnly bool - annotations map[string]string - labels map[string]string - resources corev1.ResourceRequirements - withSecret bool - defaultResticMaintenanceFrequency time.Duration - garbageCollectionFrequency time.Duration - plugins []string - features []string - defaultVolumesToRestic bool - uploaderType string + image string + envVars []corev1.EnvVar + restoreOnly bool + annotations map[string]string + labels map[string]string + resources corev1.ResourceRequirements + withSecret bool + defaultRepoMaintenanceFrequency time.Duration + garbageCollectionFrequency time.Duration + plugins []string + features []string + defaultVolumesToFsBackup bool + uploaderType string } func WithImage(image string) podTemplateOption { @@ -99,9 +99,9 @@ func WithResources(resources corev1.ResourceRequirements) podTemplateOption { } } -func WithDefaultResticMaintenanceFrequency(val time.Duration) podTemplateOption { +func WithDefaultRepoMaintenanceFrequency(val time.Duration) podTemplateOption { return func(c *podTemplateConfig) { - c.defaultResticMaintenanceFrequency = val + c.defaultRepoMaintenanceFrequency = val } } @@ -129,9 +129,9 @@ func WithUploaderType(t string) podTemplateOption { } } -func WithDefaultVolumesToRestic() podTemplateOption { +func WithDefaultVolumesToFsBackup() podTemplateOption { return func(c *podTemplateConfig) { - c.defaultVolumesToRestic = true + c.defaultVolumesToFsBackup = true } } @@ -157,8 +157,8 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment args = append(args, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) } - if c.defaultVolumesToRestic { - args = append(args, "--default-volumes-to-restic=true") + if c.defaultVolumesToFsBackup { + args = append(args, "--default-volumes-to-fs-backup=true") } if len(c.uploaderType) > 0 { @@ -288,8 +288,8 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--restore-only") } - if c.defaultResticMaintenanceFrequency > 0 { - deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-restic-prune-frequency=%v", c.defaultResticMaintenanceFrequency)) + if c.defaultRepoMaintenanceFrequency > 0 { + deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-repo-maintain-frequency=%v", c.defaultRepoMaintenanceFrequency)) } if c.garbageCollectionFrequency > 0 { diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index ef5f871a06..604af44dfd 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -46,9 +46,9 @@ func TestDeployment(t *testing.T) { assert.Equal(t, 7, len(deploy.Spec.Template.Spec.Containers[0].Env)) assert.Equal(t, 3, len(deploy.Spec.Template.Spec.Volumes)) - deploy = Deployment("velero", WithDefaultResticMaintenanceFrequency(24*time.Hour)) + deploy = Deployment("velero", WithDefaultRepoMaintenanceFrequency(24*time.Hour)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) - assert.Equal(t, "--default-restic-prune-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) + assert.Equal(t, "--default-repo-maintain-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) deploy = Deployment("velero", WithGarbageCollectionFrequency(24*time.Hour)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 7053b87c59..aa9b5f237e 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -209,30 +209,30 @@ func appendUnstructured(list *unstructured.UnstructuredList, obj runtime.Object) } type VeleroOptions struct { - Namespace string - Image string - ProviderName string - Bucket string - Prefix string - PodAnnotations map[string]string - PodLabels map[string]string - ServiceAccountAnnotations map[string]string - VeleroPodResources corev1.ResourceRequirements - ResticPodResources corev1.ResourceRequirements - SecretData []byte - RestoreOnly bool - UseRestic bool - UseVolumeSnapshots bool - BSLConfig map[string]string - VSLConfig map[string]string - DefaultResticMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - Plugins []string - NoDefaultBackupLocation bool - CACertData []byte - Features []string - DefaultVolumesToRestic bool - UploaderType string + Namespace string + Image string + ProviderName string + Bucket string + Prefix string + PodAnnotations map[string]string + PodLabels map[string]string + ServiceAccountAnnotations map[string]string + VeleroPodResources corev1.ResourceRequirements + ResticPodResources corev1.ResourceRequirements + SecretData []byte + RestoreOnly bool + UseRestic bool + UseVolumeSnapshots bool + BSLConfig map[string]string + VSLConfig map[string]string + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + Plugins []string + NoDefaultBackupLocation bool + CACertData []byte + Features []string + DefaultVolumesToFsBackup bool + UploaderType string } func AllCRDs() *unstructured.UnstructuredList { @@ -272,7 +272,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { appendUnstructured(resources, bsl) } - // A snapshot location may not be desirable for users relying on restic + // A snapshot location may not be desirable for users relying on pod volume backup/restore if o.UseVolumeSnapshots { vsl := VolumeSnapshotLocation(o.Namespace, o.ProviderName, o.VSLConfig) appendUnstructured(resources, vsl) @@ -286,7 +286,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { WithImage(o.Image), WithResources(o.VeleroPodResources), WithSecret(secretPresent), - WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency), + WithDefaultRepoMaintenanceFrequency(o.DefaultRepoMaintenanceFrequency), WithGarbageCollectionFrequency(o.GarbageCollectionFrequency), WithUploaderType(o.UploaderType), } @@ -303,8 +303,8 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { deployOpts = append(deployOpts, WithPlugins(o.Plugins)) } - if o.DefaultVolumesToRestic { - deployOpts = append(deployOpts, WithDefaultVolumesToRestic()) + if o.DefaultVolumesToFsBackup { + deployOpts = append(deployOpts, WithDefaultVolumesToFsBackup()) } deploy := Deployment(o.Namespace, deployOpts...) diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index a7ab78245a..d75b393153 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -48,6 +48,10 @@ const ( // InitContainer is the name of the init container added // to workload pods to help with restores. InitContainer = "restic-wait" + + // DefaultVolumesToFsBackup specifies whether pod volume backup should be used, by default, to + // take backup of all pod volumes. + DefaultVolumesToFsBackup = false ) // volumeBackupInfo describes the backup info of a volume backed up by PodVolumeBackups @@ -253,9 +257,9 @@ func contains(list []string, k string) bool { return false } -// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. -func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { - if !defaultVolumesToRestic { +// GetVolumesByPod returns a list of volume names to backup for the provided pod. +func GetVolumesByPod(pod *corev1api.Pod, defaultVolumesToFsBackup bool) []string { + if !defaultVolumesToFsBackup { return GetVolumesToBackup(pod) } diff --git a/pkg/podvolume/util_test.go b/pkg/podvolume/util_test.go index 88b5746685..936ee26d37 100644 --- a/pkg/podvolume/util_test.go +++ b/pkg/podvolume/util_test.go @@ -348,16 +348,16 @@ func TestGetVolumesToBackup(t *testing.T) { } } -func TestGetPodVolumesUsingRestic(t *testing.T) { +func TestGetVolumesByPod(t *testing.T) { testCases := []struct { - name string - pod *corev1api.Pod - expected []string - defaultVolumesToRestic bool + name string + pod *corev1api.Pod + expected []string + defaultVolumesToFsBackup bool }{ { - name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", - defaultVolumesToRestic: false, + name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToFsBackup is false", + defaultVolumesToFsBackup: false, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -368,8 +368,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", - defaultVolumesToRestic: true, + name: "should get all pod volumes when defaultVolumesToFsBackup is true and no PVs are excluded", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -381,8 +381,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", - defaultVolumesToRestic: true, + name: "should get all pod volumes except ones excluded when defaultVolumesToFsBackup is true", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -401,8 +401,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude default service account token from restic backup", - defaultVolumesToRestic: true, + name: "should exclude default service account token from restic backup", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -416,8 +416,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude host path volumes from restic backups", - defaultVolumesToRestic: true, + name: "should exclude host path volumes from restic backups", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -438,8 +438,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude volumes mounting secrets", - defaultVolumesToRestic: true, + name: "should exclude volumes mounting secrets", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -460,8 +460,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude volumes mounting config maps", - defaultVolumesToRestic: true, + name: "should exclude volumes mounting config maps", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -482,8 +482,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude projected volumes", - defaultVolumesToRestic: true, + name: "should exclude projected volumes", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -517,8 +517,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude DownwardAPI volumes", - defaultVolumesToRestic: true, + name: "should exclude DownwardAPI volumes", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -553,7 +553,7 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) + actual := GetVolumesByPod(tc.pod, tc.defaultVolumesToFsBackup) sort.Strings(tc.expected) sort.Strings(actual) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 90a38f58be..f1ecb9a718 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -37,10 +37,6 @@ const ( // at which restic prune is run. DefaultMaintenanceFrequency = 7 * 24 * time.Hour - // DefaultVolumesToRestic specifies whether restic should be used, by default, to - // take backup of all pod volumes. - DefaultVolumesToRestic = false - // insecureSkipTLSVerifyKey is the flag in BackupStorageLocation's config // to indicate whether to skip TLS verify to setup insecure HTTPS connection. insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" diff --git a/test/e2e/basic/namespace-mapping.go b/test/e2e/basic/namespace-mapping.go index 2f72f23440..3a8d56ccef 100644 --- a/test/e2e/basic/namespace-mapping.go +++ b/test/e2e/basic/namespace-mapping.go @@ -57,7 +57,7 @@ func (n *NamespaceMapping) StartRun() error { n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, "--include-namespaces", strings.Join(*n.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } n.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index ae6e15455a..7c1d6ad4f6 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -81,7 +81,7 @@ func (m *MultiNSBackup) StartRun() error { m.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", m.BackupName, "--exclude-namespaces", strings.Join(*m.NSExcluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } m.RestoreArgs = []string{ diff --git a/test/e2e/basic/resources-check/namespaces_annotation.go b/test/e2e/basic/resources-check/namespaces_annotation.go index f3f602147c..5033dd14ec 100644 --- a/test/e2e/basic/resources-check/namespaces_annotation.go +++ b/test/e2e/basic/resources-check/namespaces_annotation.go @@ -56,7 +56,7 @@ func (n *NSAnnotationCase) Init() error { n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, "--include-namespaces", strings.Join(*n.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } n.RestoreArgs = []string{ diff --git a/test/e2e/basic/resources-check/rbac.go b/test/e2e/basic/resources-check/rbac.go index c07329d817..7657669cfb 100644 --- a/test/e2e/basic/resources-check/rbac.go +++ b/test/e2e/basic/resources-check/rbac.go @@ -71,7 +71,7 @@ func (r *RBACCase) Init() error { r.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", r.BackupName, "--include-namespaces", strings.Join(*r.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } r.RestoreArgs = []string{ diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go index af6074ee8f..1691413f9a 100644 --- a/test/e2e/orderedresources/ordered_resources.go +++ b/test/e2e/orderedresources/ordered_resources.go @@ -127,7 +127,7 @@ func (o *OrderedResources) Init() error { } o.ScheduleArgs = []string{"--schedule", "@every 1m", - "--include-namespaces", o.Namespace, "--default-volumes-to-restic", "--ordered-resources"} + "--include-namespaces", o.Namespace, "--default-volumes-to-fs-backup", "--ordered-resources"} var orderStr string for kind, resource := range o.OrderMap { orderStr += fmt.Sprintf("%s=%s;", kind, resource) diff --git a/test/e2e/resource-filtering/base.go b/test/e2e/resource-filtering/base.go index fcfd1fc37c..08def1a7bd 100644 --- a/test/e2e/resource-filtering/base.go +++ b/test/e2e/resource-filtering/base.go @@ -53,7 +53,7 @@ func (f *FilteringCase) Init() error { f.NamespacesTotal = 3 f.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", f.BackupName, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } f.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_label.go b/test/e2e/resource-filtering/exclude_label.go index 2147221c93..b5e46621bd 100644 --- a/test/e2e/resource-filtering/exclude_label.go +++ b/test/e2e/resource-filtering/exclude_label.go @@ -65,7 +65,7 @@ func (e *ExcludeFromBackup) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", e.NSBaseName, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_namespaces.go b/test/e2e/resource-filtering/exclude_namespaces.go index b76b15fb19..8d195c2476 100644 --- a/test/e2e/resource-filtering/exclude_namespaces.go +++ b/test/e2e/resource-filtering/exclude_namespaces.go @@ -83,7 +83,7 @@ func (e *ExcludeNamespaces) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--exclude-namespaces", strings.Join(*e.nsExcluded, ","), "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ @@ -96,7 +96,7 @@ func (e *ExcludeNamespaces) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_resources.go b/test/e2e/resource-filtering/exclude_resources.go index 1080ee70f1..844a4243e6 100644 --- a/test/e2e/resource-filtering/exclude_resources.go +++ b/test/e2e/resource-filtering/exclude_resources.go @@ -66,7 +66,7 @@ func (e *ExcludeResources) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), "--exclude-resources", "secrets", - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ @@ -86,7 +86,7 @@ func (e *ExcludeResources) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", e.RestoreName, diff --git a/test/e2e/resource-filtering/include_namespaces.go b/test/e2e/resource-filtering/include_namespaces.go index 1d6fdf49d6..5783586fad 100644 --- a/test/e2e/resource-filtering/include_namespaces.go +++ b/test/e2e/resource-filtering/include_namespaces.go @@ -73,7 +73,7 @@ func (i *IncludeNamespaces) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ @@ -92,7 +92,7 @@ func (i *IncludeNamespaces) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.allTestNamespaces, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/include_resources.go b/test/e2e/resource-filtering/include_resources.go index 102991bc66..268ee6fb85 100644 --- a/test/e2e/resource-filtering/include_resources.go +++ b/test/e2e/resource-filtering/include_resources.go @@ -63,7 +63,7 @@ func (i *IncludeResources) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-resources", "deployments,configmaps", - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ @@ -81,7 +81,7 @@ func (i *IncludeResources) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", i.RestoreName, diff --git a/test/e2e/resource-filtering/label_selector.go b/test/e2e/resource-filtering/label_selector.go index 3b0edfd3d4..fff56b1b56 100644 --- a/test/e2e/resource-filtering/label_selector.go +++ b/test/e2e/resource-filtering/label_selector.go @@ -64,7 +64,7 @@ func (l *LabelSelector) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", l.BackupName, "--selector", "resourcefiltering=true", "--include-namespaces", strings.Join(*l.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } l.RestoreArgs = []string{ diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 9b68024a8f..ff11334dd3 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -323,10 +323,10 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes") } else { - args = append(args, "--default-volumes-to-restic") + args = append(args, "--default-volumes-to-fs-backup") // To workaround https://github.com/vmware-tanzu/velero-plugin-for-vsphere/issues/347 for vsphere plugin v1.1.1 // if the "--snapshot-volumes=false" isn't specified explicitly, the vSphere plugin will always take snapshots - // for the volumes even though the "--default-volumes-to-restic" is specified + // for the volumes even though the "--default-volumes-to-fs-backup" is specified // TODO This can be removed if the logic of vSphere plugin bump up to 1.3 args = append(args, "--snapshot-volumes=false") } @@ -362,7 +362,7 @@ func VeleroBackupExcludeNamespaces(ctx context.Context, veleroCLI string, velero args := []string{ "--namespace", veleroNamespace, "create", "backup", backupName, "--exclude-namespaces", namespaces, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupName, args) } @@ -373,7 +373,7 @@ func VeleroBackupIncludeNamespaces(ctx context.Context, veleroCLI string, velero args := []string{ "--namespace", veleroNamespace, "create", "backup", backupName, "--include-namespaces", namespaces, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupName, args) } From ce247a3d908183b7a266793a77826f7db50a2868 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Fri, 23 Sep 2022 15:44:39 +0800 Subject: [PATCH 314/366] Remove snapshot related lister, informer and client from backup controller. (#5299) Signed-off-by: Xun Jiang --- changelogs/unreleased/5299-jxun | 1 + pkg/backup/request.go | 2 +- pkg/cmd/server/server.go | 38 ------ pkg/controller/backup_controller.go | 183 +++++++++++++--------------- pkg/util/csi/reset.go | 2 +- 5 files changed, 91 insertions(+), 135 deletions(-) create mode 100644 changelogs/unreleased/5299-jxun diff --git a/changelogs/unreleased/5299-jxun b/changelogs/unreleased/5299-jxun new file mode 100644 index 0000000000..ce574df7a7 --- /dev/null +++ b/changelogs/unreleased/5299-jxun @@ -0,0 +1 @@ +Remove snapshot related lister, informer and client from backup controller. \ No newline at end of file diff --git a/pkg/backup/request.go b/pkg/backup/request.go index 38cd499177..69dbeca704 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -50,7 +50,7 @@ type Request struct { VolumeSnapshots []*volume.Snapshot PodVolumeBackups []*velerov1api.PodVolumeBackup BackedUpItems map[itemKey]struct{} - CSISnapshots []*snapshotv1api.VolumeSnapshot + CSISnapshots []snapshotv1api.VolumeSnapshot } // BackupResourceList returns the list of backed up resources grouped by the API diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index c4cb302519..ccf9f1edd1 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -35,7 +35,6 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" corev1api "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" @@ -50,7 +49,6 @@ import ( snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" - snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" @@ -553,36 +551,6 @@ func (s *server) initRestic() error { return nil } -func (s *server) getCSISnapshotListers() (snapshotv1listers.VolumeSnapshotLister, snapshotv1listers.VolumeSnapshotContentLister, snapshotv1listers.VolumeSnapshotClassLister) { - // Make empty listers that will only be populated if CSI is properly enabled. - var vsLister snapshotv1listers.VolumeSnapshotLister - var vscLister snapshotv1listers.VolumeSnapshotContentLister - var vsClassLister snapshotv1listers.VolumeSnapshotClassLister - var err error - - // If CSI is enabled, check for the CSI groups and generate the listers - // If CSI isn't enabled, return empty listers. - if features.IsEnabled(velerov1api.CSIFeatureFlag) { - _, err = s.discoveryClient.ServerResourcesForGroupVersion(snapshotv1api.SchemeGroupVersion.String()) - switch { - case apierrors.IsNotFound(err): - // CSI is enabled, but the required CRDs aren't installed, so halt. - s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", velerov1api.CSIFeatureFlag, snapshotv1api.SchemeGroupVersion.String()) - case err == nil: - // CSI is enabled, and the resources were found. - // Instantiate the listers fully - s.logger.Debug("Creating CSI listers") - // Access the wrapped factory directly here since we've already done the feature flag check above to know it's safe. - vsLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshots().Lister() - vscLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshotContents().Lister() - vsClassLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshotClasses().Lister() - case err != nil: - cmd.CheckError(err) - } - } - return vsLister, vscLister, vsClassLister -} - func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string) error { s.logger.Info("Starting controllers") @@ -607,8 +575,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupStoreGetter := persistence.NewObjectBackupStoreGetter(s.credentialFileStore) - csiVSLister, csiVSCLister, csiVSClassLister := s.getCSISnapshotListers() - backupTracker := controller.NewBackupTracker() backupControllerRunInfo := func() controllerRunInfo { @@ -645,10 +611,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string defaultVolumeSnapshotLocations, s.metrics, s.config.formatFlag.Parse(), - csiVSLister, - s.csiSnapshotClient, - csiVSCLister, - csiVSClassLister, backupStoreGetter, s.credentialFileStore, ) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index c7e0ca944c..39444459e4 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -46,8 +46,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/csi" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" - snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" @@ -76,29 +74,25 @@ import ( type backupController struct { *genericController - discoveryHelper discovery.Helper - backupper pkgbackup.Backupper - lister velerov1listers.BackupLister - client velerov1client.BackupsGetter - kbClient kbclient.Client - clock clock.Clock - backupLogLevel logrus.Level - newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - backupTracker BackupTracker - defaultBackupLocation string - defaultVolumesToFsBackup bool - defaultBackupTTL time.Duration - defaultCSISnapshotTimeout time.Duration - snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister - defaultSnapshotLocations map[string]string - metrics *metrics.ServerMetrics - backupStoreGetter persistence.ObjectBackupStoreGetter - formatFlag logging.Format - volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister - volumeSnapshotClient *snapshotterClientSet.Clientset - volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister - volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister - credentialFileStore credentials.FileStore + discoveryHelper discovery.Helper + backupper pkgbackup.Backupper + lister velerov1listers.BackupLister + client velerov1client.BackupsGetter + kbClient kbclient.Client + clock clock.Clock + backupLogLevel logrus.Level + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager + backupTracker BackupTracker + defaultBackupLocation string + defaultVolumesToFsBackup bool + defaultBackupTTL time.Duration + defaultCSISnapshotTimeout time.Duration + snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister + defaultSnapshotLocations map[string]string + metrics *metrics.ServerMetrics + backupStoreGetter persistence.ObjectBackupStoreGetter + formatFlag logging.Format + credentialFileStore credentials.FileStore } func NewBackupController( @@ -119,38 +113,30 @@ func NewBackupController( defaultSnapshotLocations map[string]string, metrics *metrics.ServerMetrics, formatFlag logging.Format, - volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, - volumeSnapshotClient *snapshotterClientSet.Clientset, - volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, - volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister, backupStoreGetter persistence.ObjectBackupStoreGetter, credentialStore credentials.FileStore, ) Interface { c := &backupController{ - genericController: newGenericController(Backup, logger), - discoveryHelper: discoveryHelper, - backupper: backupper, - lister: backupInformer.Lister(), - client: client, - clock: &clock.RealClock{}, - backupLogLevel: backupLogLevel, - newPluginManager: newPluginManager, - backupTracker: backupTracker, - kbClient: kbClient, - defaultBackupLocation: defaultBackupLocation, - defaultVolumesToFsBackup: defaultVolumesToFsBackup, - defaultBackupTTL: defaultBackupTTL, - defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, - snapshotLocationLister: volumeSnapshotLocationLister, - defaultSnapshotLocations: defaultSnapshotLocations, - metrics: metrics, - formatFlag: formatFlag, - volumeSnapshotLister: volumeSnapshotLister, - volumeSnapshotClient: volumeSnapshotClient, - volumeSnapshotContentLister: volumeSnapshotContentLister, - volumeSnapshotClassLister: volumesnapshotClassLister, - backupStoreGetter: backupStoreGetter, - credentialFileStore: credentialStore, + genericController: newGenericController(Backup, logger), + discoveryHelper: discoveryHelper, + backupper: backupper, + lister: backupInformer.Lister(), + client: client, + clock: &clock.RealClock{}, + backupLogLevel: backupLogLevel, + newPluginManager: newPluginManager, + backupTracker: backupTracker, + kbClient: kbClient, + defaultBackupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, + defaultBackupTTL: defaultBackupTTL, + defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, + snapshotLocationLister: volumeSnapshotLocationLister, + defaultSnapshotLocations: defaultSnapshotLocations, + metrics: metrics, + formatFlag: formatFlag, + backupStoreGetter: backupStoreGetter, + credentialFileStore: credentialStore, } c.syncHandler = c.processBackup @@ -662,47 +648,51 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { // Empty slices here so that they can be passed in to the persistBackup call later, regardless of whether or not CSI's enabled. // This way, we only make the Lister call if the feature flag's on. - var volumeSnapshots []*snapshotv1api.VolumeSnapshot - var volumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent - var volumeSnapshotClasses []*snapshotv1api.VolumeSnapshotClass + var volumeSnapshots []snapshotv1api.VolumeSnapshot + var volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent + var volumeSnapshotClasses []snapshotv1api.VolumeSnapshotClass if features.IsEnabled(velerov1api.CSIFeatureFlag) { selector := label.NewSelectorForBackup(backup.Name) // Listers are wrapped in a nil check out of caution, since they may not be populated based on the // EnableCSI feature flag. This is more to guard against programmer error, as they shouldn't be nil // when EnableCSI is on. - if c.volumeSnapshotLister != nil { - volumeSnapshots, err = c.volumeSnapshotLister.List(selector) - if err != nil { - backupLog.Error(err) - } + vsList := &snapshotv1api.VolumeSnapshotList{} + vscList := &snapshotv1api.VolumeSnapshotContentList{} - err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots, backup.Spec.CSISnapshotTimeout.Duration) - if err != nil { - backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) - } - - backup.CSISnapshots = volumeSnapshots + err = c.kbClient.List(context.Background(), vsList, &kbclient.ListOptions{LabelSelector: selector}) + if err != nil { + backupLog.Error(err) + } + if len(vsList.Items) >= 0 { + volumeSnapshots = vsList.Items + } + err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots, backup.Spec.CSISnapshotTimeout.Duration) + if err != nil { + backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) } + backup.CSISnapshots = volumeSnapshots - if c.volumeSnapshotContentLister != nil { - volumeSnapshotContents, err = c.volumeSnapshotContentLister.List(selector) - if err != nil { - backupLog.Error(err) - } + err = c.kbClient.List(context.Background(), vscList, &kbclient.ListOptions{LabelSelector: selector}) + if err != nil { + backupLog.Error(err) } + if len(vscList.Items) >= 0 { + volumeSnapshotContents = vscList.Items + } + vsClassSet := sets.NewString() for _, vsc := range volumeSnapshotContents { // persist the volumesnapshotclasses referenced by vsc - if c.volumeSnapshotClassLister != nil && - vsc.Spec.VolumeSnapshotClassName != nil && - !vsClassSet.Has(*vsc.Spec.VolumeSnapshotClassName) { - if vsClass, err := c.volumeSnapshotClassLister.Get(*vsc.Spec.VolumeSnapshotClassName); err != nil { + if vsc.Spec.VolumeSnapshotClassName != nil && !vsClassSet.Has(*vsc.Spec.VolumeSnapshotClassName) { + vsClass := &snapshotv1api.VolumeSnapshotClass{} + if err := c.kbClient.Get(context.TODO(), kbclient.ObjectKey{Name: *vsc.Spec.VolumeSnapshotClassName}, vsClass); err != nil { backupLog.Error(err) } else { vsClassSet.Insert(*vsc.Spec.VolumeSnapshotClassName) - volumeSnapshotClasses = append(volumeSnapshotClasses, vsClass) + volumeSnapshotClasses = append(volumeSnapshotClasses, *vsClass) } } + if err := csi.ResetVolumeSnapshotContent(vsc); err != nil { backupLog.Error(err) } @@ -806,9 +796,9 @@ func persistBackup(backup *pkgbackup.Request, backupContents, backupLog *os.File, backupStore persistence.BackupStore, log logrus.FieldLogger, - csiVolumeSnapshots []*snapshotv1api.VolumeSnapshot, - csiVolumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent, - csiVolumesnapshotClasses []*snapshotv1api.VolumeSnapshotClass, + csiVolumeSnapshots []snapshotv1api.VolumeSnapshot, + csiVolumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, + csiVolumesnapshotClasses []snapshotv1api.VolumeSnapshotClass, ) []error { persistErrs := []error{} backupJSON := new(bytes.Buffer) @@ -917,7 +907,7 @@ func encodeToJSONGzip(data interface{}, desc string) (*bytes.Buffer, []error) { // using goroutine here instead of waiting in CSI plugin, because it's not easy to make BackupItemAction // parallel by now. After BackupItemAction parallel is implemented, this logic should be moved to CSI plugin // as https://github.com/vmware-tanzu/velero-plugin-for-csi/pull/100 -func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []*snapshotv1api.VolumeSnapshot, +func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []snapshotv1api.VolumeSnapshot, csiSnapshotTimeout time.Duration) error { eg, _ := errgroup.WithContext(ctx) timeout := csiSnapshotTimeout @@ -927,7 +917,8 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo volumeSnapshot := vs eg.Go(func() error { err := wait.PollImmediate(interval, timeout, func() (bool, error) { - tmpVS, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(volumeSnapshot.Namespace).Get(ctx, volumeSnapshot.Name, metav1.GetOptions{}) + tmpVS := &snapshotv1api.VolumeSnapshot{} + err := c.kbClient.Get(ctx, kbclient.ObjectKey{Name: volumeSnapshot.Name, Namespace: volumeSnapshot.Namespace}, tmpVS) if err != nil { return false, errors.Wrapf(err, fmt.Sprintf("failed to get volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)) } @@ -952,28 +943,29 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo // which will cause snapshot deletion on cloud provider, then backup cannot restore the PV. // If DeletionPolicy is Retain, just delete it. If DeletionPolicy is Delete, need to // change DeletionPolicy to Retain before deleting VS, then change DeletionPolicy back to Delete. -func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api.VolumeSnapshot, - volumeSnapshotContents []*snapshotv1api.VolumeSnapshotContent, +func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api.VolumeSnapshot, + volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, backup pkgbackup.Request, logger logrus.FieldLogger) { var wg sync.WaitGroup - vscMap := make(map[string]*snapshotv1api.VolumeSnapshotContent) + vscMap := make(map[string]snapshotv1api.VolumeSnapshotContent) for _, vsc := range volumeSnapshotContents { vscMap[vsc.Name] = vsc } for _, vs := range volumeSnapshots { wg.Add(1) - go func(vs *snapshotv1api.VolumeSnapshot) { + go func(vs snapshotv1api.VolumeSnapshot) { defer wg.Done() - var vsc *snapshotv1api.VolumeSnapshotContent + var vsc snapshotv1api.VolumeSnapshotContent modifyVSCFlag := false if vs.Status.BoundVolumeSnapshotContentName != nil && len(*vs.Status.BoundVolumeSnapshotContentName) > 0 { - vsc = vscMap[*vs.Status.BoundVolumeSnapshotContentName] - if nil == vsc { + var found bool + if vsc, found = vscMap[*vs.Status.BoundVolumeSnapshotContentName]; !found { logger.Errorf("Not find %s from the vscMap", vs.Status.BoundVolumeSnapshotContentName) return } + if vsc.Spec.DeletionPolicy == snapshotv1api.VolumeSnapshotContentDelete { modifyVSCFlag = true } @@ -987,7 +979,7 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api logger.Debugf("Patching VolumeSnapshotContent %s", vsc.Name) original := vsc.DeepCopy() vsc.Spec.DeletionPolicy = snapshotv1api.VolumeSnapshotContentRetain - if err := c.kbClient.Patch(context.Background(), vsc, kbclient.MergeFrom(original)); err != nil { + if err := c.kbClient.Patch(context.Background(), &vsc, kbclient.MergeFrom(original)); err != nil { logger.Errorf("fail to modify VolumeSnapshotContent %s DeletionPolicy to Retain: %s", vsc.Name, err.Error()) return } @@ -1003,7 +995,7 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api // Delete VolumeSnapshot from cluster logger.Debugf("Deleting VolumeSnapshotContent %s", vsc.Name) - err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(vs.Namespace).Delete(context.TODO(), vs.Name, metav1.DeleteOptions{}) + err := c.kbClient.Delete(context.TODO(), &vs) if err != nil { logger.Errorf("fail to delete VolumeSnapshot %s/%s: %s", vs.Namespace, vs.Name, err.Error()) } @@ -1018,18 +1010,19 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api // and Source. Source is updated to let csi-controller thinks the VSC is statically provsisioned with VS. // Set VolumeSnapshotRef's UID to nil will let the csi-controller finds out the related VS is gone, then // VSC can be deleted. -func (c *backupController) recreateVolumeSnapshotContent(vsc *snapshotv1api.VolumeSnapshotContent) error { +func (c *backupController) recreateVolumeSnapshotContent(vsc snapshotv1api.VolumeSnapshotContent) error { timeout := 1 * time.Minute interval := 1 * time.Second - err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Delete(context.TODO(), vsc.Name, metav1.DeleteOptions{}) + err := c.kbClient.Delete(context.TODO(), &vsc) if err != nil { return errors.Wrapf(err, "fail to delete VolumeSnapshotContent: %s", vsc.Name) } // Check VolumeSnapshotContents is already deleted, before re-creating it. err = wait.PollImmediate(interval, timeout, func() (bool, error) { - _, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Get(context.TODO(), vsc.Name, metav1.GetOptions{}) + tmpVSC := &snapshotv1api.VolumeSnapshotContent{} + err := c.kbClient.Get(context.TODO(), kbclient.ObjectKey{Name: vsc.Name}, tmpVSC) if err != nil { if apierrors.IsNotFound(err) { return true, nil @@ -1058,7 +1051,7 @@ func (c *backupController) recreateVolumeSnapshotContent(vsc *snapshotv1api.Volu } // ResourceVersion shouldn't exist for new creation. vsc.ResourceVersion = "" - _, err = c.volumeSnapshotClient.SnapshotV1().VolumeSnapshotContents().Create(context.TODO(), vsc, metav1.CreateOptions{}) + err = c.kbClient.Create(context.TODO(), &vsc) if err != nil { return errors.Wrapf(err, "fail to create VolumeSnapshotContent %s", vsc.Name) } diff --git a/pkg/util/csi/reset.go b/pkg/util/csi/reset.go index 5065aae780..f762f8aaa6 100644 --- a/pkg/util/csi/reset.go +++ b/pkg/util/csi/reset.go @@ -27,7 +27,7 @@ import ( // It will move the snapshot Handle to the source to avoid the snapshot-controller creating a snapshot when it's // synced by the backup sync controller. // It will return an error if the snapshot handle is not set, which should not happen when this func is called. -func ResetVolumeSnapshotContent(snapCont *snapshotv1api.VolumeSnapshotContent) error { +func ResetVolumeSnapshotContent(snapCont snapshotv1api.VolumeSnapshotContent) error { if snapCont.Status != nil && snapCont.Status.SnapshotHandle != nil && len(*snapCont.Status.SnapshotHandle) > 0 { v := *snapCont.Status.SnapshotHandle snapCont.Spec.Source = snapshotv1api.VolumeSnapshotContentSource{ From 6c8981b0ada63b00fc26e8aa669562a048eb15a5 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 23 Sep 2022 16:09:21 +0800 Subject: [PATCH 315/366] fix issue 5386 Signed-off-by: Lyndon-Li --- changelogs/unreleased/5387-lyndon | 1 + pkg/repository/provider/unified_repo.go | 17 +++++++++++ pkg/repository/provider/unified_repo_test.go | 29 ++++++++++++++++--- .../udmrepo/kopialib/backend/utils.go | 12 ++++---- 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/5387-lyndon diff --git a/changelogs/unreleased/5387-lyndon b/changelogs/unreleased/5387-lyndon new file mode 100644 index 0000000000..63cf32bf37 --- /dev/null +++ b/changelogs/unreleased/5387-lyndon @@ -0,0 +1 @@ +Fix issue 5386: Velero providers a full URL as the S3Url while the underlying minio client only accept the host part of the URL as the endpoint and the schema should be specified separately. \ No newline at end of file diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index ec71ae4c9b..6bb4f38445 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -19,7 +19,9 @@ package provider import ( "context" "fmt" + "net/url" "path" + "strconv" "strings" "time" @@ -435,6 +437,7 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo if backendType == repoconfig.AWSBackend { s3Url := config["s3Url"] + disableTls := false var err error if s3Url == "" { @@ -444,10 +447,24 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo } s3Url = fmt.Sprintf("s3-%s.amazonaws.com", region) + disableTls = false + } else { + url, err := url.Parse(s3Url) + if err != nil { + return map[string]string{}, errors.Wrapf(err, "error to parse s3Url %s", s3Url) + } + + if url.Path != "" && url.Path != "/" { + return map[string]string{}, errors.Errorf("path is not expected in s3Url %s", s3Url) + } + + s3Url = url.Host + disableTls = (url.Scheme == "http") } result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3Url, "/") result[udmrepo.StoreOptionS3DisableTlsVerify] = config["insecureSkipTLSVerify"] + result[udmrepo.StoreOptionS3DisableTls] = strconv.FormatBool(disableTls) } else if backendType == repoconfig.AzureBackend { result[udmrepo.StoreOptionAzureDomain] = getAzureStorageDomain(config) } diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index fb1c9d170f..e6f891cce7 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -250,7 +250,7 @@ func TestGetStorageVariables(t *testing.T) { expectedErr: "invalid storage provider", }, { - name: "aws, ObjectStorage section not exists in BSL, s3Url exist", + name: "aws, ObjectStorage section not exists in BSL, s3Url exist, https", backupLocation: velerov1api.BackupStorageLocation{ Spec: velerov1api.BackupStorageLocationSpec{ Provider: "velero.io/aws", @@ -258,7 +258,7 @@ func TestGetStorageVariables(t *testing.T) { "bucket": "fake-bucket", "prefix": "fake-prefix", "region": "fake-region/", - "s3Url": "fake-url", + "s3Url": "https://fake-url/", "insecureSkipTLSVerify": "true", }, }, @@ -270,9 +270,28 @@ func TestGetStorageVariables(t *testing.T) { "region": "fake-region", "fspath": "", "endpoint": "fake-url", + "doNotUseTLS": "false", "skipTLSVerify": "true", }, }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url exist, invalid", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region/", + "s3Url": "https://fake-url/fake-path", + "insecureSkipTLSVerify": "true", + }, + }, + }, + repoBackend: "fake-repo-type", + expected: map[string]string{}, + expectedErr: "path is not expected in s3Url https://fake-url/fake-path", + }, { name: "aws, ObjectStorage section not exists in BSL, s3Url not exist", backupLocation: velerov1api.BackupStorageLocation{ @@ -295,6 +314,7 @@ func TestGetStorageVariables(t *testing.T) { "region": "region from bucket: fake-bucket", "fspath": "", "endpoint": "s3-region from bucket: fake-bucket.amazonaws.com", + "doNotUseTLS": "false", "skipTLSVerify": "false", }, }, @@ -313,7 +333,7 @@ func TestGetStorageVariables(t *testing.T) { expectedErr: "error get s3 bucket region: fake error", }, { - name: "aws, ObjectStorage section exists in BSL, s3Url exist", + name: "aws, ObjectStorage section exists in BSL, s3Url exist, http", backupLocation: velerov1api.BackupStorageLocation{ Spec: velerov1api.BackupStorageLocationSpec{ Provider: "velero.io/aws", @@ -321,7 +341,7 @@ func TestGetStorageVariables(t *testing.T) { "bucket": "fake-bucket-config", "prefix": "fake-prefix-config", "region": "fake-region", - "s3Url": "fake-url", + "s3Url": "http://fake-url/", "insecureSkipTLSVerify": "false", }, StorageType: velerov1api.StorageType{ @@ -342,6 +362,7 @@ func TestGetStorageVariables(t *testing.T) { "region": "fake-region", "fspath": "", "endpoint": "fake-url", + "doNotUseTLS": "true", "skipTLSVerify": "false", }, }, diff --git a/pkg/repository/udmrepo/kopialib/backend/utils.go b/pkg/repository/udmrepo/kopialib/backend/utils.go index 5aab1f1afb..eb673539fd 100644 --- a/pkg/repository/udmrepo/kopialib/backend/utils.go +++ b/pkg/repository/udmrepo/kopialib/backend/utils.go @@ -39,12 +39,14 @@ func optionalHaveString(key string, flags map[string]string) string { func optionalHaveBool(ctx context.Context, key string, flags map[string]string) bool { if value, exist := flags[key]; exist { - ret, err := strconv.ParseBool(value) - if err == nil { - return ret - } + if value != "" { + ret, err := strconv.ParseBool(value) + if err == nil { + return ret + } - backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } } return false From 081b70d0eb55c04ddb9c7f53fc9d4a90a50f59bf Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 13 Sep 2022 04:42:06 +0000 Subject: [PATCH 316/366] Add backup opt-in/opt-out E2E test Signed-off-by: danfengl --- changelogs/unreleased/5331-danfengliu | 1 + test/e2e/backups/ttl.go | 7 +- test/e2e/basic/enable_api_group_versions.go | 11 +- test/e2e/e2e_suite_test.go | 5 + test/e2e/migration/migration.go | 2 +- .../e2e/orderedresources/ordered_resources.go | 6 +- test/e2e/pv-backup/pv-backup-filter.go | 209 ++++++++++++++++++ test/e2e/test/test.go | 10 +- test/e2e/testdata/storage-class/aws.yaml | 9 + test/e2e/testdata/storage-class/azure.yaml | 11 + test/e2e/testdata/storage-class/gcp.yaml | 13 ++ test/e2e/testdata/storage-class/vsphere.yaml | 11 + test/e2e/util/common/common.go | 16 ++ test/e2e/util/k8s/common.go | 65 ++++++ test/e2e/util/k8s/namespace.go | 2 +- test/e2e/util/k8s/persistentvolumes.go | 38 ++++ test/e2e/util/k8s/pod.go | 79 +++++++ test/e2e/util/k8s/pvc.go | 51 +++++ 18 files changed, 524 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/5331-danfengliu create mode 100644 test/e2e/pv-backup/pv-backup-filter.go create mode 100644 test/e2e/testdata/storage-class/aws.yaml create mode 100644 test/e2e/testdata/storage-class/azure.yaml create mode 100644 test/e2e/testdata/storage-class/gcp.yaml create mode 100644 test/e2e/testdata/storage-class/vsphere.yaml create mode 100644 test/e2e/util/k8s/pod.go create mode 100644 test/e2e/util/k8s/pvc.go diff --git a/changelogs/unreleased/5331-danfengliu b/changelogs/unreleased/5331-danfengliu new file mode 100644 index 0000000000..a48cc7bec3 --- /dev/null +++ b/changelogs/unreleased/5331-danfengliu @@ -0,0 +1 @@ +Add opt-in and opt-out PersistentVolume backup to E2E tests \ No newline at end of file diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index b2e05a17ba..6ce8816a93 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -58,12 +58,11 @@ func (b *TTL) Init() { } func TTLTest() { + var err error useVolumeSnapshots := true test := new(TTL) - client, err := NewTestClient(VeleroCfg.DefaultCluster) - if err != nil { - println(err.Error()) - } + client := *VeleroCfg.ClientToInstallVelero + //Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") BeforeEach(func() { diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 5c4d11c348..6930c2b2df 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/util/common" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -464,14 +465,8 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso func installCRD(ctx context.Context, yaml string) error { fmt.Printf("Install CRD with %s.\n", yaml) - cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", yaml) - - _, stderr, err := veleroexec.RunCommand(cmd) - if err != nil { - return errors.Wrap(err, stderr) - } - - return nil + err := KubectlApplyFile(ctx, yaml) + return err } func deleteCRD(ctx context.Context, yaml string) error { diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 9e509b3024..7bf85a16d9 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -35,6 +35,7 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/bsl-mgmt" . "github.com/vmware-tanzu/velero/test/e2e/orderedresources" . "github.com/vmware-tanzu/velero/test/e2e/privilegesmgmt" + . "github.com/vmware-tanzu/velero/test/e2e/pv-backup" . "github.com/vmware-tanzu/velero/test/e2e/resource-filtering" . "github.com/vmware-tanzu/velero/test/e2e/scale" . "github.com/vmware-tanzu/velero/test/e2e/upgrade" @@ -118,6 +119,7 @@ var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once t var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) var _ = Describe("[Migration][Restic]", MigrationWithRestic) + var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots) var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) @@ -125,6 +127,9 @@ var _ = Describe("[Schedule][OrederedResources] Backup resources should follow t var _ = Describe("[NamespaceMapping][Single] Backup resources should follow the specific order in schedule", OneNamespaceMappingTest) var _ = Describe("[NamespaceMapping][Multiple] Backup resources should follow the specific order in schedule", MultiNamespacesMappingTest) +var _ = Describe("[pv-backup][Opt-In] Backup resources should follow the specific order in schedule", OptInPVBackupTest) +var _ = Describe("[pv-backup][Opt-Out] Backup resources should follow the specific order in schedule", OptOutPVBackupTest) + func GetKubeconfigContext() error { var err error var tcDefault, tcStandby TestClient diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 90f96f578b..5c31ca9b78 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -85,7 +85,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) DeleteNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace, true) }) } - By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultCluster), func() { + By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() { Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient }) diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go index af6074ee8f..ba8f3f7bbc 100644 --- a/test/e2e/orderedresources/ordered_resources.go +++ b/test/e2e/orderedresources/ordered_resources.go @@ -112,10 +112,8 @@ func ScheduleOrderedResources() { func (o *OrderedResources) Init() error { rand.Seed(time.Now().UnixNano()) UUIDgen, _ = uuid.NewRandom() - client, err := NewTestClient(VeleroCfg.DefaultCluster) - if err != nil { - return fmt.Errorf("failed to init ordered resources test with err %v", err) - } + client := *VeleroCfg.ClientToInstallVelero + o.Client = client o.ScheduleName = "schedule-ordered-resources-" + UUIDgen.String() o.NSBaseName = "schedule-ordered-resources" diff --git a/test/e2e/pv-backup/pv-backup-filter.go b/test/e2e/pv-backup/pv-backup-filter.go new file mode 100644 index 0000000000..6c5148c979 --- /dev/null +++ b/test/e2e/pv-backup/pv-backup-filter.go @@ -0,0 +1,209 @@ +package basic + +import ( + "context" + "fmt" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/pkg/errors" + + . "github.com/vmware-tanzu/velero/test/e2e" + . "github.com/vmware-tanzu/velero/test/e2e/test" + . "github.com/vmware-tanzu/velero/test/e2e/util/common" + . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" +) + +type PVBackupFiltering struct { + TestCase + annotation string + podsList [][]string + volumesList [][]string + id string +} + +const POD_COUNT, VOLUME_COUNT_PER_POD = 2, 3 +const OPT_IN_ANN, OPT_OUT_ANN = "backup.velero.io/backup-volumes", "backup.velero.io/backup-volumes-excludes" +const FILE_NAME = "test-data.txt" + +var OptInPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_IN_ANN, id: "opt-in"}) +var OptOutPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_OUT_ANN, id: "opt-out"}) + +func (p *PVBackupFiltering) Init() error { + p.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + p.Client = TestClientInstance + p.NSBaseName = "ns" + p.NSIncluded = &[]string{fmt.Sprintf("%s-%s-%d", p.NSBaseName, p.id, 1), fmt.Sprintf("%s-%s-%d", p.NSBaseName, p.id, 2)} + + p.TestMsg = &TestMSG{ + Desc: "Backup PVs filtering by opt-in/opt-out annotation", + FailedMSG: "Failed to PVs filtering by opt-in/opt-out annotation", + Text: fmt.Sprintf("Should backup PVs in namespace %s according to annotation %s", *p.NSIncluded, p.annotation), + } + return nil +} + +func (p *PVBackupFiltering) StartRun() error { + err := installStorageClass(p.Ctx, fmt.Sprintf("testdata/storage-class/%s.yaml", VeleroCfg.CloudProvider)) + if err != nil { + return err + } + p.BackupName = p.BackupName + "backup-opt-in-" + UUIDgen.String() + p.RestoreName = p.RestoreName + "restore-opt-in-" + UUIDgen.String() + p.BackupArgs = []string{ + "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", p.BackupName, + "--include-namespaces", strings.Join(*p.NSIncluded, ","), + "--snapshot-volumes=false", "--wait", + } + if p.annotation == OPT_OUT_ANN { + p.BackupArgs = append(p.BackupArgs, "--default-volumes-to-restic") + + } + p.RestoreArgs = []string{ + "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", p.RestoreName, + "--from-backup", p.BackupName, "--wait", + } + return nil +} +func (p *PVBackupFiltering) CreateResources() error { + p.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + for _, ns := range *p.NSIncluded { + By(fmt.Sprintf("Create namespaces %s for workload\n", ns), func() { + Expect(CreateNamespace(p.Ctx, p.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) + }) + var pods []string + By(fmt.Sprintf("Deploy a few pods with several PVs in namespace %s", ns), func() { + var volumesToAnnotation string + //Make sure PVC name is unique from other tests to avoid PVC creation error + for i := 0; i <= POD_COUNT-1; i++ { + var volumeToAnnotationList []string + var volumes []string + for j := 0; j <= VOLUME_COUNT_PER_POD-1; j++ { + volume := fmt.Sprintf("volume-%s-%d-%d", p.id, i, j) + volumes = append(volumes, volume) + //Volumes cherry pick policy for opt-in/out annotation to pods + if j%2 == 0 { + volumeToAnnotationList = append(volumeToAnnotationList, volume) + } + } + p.volumesList = append(p.volumesList, volumes) + volumesToAnnotation = strings.Join(volumeToAnnotationList, ",") + podName := fmt.Sprintf("pod-%d", i) + pods = append(pods, podName) + By(fmt.Sprintf("Create pod %s in namespace %s", podName, ns), func() { + pod, err := CreatePodWithPVC(p.Client, ns, podName, "e2e-storage-class", volumes) + Expect(err).To(Succeed()) + ann := map[string]string{ + p.annotation: volumesToAnnotation, + } + By(fmt.Sprintf("Add annotation to pod %s of namespace %s", pod.Name, ns), func() { + _, err := AddAnnotationToPod(p.Ctx, p.Client, ns, pod.Name, ann) + Expect(err).To(Succeed()) + }) + }) + } + }) + p.podsList = append(p.podsList, pods) + } + By(fmt.Sprintf("Waiting for all pods to start %s\n", p.podsList), func() { + for index, ns := range *p.NSIncluded { + By(fmt.Sprintf("Waiting for all pods to start %d in namespace %s", index, ns), func() { + WaitForPods(p.Ctx, p.Client, ns, p.podsList[index]) + }) + } + }) + By(fmt.Sprintf("Polulate all pods %s with file %s", p.podsList, FILE_NAME), func() { + for index, ns := range *p.NSIncluded { + By(fmt.Sprintf("Creating file in all pods to start %d in namespace %s", index, ns), func() { + WaitForPods(p.Ctx, p.Client, ns, p.podsList[index]) + for i, pod := range p.podsList[index] { + for j := range p.volumesList[i] { + Expect(CreateFileToPod(p.Ctx, ns, pod, p.volumesList[i][j], + FILE_NAME, fileContent(ns, pod, p.volumesList[i][j]))).To(Succeed()) + } + } + }) + } + }) + return nil +} + +func (p *PVBackupFiltering) Verify() error { + p.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) + By(fmt.Sprintf("Waiting for all pods to start %s", p.podsList), func() { + for index, ns := range *p.NSIncluded { + By(fmt.Sprintf("Waiting for all pods to start %d in namespace %s", index, ns), func() { + WaitForPods(p.Ctx, p.Client, ns, p.podsList[index]) + }) + } + }) + + for k, ns := range *p.NSIncluded { + By("Verify PV backed up according to annotation", func() { + for i := 0; i <= POD_COUNT-1; i++ { + for j := 0; j <= VOLUME_COUNT_PER_POD-1; j++ { + // Same with volumes cherry pick policy to verify backup result + if j%2 == 0 { + if p.annotation == OPT_IN_ANN { + By(fmt.Sprintf("File should exists in PV %s of pod %s under namespace %s\n", p.volumesList[i][j], p.podsList[k][i], ns), func() { + Expect(fileExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed(), "File not exist as expect") + }) + } else { + By(fmt.Sprintf("File should not exist in PV %s of pod %s under namespace %s\n", p.volumesList[i][j], p.podsList[k][i], ns), func() { + Expect(fileNotExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed(), "File exists, not as expect") + }) + } + } else { + if p.annotation == OPT_OUT_ANN { + By(fmt.Sprintf("File should exists in PV %s of pod %s under namespace %s\n", p.volumesList[i][j], p.podsList[k][i], ns), func() { + Expect(fileExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed(), "File not exist as expect") + }) + } else { + By(fmt.Sprintf("File should not exist in PV %s of pod %s under namespace %s\n", p.volumesList[i][j], p.podsList[k][i], ns), func() { + Expect(fileNotExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed(), "File exists, not as expect") + }) + } + } + } + } + }) + } + + return nil +} +func fileContent(namespace, podName, volume string) string { + return fmt.Sprintf("ns-%s pod-%s volume-%s", namespace, podName, volume) +} + +func fileExist(ctx context.Context, namespace, podName, volume string) error { + c, err := ReadFileFromPodVolume(ctx, namespace, podName, volume, FILE_NAME) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Fail to read file %s from volume %s of pod %s in %s ", + FILE_NAME, volume, podName, namespace)) + } + c = strings.Replace(c, "\n", "", -1) + origin_content := strings.Replace(fileContent(namespace, podName, volume), "\n", "", -1) + if c == origin_content { + return nil + } else { + return errors.New(fmt.Sprintf("UNEXPECTED: File %s does not exist in volume %s of pod %s in namespace %s.", + FILE_NAME, volume, podName, namespace)) + } +} +func fileNotExist(ctx context.Context, namespace, podName, volume string) error { + _, err := ReadFileFromPodVolume(ctx, namespace, podName, volume, FILE_NAME) + if err != nil { + return nil + } else { + return errors.New(fmt.Sprintf("UNEXPECTED: File %s exist in volume %s of pod %s in namespace %s.", + FILE_NAME, volume, podName, namespace)) + } +} + +func installStorageClass(ctx context.Context, yaml string) error { + fmt.Printf("Install storage class with %s.\n", yaml) + err := KubectlApplyFile(ctx, yaml) + return err +} diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index dc8597512a..ee9218a0c1 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -164,10 +164,12 @@ func (t *TestCase) Destroy() error { } func (t *TestCase) Restore() error { - if err := VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.RestoreArgs); err != nil { - RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "") - return errors.Wrapf(err, "Failed to restore resources") - } + By("Start to restore ......", func() { + Expect(VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.RestoreArgs)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "") + return "Fail to restore workload" + }) + }) return nil } diff --git a/test/e2e/testdata/storage-class/aws.yaml b/test/e2e/testdata/storage-class/aws.yaml new file mode 100644 index 0000000000..29e79c8eac --- /dev/null +++ b/test/e2e/testdata/storage-class/aws.yaml @@ -0,0 +1,9 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: e2e-storage-class +provisioner: kubernetes.io/aws-ebs +parameters: + type: gp2 +reclaimPolicy: Delete +volumeBindingMode: WaitForFirstConsumer diff --git a/test/e2e/testdata/storage-class/azure.yaml b/test/e2e/testdata/storage-class/azure.yaml new file mode 100644 index 0000000000..aa9451bf67 --- /dev/null +++ b/test/e2e/testdata/storage-class/azure.yaml @@ -0,0 +1,11 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: e2e-storage-class +provisioner: kubernetes.io/azure-disk +parameters: + cachingmode: ReadOnly + kind: Managed + storageaccounttype: StandardSSD_LRS +reclaimPolicy: Delete +volumeBindingMode: WaitForFirstConsumer diff --git a/test/e2e/testdata/storage-class/gcp.yaml b/test/e2e/testdata/storage-class/gcp.yaml new file mode 100644 index 0000000000..30ee8fc1fa --- /dev/null +++ b/test/e2e/testdata/storage-class/gcp.yaml @@ -0,0 +1,13 @@ +allowVolumeExpansion: true +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + labels: + addonmanager.kubernetes.io/mode: EnsureExists + name: e2e-storage-class +parameters: + type: pd-standard +provisioner: kubernetes.io/gce-pd +reclaimPolicy: Delete +volumeBindingMode: volumeBindingMode: WaitForFirstConsumer + diff --git a/test/e2e/testdata/storage-class/vsphere.yaml b/test/e2e/testdata/storage-class/vsphere.yaml new file mode 100644 index 0000000000..3d06ffdf0b --- /dev/null +++ b/test/e2e/testdata/storage-class/vsphere.yaml @@ -0,0 +1,11 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: e2e-storage-class + annotations: + storageclass.kubernetes.io/is-default-class: "false" +parameters: + StoragePolicyName: "vSAN Default Storage Policy" +provisioner: csi.vsphere.vmware.com +reclaimPolicy: Delete +volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/test/e2e/util/common/common.go b/test/e2e/util/common/common.go index 17e942cab5..adc561d76b 100644 --- a/test/e2e/util/common/common.go +++ b/test/e2e/util/common/common.go @@ -6,6 +6,10 @@ import ( "context" "fmt" "os/exec" + + "github.com/pkg/errors" + + veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) type OsCommandLine struct { @@ -52,3 +56,15 @@ func GetListBy2Pipes(ctx context.Context, cmdline1, cmdline2, cmdline3 OsCommand return ret, nil } + +func KubectlApplyFile(ctx context.Context, yaml string) error { + fmt.Printf("Kube apply file %s.\n", yaml) + cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", yaml) + + _, stderr, err := veleroexec.RunCommand(cmd) + if err != nil { + return errors.Wrap(err, stderr) + } + + return nil +} diff --git a/test/e2e/util/k8s/common.go b/test/e2e/util/k8s/common.go index 394a8d506f..2416295cd4 100644 --- a/test/e2e/util/k8s/common.go +++ b/test/e2e/util/k8s/common.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" "golang.org/x/net/context" + corev1 "k8s.io/api/core/v1" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" @@ -217,3 +218,67 @@ func GetAPIVersions(client *TestClient, name string) ([]string, error) { } return nil, errors.New("Server API groups is empty") } + +func GetPVByPodName(client TestClient, namespace, podName string) (string, error) { + pvcList, err := GetPvcByPodName(context.Background(), namespace, podName) + if err != nil { + return "", err + } + if len(pvcList) != 1 { + return "", errors.New(fmt.Sprintf("Only 1 PVC of pod %s should be found under namespace %s", podName, namespace)) + } + pvList, err := GetPvByPvc(context.Background(), namespace, pvcList[0]) + if err != nil { + return "", err + } + if len(pvList) != 1 { + return "", errors.New(fmt.Sprintf("Only 1 PV of PVC %s pod %s should be found under namespace %s", pvcList[0], podName, namespace)) + } + pv_value, err := GetPersistentVolume(context.Background(), client, "", pvList[0]) + fmt.Println(pv_value.Annotations["pv.kubernetes.io/provisioned-by"]) + if err != nil { + return "", err + } + return pv_value.Name, nil +} +func CreatePodWithPVC(client TestClient, ns, podName, sc string, volumeNameList []string) (*corev1.Pod, error) { + volumes := []corev1.Volume{} + for _, volume := range volumeNameList { + pvc, err := CreatePVC(client, ns, fmt.Sprintf("pvc-%s", volume), sc) + if err != nil { + return nil, err + } + volumes = append(volumes, corev1.Volume{ + Name: volume, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvc.Name, + ReadOnly: false, + }, + }, + }) + } + pod, err := CreatePod(client, ns, podName, volumes) + if err != nil { + return nil, err + } + return pod, nil +} + +func CreateFileToPod(ctx context.Context, namespace, podName, volume, filename, content string) error { + arg := []string{"exec", "-n", namespace, "-c", podName, podName, + "--", "/bin/sh", "-c", fmt.Sprintf("echo ns-%s pod-%s volume-%s > /%s/%s", namespace, podName, volume, volume, filename)} + cmd := exec.CommandContext(ctx, "kubectl", arg...) + fmt.Printf("Kubectl exec cmd =%v\n", cmd) + return cmd.Run() +} +func ReadFileFromPodVolume(ctx context.Context, namespace, podName, volume, filename string) (string, error) { + arg := []string{"exec", "-n", namespace, "-c", podName, podName, + "--", "cat", fmt.Sprintf("/%s/%s", volume, filename)} + cmd := exec.CommandContext(ctx, "kubectl", arg...) + fmt.Printf("Kubectl exec cmd =%v\n", cmd) + stdout, stderr, err := veleroexec.RunCommand(cmd) + fmt.Print(stdout) + fmt.Print(stderr) + return stdout, err +} diff --git a/test/e2e/util/k8s/namespace.go b/test/e2e/util/k8s/namespace.go index cbdc644eb5..767aba7ba5 100644 --- a/test/e2e/util/k8s/namespace.go +++ b/test/e2e/util/k8s/namespace.go @@ -100,7 +100,7 @@ func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseNam if err != nil { return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name) } - fmt.Printf("Delete namespace %s", checkNamespace.Name) + fmt.Printf("Delete namespace %s\n", checkNamespace.Name) } } return nil diff --git a/test/e2e/util/k8s/persistentvolumes.go b/test/e2e/util/k8s/persistentvolumes.go index 2f414cda44..cc7962cbe8 100644 --- a/test/e2e/util/k8s/persistentvolumes.go +++ b/test/e2e/util/k8s/persistentvolumes.go @@ -18,11 +18,49 @@ package k8s import ( "context" + "fmt" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func CreatePersistentVolume(client TestClient, name string) (*corev1.PersistentVolume, error) { + + p := &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: corev1.PersistentVolumeSpec{ + StorageClassName: "manual", + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Capacity: corev1.ResourceList{corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("2Gi")}, + + PersistentVolumeSource: corev1.PersistentVolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/demo", + }, + }, + }, + } + + return client.ClientGo.CoreV1().PersistentVolumes().Create(context.TODO(), p, metav1.CreateOptions{}) +} + func GetPersistentVolume(ctx context.Context, client TestClient, namespace string, persistentVolume string) (*corev1.PersistentVolume, error) { return client.ClientGo.CoreV1().PersistentVolumes().Get(ctx, persistentVolume, metav1.GetOptions{}) } + +func AddAnnotationToPersistentVolume(ctx context.Context, client TestClient, namespace string, persistentVolume, key string) (*corev1.PersistentVolume, error) { + newPV, err := GetPersistentVolume(ctx, client, "", persistentVolume) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("Fail to ge PV %s", persistentVolume)) + } + ann := newPV.ObjectMeta.Annotations + ann[key] = persistentVolume + newPV.Annotations = ann + + return client.ClientGo.CoreV1().PersistentVolumes().Update(ctx, newPV, metav1.UpdateOptions{}) +} diff --git a/test/e2e/util/k8s/pod.go b/test/e2e/util/k8s/pod.go new file mode 100644 index 0000000000..4caf299a33 --- /dev/null +++ b/test/e2e/util/k8s/pod.go @@ -0,0 +1,79 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8s + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func CreatePod(client TestClient, ns, name string, volumes []corev1.Volume) (*corev1.Pod, error) { + vmList := []corev1.VolumeMount{} + for _, v := range volumes { + vmList = append(vmList, corev1.VolumeMount{ + Name: v.Name, + MountPath: "/" + v.Name, + }) + } + p := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: name, + Image: "gcr.io/velero-gcp/busybox", + Command: []string{"sleep", "3600"}, + VolumeMounts: vmList, + }, + }, + Volumes: volumes, + }, + } + + return client.ClientGo.CoreV1().Pods(ns).Create(context.TODO(), p, metav1.CreateOptions{}) +} + +func GetPod(ctx context.Context, client TestClient, namespace string, pod string) (*corev1.Pod, error) { + return client.ClientGo.CoreV1().Pods(namespace).Get(ctx, pod, metav1.GetOptions{}) +} + +func AddAnnotationToPod(ctx context.Context, client TestClient, namespace, podName string, ann map[string]string) (*corev1.Pod, error) { + + newPod, err := GetPod(ctx, client, namespace, podName) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("Fail to ge pod %s in namespace %s", podName, namespace)) + } + newAnn := newPod.ObjectMeta.Annotations + if newAnn == nil { + newAnn = make(map[string]string) + } + for k, v := range ann { + fmt.Println(k, v) + newAnn[k] = v + } + newPod.Annotations = newAnn + fmt.Println(newPod.Annotations) + + return client.ClientGo.CoreV1().Pods(namespace).Update(ctx, newPod, metav1.UpdateOptions{}) +} diff --git a/test/e2e/util/k8s/pvc.go b/test/e2e/util/k8s/pvc.go new file mode 100644 index 0000000000..f6c30a0d67 --- /dev/null +++ b/test/e2e/util/k8s/pvc.go @@ -0,0 +1,51 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func CreatePVC(client TestClient, ns, name, sc string) (*corev1.PersistentVolumeClaim, error) { + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + StorageClassName: &sc, + }, + } + + return client.ClientGo.CoreV1().PersistentVolumeClaims(ns).Create(context.TODO(), pvc, metav1.CreateOptions{}) +} + +func GetPVC(ctx context.Context, client TestClient, namespace string, persistentVolume string) (*corev1.PersistentVolume, error) { + return client.ClientGo.CoreV1().PersistentVolumes().Get(ctx, persistentVolume, metav1.GetOptions{}) +} From 09240a269bef4b0f1ff84ed83c6da72bdded303b Mon Sep 17 00:00:00 2001 From: danfengl Date: Tue, 13 Sep 2022 09:12:37 +0000 Subject: [PATCH 317/366] Add Kopia support for Velero installation in E2E test Signed-off-by: danfengl --- test/e2e/Makefile | 5 +++- test/e2e/backups/deletion.go | 33 +++++++++++++-------- test/e2e/backups/sync_backups.go | 14 +++++---- test/e2e/basic/enable_api_group_versions.go | 20 +++++++------ test/e2e/bsl-mgmt/deletion.go | 10 +++++-- test/e2e/e2e_suite_test.go | 1 + test/e2e/migration/migration.go | 19 ++++++------ test/e2e/types.go | 2 ++ test/e2e/upgrade/upgrade.go | 11 ++++--- test/e2e/util/velero/install.go | 5 ++++ test/e2e/util/velero/velero_utils.go | 7 ++++- 11 files changed, 81 insertions(+), 46 deletions(-) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 53d52364e7..8f703cc003 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -95,6 +95,8 @@ DEBUG_E2E_TEST ?= false DEFAULT_CLUSTER ?= STANDBY_CLUSTER ?= +UPLOADER_TYPE ?= + .PHONY:ginkgo ginkgo: # Make sure ginkgo is in $GOPATH/bin @@ -137,7 +139,8 @@ run: ginkgo -kibishii-directory=$(KIBISHII_DIRECTORY) \ -debug-e2e-test=$(DEBUG_E2E_TEST) \ -default-cluster=$(DEFAULT_CLUSTER) \ - -standby-cluster=$(STANDBY_CLUSTER) + -standby-cluster=$(STANDBY_CLUSTER) \ + -uploader-type=$(UPLOADER_TYPE) build: ginkgo mkdir -p $(OUTPUT_DIR) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index 59944d6aa6..274df9d200 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -122,12 +122,14 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa BackupCfg.BackupLocation = backupLocation BackupCfg.UseVolumeSnapshots = useVolumeSnapshots BackupCfg.Selector = "" - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { - // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command - // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case - VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) - return errors.Wrapf(err, "Failed to backup kibishii namespace %s", deletionTest) - } + + By(fmt.Sprintf("Back up workload with name %s", BackupCfg.BackupName), func() { + Expect(VeleroBackupNamespace(oneHourTimeout, veleroCLI, + veleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), veleroCLI, veleroNamespace, BackupCfg.BackupName, "") + return "Fail to backup workload" + }) + }) if providerName == "vsphere" && useVolumeSnapshots { // Wait for uploads started by the Velero Plug-in for vSphere to complete @@ -176,22 +178,27 @@ func runBackupDeletionTests(client TestClient, veleroCfg VerleroConfig, backupNa return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } - backupName = "backup-1-" + UUIDgen.String() - if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { - // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command - // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case - VeleroBackupLogs(context.Background(), VeleroCfg.UpgradeFromVeleroCLI, veleroNamespace, backupName) - return errors.Wrapf(err, "Failed to backup kibishii namespace %s", deletionTest) - } + BackupCfg.BackupName = backupName + + By(fmt.Sprintf("Back up workload with name %s", BackupCfg.BackupName), func() { + Expect(VeleroBackupNamespace(oneHourTimeout, veleroCLI, + veleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), veleroCLI, veleroNamespace, BackupCfg.BackupName, "") + return "Fail to backup workload" + }) + }) + err = DeleteObjectsInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix) if err != nil { return err } + err = ObjectsShouldNotBeInBucket(VeleroCfg.CloudProvider, VeleroCfg.CloudCredentialsFile, VeleroCfg.BSLBucket, bslPrefix, bslConfig, backupName, BackupObjectsPrefix, 1) if err != nil { return err } + err = DeleteBackupResource(context.Background(), veleroCLI, backupName) if err != nil { return errors.Wrapf(err, "|| UNEXPECTED || - Failed to delete backup %q", backupName) diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index fc2c273373..deb9befa64 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -93,10 +93,11 @@ func BackupsSyncTest() { BackupCfg.UseVolumeSnapshots = false BackupCfg.Selector = "" By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg); err != nil { + Expect(VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") - } - Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) + return "Fail to backup workload" + }) }) By("Uninstall velero", func() { @@ -132,10 +133,11 @@ func BackupsSyncTest() { BackupCfg.UseVolumeSnapshots = false BackupCfg.Selector = "" By(fmt.Sprintf("Backup the workload in %s namespace", test.testNS), func() { - if err = VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg); err != nil { + Expect(VeleroBackupNamespace(test.ctx, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, test.backupName, "") - } - Expect(err).To(Succeed(), fmt.Sprintf("Failed to backup %s namespace", test.testNS)) + return "Fail to backup workload" + }) }) By(fmt.Sprintf("Delete %s backup files in object store", test.backupName), func() { diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index 5c4d11c348..5441e075fc 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -125,10 +125,10 @@ func APIExtensionsVersionsTest() { BackupCfg.IncludeClusterResources = true BackupCfg.Selector = label Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { - VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, backupName) - return "Get backup logs" + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backupName, "") + return "Fail to backup workload" }) }) @@ -367,11 +367,13 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso BackupCfg.BackupLocation = "" BackupCfg.UseVolumeSnapshots = false BackupCfg.Selector = "" - err = VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg) - if err != nil { - RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, backup, "") - return errors.Wrapf(err, "back up %s namespaces on source cluster", namespacesStr) - } + + Expect(VeleroBackupNamespace(ctx, VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, + VeleroCfg.VeleroNamespace, backup, "") + return "Fail to backup workload" + }) if err := deleteCRD(ctx, tc.srcCrdYaml); err != nil { return errors.Wrapf(err, "delete music-system CRD from source cluster") diff --git a/test/e2e/bsl-mgmt/deletion.go b/test/e2e/bsl-mgmt/deletion.go index d7504342a6..20b591d73f 100644 --- a/test/e2e/bsl-mgmt/deletion.go +++ b/test/e2e/bsl-mgmt/deletion.go @@ -189,7 +189,10 @@ func BslDeletionTest(useVolumeSnapshots bool) { // TODO currently, the upgrade case covers the upgrade path from 1.6 to main and the velero v1.6 doesn't support "debug" command // TODO move to "runDebug" after we bump up to 1.7 in the upgrade case Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed()) + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg.BackupName, "") + return "Fail to backup workload" + }) }) BackupCfg.BackupName = backupName_2 @@ -197,7 +200,10 @@ func BslDeletionTest(useVolumeSnapshots bool) { BackupCfg.Selector = label_2 By(fmt.Sprintf("Back up the other one PV of sample workload with label-2 into the additional BSL %s", backupLocation_2), func() { Expect(VeleroBackupNamespace(oneHourTimeout, VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed()) + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg.BackupName, "") + return "Fail to backup workload" + }) }) if useVolumeSnapshots { diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 0f06f6a193..fc396b84e9 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -77,6 +77,7 @@ func init() { flag.StringVar(&VeleroCfg.GCFrequency, "garbage-collection-frequency", "", "Frequency of garbage collection.") flag.StringVar(&VeleroCfg.DefaultCluster, "default-cluster", "", "Default cluster context for migration test.") flag.StringVar(&VeleroCfg.StandbyCluster, "standby-cluster", "", "Standby cluster context for migration test.") + flag.StringVar(&VeleroCfg.UploaderType, "uploader-type", "", "Identify persistent volume backup uploader.") } var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 90f96f578b..6894aff58c 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -126,6 +126,8 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) OriginVeleroCfg.VeleroImage = "" OriginVeleroCfg.ResticHelperImage = "" OriginVeleroCfg.Plugins = "" + //TODO: Remove this once origin Velero version is 1.10 and upper + OriginVeleroCfg.UploaderType = "" } fmt.Println(OriginVeleroCfg) Expect(VeleroInstall(context.Background(), &OriginVeleroCfg, useVolumeSnapshots)).To(Succeed()) @@ -156,11 +158,11 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupStorageClassCfg.BackupName = backupScName BackupStorageClassCfg.IncludeResources = "StorageClass" BackupStorageClassCfg.IncludeClusterResources = true + Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupStorageClassCfg)).ShouldNot(HaveOccurred(), func() string { - err = VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, backupName) - return "Get backup logs" + VeleroCfg.VeleroNamespace, BackupStorageClassCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupStorageClassCfg.BackupName, "") + return "Fail to backup workload" }) var BackupCfg BackupConfig @@ -169,14 +171,11 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupCfg.UseVolumeSnapshots = useVolumeSnapshots BackupCfg.BackupLocation = "" BackupCfg.Selector = "" - //BackupCfg.ExcludeResources = "tierentitlementbindings,tierentitlements,tiers,capabilities,customresourcedefinitions" Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { - err = VeleroBackupLogs(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, backupName) - return "Get backup logs" + VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg.BackupName, "") + return "Fail to backup workload" }) - }) if useVolumeSnapshots { diff --git a/test/e2e/types.go b/test/e2e/types.go index bb54562e19..068d957ebb 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -63,6 +63,7 @@ type VerleroConfig struct { ClientToInstallVelero *TestClient DefaultClient *TestClient StandbyClient *TestClient + UploaderType string } type SnapshotCheckPoint struct { @@ -86,6 +87,7 @@ type BackupConfig struct { ExcludeResources string IncludeClusterResources bool OrderedResources string + UseRestic bool } type VeleroCLI2Version struct { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index c862730381..a46231b464 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -112,6 +112,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC tmpCfgForOldVeleroInstall.VeleroImage = "" tmpCfgForOldVeleroInstall.ResticHelperImage = "" tmpCfgForOldVeleroInstall.Plugins = "" + tmpCfgForOldVeleroInstall.UploaderType = "" Expect(VeleroInstall(context.Background(), &tmpCfgForOldVeleroInstall, useVolumeSnapshots)).To(Succeed()) @@ -143,11 +144,13 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC BackupCfg.BackupLocation = "" BackupCfg.UseVolumeSnapshots = useVolumeSnapshots BackupCfg.Selector = "" + //TODO: pay attention to this param + BackupCfg.UseRestic = true Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg.UpgradeFromVeleroCLI, - tmpCfg.VeleroNamespace, BackupCfg)).ShouldNot(HaveOccurred(), func() string { - err = VeleroBackupLogs(context.Background(), tmpCfg.UpgradeFromVeleroCLI, - tmpCfg.VeleroNamespace, backupName) - return "Get backup logs" + tmpCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), tmpCfg.UpgradeFromVeleroCLI, tmpCfg.VeleroNamespace, + BackupCfg.BackupName, "") + return "Fail to backup workload" }) }) diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 0b3e5962ba..b40ec17216 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -90,6 +90,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps veleroInstallOptions.UseRestic = !useVolumeSnapshots veleroInstallOptions.Image = veleroCfg.VeleroImage veleroInstallOptions.Namespace = veleroCfg.VeleroNamespace + veleroInstallOptions.UploaderType = veleroCfg.UploaderType GCFrequency, _ := time.ParseDuration(veleroCfg.GCFrequency) veleroInstallOptions.GarbageCollectionFrequency = GCFrequency @@ -213,6 +214,10 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption args = append(args, fmt.Sprintf("--garbage-collection-frequency=%v", options.GarbageCollectionFrequency)) } + if len(options.UploaderType) > 0 { + args = append(args, fmt.Sprintf("--uploader-type=%v", options.UploaderType)) + } + if err := createVelereResources(ctx, cli, namespace, args, options.RegistryCredentialFile, options.ResticHelperImage); err != nil { return err } diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index ef50610a57..c67598ad19 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -323,7 +323,12 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes") } else { - args = append(args, "--default-volumes-to-fs-backup") + if backupCfg.UseRestic { + args = append(args, "--default-volumes-to-restic") + } else { + args = append(args, "--default-volumes-to-fs-backup") + } + // To workaround https://github.com/vmware-tanzu/velero-plugin-for-vsphere/issues/347 for vsphere plugin v1.1.1 // if the "--snapshot-volumes=false" isn't specified explicitly, the vSphere plugin will always take snapshots // for the volumes even though the "--default-volumes-to-fs-backup" is specified From 9693aca1f3991952decba04c9c17dd3879fda796 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 26 Sep 2022 17:05:59 +0800 Subject: [PATCH 318/366] Fix Test_prepareBackupRequest_BackupStorageLocation UT failure. Signed-off-by: Xun Jiang --- changelogs/unreleased/5394-blackpiglet | 1 + pkg/controller/backup_controller_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5394-blackpiglet diff --git a/changelogs/unreleased/5394-blackpiglet b/changelogs/unreleased/5394-blackpiglet new file mode 100644 index 0000000000..20d2cbcede --- /dev/null +++ b/changelogs/unreleased/5394-blackpiglet @@ -0,0 +1 @@ +Fix Test_prepareBackupRequest_BackupStorageLocation UT failure. \ No newline at end of file diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index fc6370f327..4617975681 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -382,7 +382,7 @@ func Test_prepareBackupRequest_BackupStorageLocation(t *testing.T) { test.backup.Spec.StorageLocation = test.backupLocationNameInBackup // Run - res := c.prepareBackupRequest(test.backup) + res := c.prepareBackupRequest(test.backup, logger) // Assert if test.expectedSuccess { From eacc10347b58128ae21d2fb6ae05e75af056479c Mon Sep 17 00:00:00 2001 From: qiuming Date: Thu, 29 Sep 2022 11:54:51 +0800 Subject: [PATCH 319/366] Fix restore error with flag namespace-mappings (#5377) Signed-off-by: Ming --- changelogs/unreleased/5377-qiuming-best | 1 + config/crd/v1/bases/velero.io_podvolumerestores.yaml | 5 +++++ config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/pod_volume_restore_type.go | 3 +++ pkg/controller/pod_volume_restore_controller.go | 4 +++- pkg/podvolume/restorer.go | 5 +++-- pkg/uploader/provider/provider.go | 3 ++- 7 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/5377-qiuming-best diff --git a/changelogs/unreleased/5377-qiuming-best b/changelogs/unreleased/5377-qiuming-best new file mode 100644 index 0000000000..bd3cf12368 --- /dev/null +++ b/changelogs/unreleased/5377-qiuming-best @@ -0,0 +1 @@ +Fix restore error with flag namespace-mappings diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index 036f58a06d..0a2acd64ba 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -116,6 +116,10 @@ spec: snapshotID: description: SnapshotID is the ID of the volume snapshot to be restored. type: string + sourceNamespace: + description: SourceNamespace is the original namespace for namaspace + mapping. + type: string uploaderType: description: UploaderType is the type of the uploader to handle the data transfer. @@ -133,6 +137,7 @@ spec: - pod - repoIdentifier - snapshotID + - sourceNamespace - volume type: object status: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 44ff941276..ad661abd0b 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -35,7 +35,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83\xe6\f\x963\x00\x02`H3[\xfb\xdfS\x8d\a9/\x92\xa2*Z\xcf\xc5\x16\xd0h|\xf8\xd0/4'\xd3\xe9t´xBc\x85\x92s`Z\xe0W\x87\x92\xfe\xb2\xd9\xfa\xef6\x13j\xb6\xf9q\xb2\x16\x92\xcfᦱN\xd5\x1fѪ\xc6\xe4x\x8b+!\x85\x13JNjt\x8c3\xc7\xe6\x13\x00&\xa5r\x8c\x86-\xfd\t\x90+錪*4\xd3\x02e\xb6n\x96\xb8lD\xc5\xd1x\xe5i\xeb\xcd\x0f\xd9߲\x1f&\x00\xb9A\xbf\xfcQ\xd4h\x1d\xab\xf5\x1cdSU\x13\x00\xc9j\x9c\x83V|\xa3\xaa\xa6F\x83\xd6)\x836\xdb`\x85FeBM\xacƜv-\x8cj\xf4\x1c\x0e\x13aqD\x14N\xf3\xa0\xf8\x93\xd7\xf31\xe8\xf1S\x95\xb0\xeeߣ\xd3?\v뼈\xae\x1aê\x11\x1c~\xd6\nY4\x153\xc3\xf9\t\x80͕\xc69\xdc\x13\x14\xcdr\xe4\x13\x80H\x80\x876\x05ƹ\xa7\x94U\x0fFH\x87\xe6\x86T$*\xa7\xc0\xd1\xe6Fh\xe7)\xdb\xeb\x01\xb5\x02W\"m\xe9\xe9fB\nY\xf8\xa1\x00\x01\x9c\x82%BD½2\x80_\xad\x92\x0f̕sȈ\xb8L+\x9eɤ3\xca\x04\xce\xef{\xa3nG\xe7\xb0\xce\bY\x1cC\xf6\u007f\x06\xd5\xc1\xf3\xa0\xf83\x91<\x96\xe8e\x12\x9aFW\x8aq4\xb4y\xc9$\xaf\x10\xc8r\xc1\x19&\xed\n\xcd\x11\x14i\xd9\xe3Nw\x91|J\xfaZ3\x97\xb0s\t\x15A\xb6\xb3\xfdS{\xe8ܾ\x0f\x8a\xc7\x05\x10\x8d\x1a\xacc\xae\xb1`\x9b\xbc\x04f\xe1\x1e\xb7\xb3;\xf9`Ta\xd0\xda\x11\x18^<\xd3%\xb3]\x1c\v?\xf1\xba8V\xca\xd4\xcc\xcdAH\xf7\u05ff\x1c\xc7\x16\x17eN9V\xbd\xdf9\xb4\x1d\xa4\x8f\xfdဖ\x9c\xad\x88\xd7\xffM\xe0.\tҭ\x92]^\xdf\xf7F\xc7\xc0\xb6\x94\xa6@\x9c\r\x82hG뻢\xab\x8f3\x17\x06\xc2\xf4\xe6\xc7\x10\xca\xf2\x12k6\x8f\x92J\xa3|\xf7p\xf7\xf4\xe7Eg\x18@\x1b\xa5\xd18\x91\xa2k\xf8ZY\xa55\n]f\xafIa\x90\x02N\xe9\x04mp\x8a0\x86\xa3f5\xff\xce\xc4\xfck\xaf;X\aN\x17>\x9f\xebN\xdc\x00%;\x10\x16X\\\x1aNq :\x85\xec\x8f\xffX\xdc\xcf\xfe9\xc6\xfc\xfe\x14\xc0\xf2\x1c\xad\xf5\xf9\x1ak\x94\xee\xcd>gs\xb4\xc2 \xa7\xc2\x05\xb3\x9aI\xb1B벸\a\x1a\xfb\xf9\xed\x97q\xf6\x00~R\x06\xf0+\xabu\x85o@\x04\xc6\xf7\xe1/ٌ\xb0\x81\x8e\xbdF\xd8\nW\x8a~\xd2\xda3@\xd6\x15\x8f\xbd\xf5\xc7ul\x8d\xa0\xe2q\x1b\x84J\xacq\x0eW\xbe\x12<\xc0\xfc\x8d\x1c\xeb\xf7\xab#Z\xff\x14\x1c芄\xae\x02\xb8}\xbek{\xe4\x01\xa4+\x99\x03gDQ\xe0\xa1\x10\xed\u007f>xSH\xfc\x1e\x94!\x06\xa4j\xa9\xf0\x8a\xe9\xf6B\x00\xfd\xf9헣\x88\xbb|\x81\x90\x1c\xbf\xc2[\x102p\xa3\x15\xff>\x83Go\x1d;\xe9\xd8W\xda)/\x95\xc5c\xcc*Y\xedB\xb5\xbfA\xb0\xaaF\xd8bUMC\xbd\xc1a\xcbv\xc4B\xba8\xb27\x06\x9a\x19w\xd2ZS\x95\xf1\xf8\xe1\xf6\xc3< #\x83*|\xbc\xa3\xec\xb4\x12T5P\xb9\x10r\x9e\xb7\xc6A\xd2L\x9fm\x82\xf98\x05y\xc9d\x81\xe1\xbc\b\xab\x86\xb2Pv\xfd\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe83\x0f\xe7+\xd5g\x1c\xae\xfd\xd6:y\xb8u\xb3D#ѡ?\x1fW\xb9\xa5\xa3娝\x9d\xa9\r\x9a\x8d\xc0\xedl\xab\xccZ\xc8bJ\xa69\r6`g\xfe\xc9<\xfb\xce\xff\xf3\xe2\xb3\xf8\xd7\xf5s\x0f\xd4y\xf4\xbf\xe6\xa9h\x1f;{ѡR\xad\xf8\xfc2q5\xacTN0\x11\f\xe0\f\a\xb1\x994\xf22\x8a\xf6\x13*E?B\xaf\x11oE\xe3!\xf6R\xbb\xa2\x874\x95\xbd]\x84\xd3\xf1\xf7^OF+>\xe9\x93\xd6v\xc9\xde\xe4\xc1\xa1\xfa\x13][\xed\xcdv\x9a\x9c\xed\xd3\f\x9fʾ\x83v\xc9c9t\xed\"\xef!f\xbb\xd4ˣ\aˋ\x9f˹\xa2\xc7@\xf7W\x8b\xd36p3\\\xe1{S\x86G\x9f\x105\xfa7hh8n\x99M\x9b\x8c\xdd7\xb4\xf4\x85\xa5>O\x92:\xe4\xbeT\xa7\x97Ċ\x89\n9\xec\u007f7\xf1\xcdq\xeb\x9b4\xd7c\x95iR\xd4X\xe4>n\x8c\x80\x1e\xaeK}O\xce\x1cNI\xc5@B6UŖ\x15\xce\xc1\x99f8}½j\xb4\x96\x15\xe7\xfc\xeb\x97 \x15^\xf1q\t\xb0\xa5j\xdc\xfe\x19\x1f\x1d-Rqm\xa3\x15\\\xd6J(\x99=\a\xe5\x81d\xc6,n\xef\xf2\xa7M\x0eN\x84\xb2{\u070e\x8c\x0e\xfa\xd0\xedɛdB#s?y븈\x80\xb8\xd19\x0e\xa2\x18\x94\xaaJ֭\x1c%\xa5\xa6^\xa2!\"|\xf3;1\x92\x02\xc7X_Ŀ\xa7\x0eL\x1e4\xa4X\x18T\xc5\x17bΤo\x13\x92\xfd:\x05\\X]\xb1݈\xdet\x12_2\x91\xf9\x92\x1f\x1d,&y!\xb9\xbf\x9f\xbb\xb4\x9f\xb3o\xee\x8f\x17tc?\x15\x8c\xddB\xbb\xefߛ\xdf\xff\xaa\xf1:;\x9c(\xe2\xacc\xc6=7\xec-:\xc2\xe7\"\x9eW=\x1e\xefڡk\x18\xa8\xba\xdb\xfc\x911j\x94\xa8\xc1\xa0G\xce[\xbac/\xb4=\xd2,\xf7\x9d\xfe9\xfc\xf6\xfb\xe4\x90\xeeXNU;\xf2\xfb\xfeOڱVI\xbfP\xfb?s%\xc3O\xcav\x0e\x9f\xbfL 6M\x9f\xd2\xcf\xce4\xf8\xbf\x00\x00\x00\xff\xffe\xe5\xd5&\b \x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xa0\xb1R\xab+\x10\x85\xc4\xef\x0e\x15\xfde\xd7O\xffj\xd7R_\xeeߟ=I\x95^\xc1ui\x9dο\xa1եI\xf0#n\xa5\x92Nju\x96\xa3\x13\xa9p\xe2\xea\f@(\xa5\x9d\xa0ϖ\xfe\x04H\xb4rFg\x19\x9a\xd5#\xaa\xf5S\xb9\xc1M)\xb3\x14\r\x03\xaf\xa6\xde\xff\xb4\xfe\x97\xf5Og\x00\x89A\x1e~/s\xb4N\xe4\xc5\x15\xa82\xcb\xce\x00\x94\xc8\xf1\nl\xb2ô\xccЮ\xf7\x98\xa1\xd1k\xa9\xcfl\x81\t\xcd\xf6htY\\A\xf3\x83\x1f\x140\xf1\xab\xb8\v\xe3\xf9S&\xad\xfb\xa5\xf3\xf9\x93\xb4\x8e\u007f*\xb2҈\xac5\x1f\u007f\xb5R=\x96\x990\xcd\xf73\x00\x9b\xe8\x02\xaf\xe03MU\x88\x04\xd33\x80\xb00\x9ez\x05\"M\x99T\"\xfbj\xa4rh\xaeuV\xe6\x15\x89V\x90\xa2M\x8c,\x1c\x93\xe2\xce\tWZ\xd0[p;l\xcfC\xed7\xab\xd5W\xe1vW\xb0\xb6\xdco]섭~\xf5$\xf2\x00\xc2'w ܬ3R=\x0e\xcd\xf6\x01\xae\x8dV\x80\xdf\v\x83\x96P\x86\x949\xab\x1e\xe1y\x87\n\x9c\x06S*F\xe5\xdfD\xf2T\x16\x03\x88\x14\x98\xac{x\x06L\xba\x1f\xe7p\xb9\xdf!d\xc2:p2G\x10aBx\x16\x96q\xd8j\x03n'\xedh\x1ao`%2؋\xac\xc4\v\x10*\x85\\\x1c\xc0 \xcd\x02\xa5j\xc1\xe3.v\r\xff\xa1\r\x82T[}\x05;\xe7\n{uy\xf9(]\xa5b\x13\x9d祒\xeep\xc9\xdaRnJ\xa7\x8d\xbdLq\x8f٥\x95\x8f+a\x92\x9dt\x98\xb8\xd2\xe0\xa5(\xe4\x8aQW\xacf\xd7y\xfa\x0f\x15G\xed\xbb\x0e\xaeG\xfb\xcd7V\x84\x13\x1c \x8d\xe8\x05\xc6\x0f\xf5\xabh\bM\x9f\x88:\xdfn\xee\xee\xdb\xc2$m\x9f\xfaL\xf7\x96\x845, \x82I\xb5Ű\xa3\xb7F\xe7\f\x13UZh\xa9\x1c\xff\x91d\x12U\x9f\xfc\xb6\xdc\xe4\xd2\x11\xdf\u007f/\xd1:\xe2\xd5\x1a\xae\xd9\xee\x90\x1c\x96\x05\xed\xc0t\r\xb7\n\xaeE\x8eٵ\xb0\xf8\xea\f J\xdb\x15\x116\x8e\x05m\x93\xd9\xef\xec\xa9\xd6\xfa\xa12o#\xfc\xaa\xf6\xf8]\x81Ig\xcb\xd08\xb9\x95\to\f֞\xb5\n\xe8iP߆w-\xff\xc2j\xaa\xff\xb5\x87\x87\xd7eլh\xc9~\xb8\x1ds\xb81c$W\x1e\x1a\xe9\x14\xa5\xfb\xdc\x1d҂-J\x04(3\x98t\xb5^\xac};\x82\tAխGp<\xe2*\xff\x84yAjc\x06\xc5\xfbЍP$\xfa\xa4\xb5;U\x19\xfeJ\xcd\xea\xa0]\xe1H\xb9\xf1t;$\xbe\xede\x1a\xb4\xd7\x11Wa\x92\xb3\xd4\x12+\xef\x94(\xecN;\xb2q\xbatC\xbdz\v\xb8\xbe\xbb\xed\rjq\x9e\xb0b\x1bΌv\x1a\x9e\x85<\xe6\xb4o$\x97\xd7w\xb7\xf0@.\x11V0\xc1[rp\xa5Q\xac\x8e\xbf\xa1H\x0f\xf7\xfaW\x8b\x90\x96\xac\x95*\xbb|1\x02x\x83[\xda\xf4\x06\t\x06\r@ch\x0fXFM\x97n\xcd\x0eG\x8a[Qf.(9i\xe1\xfdO\x90KU:<\xe6;L\xf3\xde\x13\x89\xc1\xf9\xd5\xd8{\xfd\xb3\xf5\x8c\x8c \xe9Ǒ\xa1\x03[\xaa\xd0)\xec\xb9\xdf\x18Ue\x86`\x0f\xd6a\x0e\x9b\x00\xa5\xb6\xd5\xcc\x15\xd6\aY\x16\xc0X\xd8\x1c*܇\xd7M^\xb8\xd8dx\x05Δ\xc3\xd3Nm\xdd!\xda|C\xebd\x12A\x99\xf3>i\xfc\xc8\x01\xc2\x18\xfea\x84(=\n\x90\x91\x17O\xe4h\x06\n\x91\xb7\x90e-\xe2\xceS\x05\xe0\xbf\x14|$\x03\x97\x90ٹ\n\xe6Lb\xc6&TiȴzD\xe3g$W\xe1Yf\x19oi\xcc\xf5\xbe\xe3d\xb5\x1b\xd9\x16\x83\x19\x19Iؖdv\xd6@\xb2?*#RY\x87\"]\x9f\xbf\x16\xf3\xf0{\x92\x95)\xa6u\x983\xa8Kz\x8c\xbb9\x1a\xc4\x01\xa1\x90\x8a43\x85_DtU\xff:B\r\xf65\x85A2\x18 \x95\x87I\xa4!E\xb3\x19Q\xd2Ԥ\xc3|\x04\xcfٝ\xbc\x80j\xc2\x18q\x98\xa0Y\x154/!Y=&\xb8b\x99L\x90\x88U;\\L5&\xcd\xc8\xfa\xfe\x1f\x12l\xa7\xf5S\f\x91\xfe\x9d\xfa5\x8e%$|6\x01\x1b܉\xbd\xd4\xc6\xf6\xa3\x13\xfc\x8eI\xe9Fw\x9bp\x90\xca\xed\x16\r\xc1›\x8e\xbf\xa7\x885mV\xa9\x99i\xc6\x1f\xad\xaba:1\x8f\xa91\xb6\x14v_F\xa1\x02#NV\x8fuC*\xf72-E\xc6jB\xa8įO\xd4\xf8\x8d)\xb7\x19\x818\xc2\xdf+\xa3j\x15ĥ\x8eW\xaa\x15\x92ۗk3f\xb7|;\x063N\x86\x8d`gr̅k\x9a)3\xb4\x01\x15o\xfe\x1a\xbds\xd1p\xca\at\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02\xdfb\xf5\xe7\be\a4i\xd7ߚU\xa2M#\x87l'\x93\x9d7V$e\f\vR\x8d\x965\x86(\x8a\xec0\xb5h\x88\x91\x8c0ٜ\xd2hZ\x84\xfa\xe8\xc3\x1dS$M\x8b\xd4\xc1M\x9b\xd1\xc6]\xaa\xd7b\xf3F\xf4\x0e\x9aꇄ\xfd\xf6h\xf8\xcb\v;\x91[\xa2]\xc3\xed\x160/\xdc\xe1\x02\xa4\xab\xbe\xc6@%W\xb1\xc1\xe3oƸ\xd3v\xcbm\u007f\xf4\x8b\xef\x96\x17\xe1Z\x8d\xc6߄il\xac\ue0adZİO\xed\x91\x17 \xb75\xc3\xd2\v\x8a!\x1d\xb2/5\x87h\xcbљ\xe5\xdcK\x12(\xd6\xf6R˅Kv7\xf51PĈ\x1e\xad\xfa\x00\xbc_^\xc50̃\b\x90P;\x15|\x82)\r\xe6\xfed\xf4\x9e\xf7G\xf3\x85=\xc0\x0f\x9f?b:G2\x88\x97ԣE}\xe8y:m\x14x\x81Q [\x8bb7\xad\x8e\xf1\xfc\xf9\xf7\x05\bx\u0083\xf7\xac\x06\x83ˡF\xac\x155H\x83|\x18\xcfj\xe4\t\x0f\f*\x9c\xaeG\xc1[\"*\xbe=\xe1!\xb6k\x8f\xa8\x84_8\xd7\xf3ԥ\x0f\xbc\x8a\x98\xadԴ\x9a\xa8a\xef\x80\xd3q\x8b\x85eJ\xa9j\x15\xc5O\\vͰΕ\xd2\x13\x1e\xdeY\xcf>\xda5;Y,\xa0\x00)l\xb0\xc8;\xac\xbaKy\x10\x99L\xeb\xc9x\x9f,\x80x\xab.\xe0\xb3v\xf4\x9f\x9b\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xf8˫\x92\xd8/\xe2D\x02\xfb\xc1\xbc-\x957\vD\x97E\xf378\xb0\t%\x11\xad\xd9&-\xdc*\x8a\xcf<}\x96\xb0i\x87\x15r\x1e\xad\xbc\xb4|\x1b\xa3\xb4Z\xb1\x99\xaef[\x00\xb4\x8dW`\x956\x1dN],\x848\x88b@\uf7ac\x95\xff\xe5\xe8\x1ek\xaa\x19,2\x91`Z\x9dJ\xf3\xa5\x99p\xf8(\x13\xc8\xd1<\"\x14d7\xe2\x85j\x81&\xf7\xed\x04)\x8cw-\xaa\x16\xcc\xc2\xc0\x1d\xd0P[Ѯ\x8f\xecY\xb19\xaa\xfb\xc8\r\xd9t\xf7\xb8U\xb2yg\u007f(\x8a\xfa픎e\x96e!\xbf\x8e}\x10\x8f\xa4w?r\xc1\xc7\xd6\u007f\x90ye\xf1\xfe3\xce\x1a\ni\xec\x1a>pBK\x86\xed\xf1\xd5)ak\xaa(\x90\x84\x89\xb4@r\xb2\x17\x19\xb9\x0f\xa4\xbc\x15`\xe6\x9d\t\xbd=\xf2\xa0\xe2T\xcc\xf3N[o\xf3\xebc\xf5\xf3'<\x9c_\x1ci\xaf\xf3[u\x1e\a\x93t\xfe\x91Ҫ\xbd\x16\xad\xb2\x03\x9c\xf3o\xe7\xec\x98-\xd9\"'8o\v\xa4:\xba+\xa7\x8e,\t\x05(֮\xbc\x16\x1a\\'X\x90\v?\xb7\x8ah\x99.\xb4\x1d\xb9]\x1cA뫶\xce\x1f\x00v\xdc\xed\x81\x13\u0098\xe8/\x9c\x1a\x82\xd8:4`\x9d6U2\x03\xa9\xdd\xde\x019q\xde\xce\xf3\x9eX]\x9fFz\xc0\x14d\x9e7\x1a\xc2\xeb\xf4s\x9f\xe5@\xff?\x0f3ag\x89a\x17F'h\xed\xbc(EZ\x8e\x99\x03\xdb\xfa\xb0V\xf8\xe0m\x1b\xa5\x9ac\x8e\x92\xab\xb6\xcc\x15'Ҟ\x10\xd8\xdc|o\x9d;\x93\x1a\xa2\xbfcD\xf9\x14\x1c\x81\x13\x1d\xf3\\\xf4\x13k\xa2ѽ\xf6\xa3\xab\r\x18\x80\xf9\x80\xc9<\x96\xacT\x96\xf9\xcdA$\xffj\x8eG.\xd5-O\x04\xef_\xcdY\x81J\x95㩡\xccu5\xbeaH\xfd!6~\x85*=C\xf3]\x8d\xc1\x0eg\x8fo2\xe29\x05\xe4L+\xedڇ5a\xa6w\x16\xb6\xd2X\xd7 \xbc\x00\xaa\xb4|M\xfd\xba1\xa6\xba1\xe6\xe4\x10\xf3\x8b\x1f\xdd:V\xdc\xe9\xe7\x90Դ$\xb0\xae\x88\xbf\x13{\x04\xb9\x05\xe9\x00U\xa2K\xc5\a^\xa4.h\x9a\x05\x10=\x13\xbd1\x89\xb4\x99\xad\xc1\xaa\xcc\xe3\t\xb2b\xe9\x94j\xf6t\xac=\xe4g!\xe3N\xa7\xe04\xb6\xba\xa9ġ\xa1\xd6͆\n\x19D\xed\xec\xb5\\|\x97y\x99\x83ȉ-K\xe2ƭ\xcf=\xaaR\xdd<\xaf\x9f\x85t!\x83\xd8_\xac.Ӧ\x89\u038b\f\x1dVYE\x89VV\xa6X\xbb\x0f\x81\xff\x839ZcM\xc0VȬ4\vt\xf4b\xce,\x8dۂzz\xf9`,\x1e\x91\x15\x133\xf2\xd0}\x81\xd3\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-{ 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xb5}l\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3'\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe2\xa8|\xa8\xe3\\\xa7\xa9\xa5\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x87\xe1\x8a\x18U\x9b\xb7\xb5\xd9\xff\x85\x04\xfe袵\xe9\xb8\xc01Qɗ\xde\x10\xe2}\xe5\xf5\r\xb9\xd5\xe31\x9ew\xb6Op\xabG@\xden!/3'\x8b\xacU\x92\xc2\xed\xf0P?y\xffM\xf3\xd3\xcb́\xa1}\xf9V\v\xf0\x18\xc8n\x80 ,\xe8\a]?\xfc\xcc%\xfa\x1a\u007f\xd1]R\xec\x15|\xdc5\xcf\xfc\xf3\x95\xc8g+\x91\x97@1\xd8G>OY\xfe,%\x92\xce'\x06[\x93SG>?Y\x14n\x9d\x18pMB\x9czn2\x1drM\x1f\xa7\xf5\x9f\x99\x9c\xe0NDH\xd8l\x97\x1f\xbe\x11\xd0&E3{\xb9\xb2D4g\x85\xb2\x17\x13u\xe7\xefU\x1d\xa8\xaa{Q\xaf\xf6\xc5\xcd\x18wt\xfd\n>\x81_\xa4J=oH\b[\xfe\x05\x17\xc9\xe4w2\xb5\xe33.F\x8d\xb7ٻ4\xb2X\bR\xa3\x1c\x14\xf1\xe5\xb6]ÍHvu\xc7\x11\x88<\xf3NX\xd8j\x93\v\a\xe7\xf5m\xdce5\x92\xbe\x9c\xaf\x01~\xd6\xf5Eh\xab\xca\xcd\b\\+\xf3\";P\xf4\x03\xe7]@?&:\xa3\xe2gC\xc1\xbfP\xd1,\"\x02\xbe\xeb\x8e\x18*8\x19\n\xbb%\x99.\xd3z\x86\tv\vu\x80\xaf\x0f\xecMq\x11\xa8\xa4)\x96\x15|\xa5*\x12\xee\xd5\xd2\x1a\x019V7r\x11\xc9\xc6/\x87\xad\xd3F<\xe2'\xedK{\xc6Ь;\xa2S\xdd5\xe8\xaa*U$\xbc\xfd\x1a\x95d\xbf\xb6>\xc0&\x83\xec\xa8\xda a;\xa6\xc4f\xf6\xb9sY\xc4\xe2\xee\xef?\xf9\x059\x99\xe3\xfac\xe9/\xeaW\x850\x16\x89\xd2\xd5B\xfd\xa0\u0378\x9d\xdb\xe9g.\xd7\u05ee\xbf٪p\x8c\x9c\xb1\xc69\x01'\xadfߩpY\x91.F\xe4\x1f\x86G\xb6\x02\xd9\x16\x13\xa7\xae\xf6\xf5v\x14\x96\xb0V'\x92u\x11\x1f\tq\xa2\xd8땊\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9VmT{\xab\xc6\nlvH\xf8\xeb\xd1\xc0\xd1\xe2\x9aN\xb3\xfe\xebu\x1f\xb2{*\x10\xc8\xfab\xa4\xd5ٖ\xb4u\t\xdac\xd2\xcd\xec\xff\xf1\xbd?컮\x86\xab\xbe\xae\xeaB\xb4g\x11\x94\xf5\xc5Vcj\f\xfb\xaa\xac\x89(\\i\x82yMJ\xc3u\xf3\b\b\xfa\xb2r\xa7U\x19n\xaa\xb6\xcf\xf0\xb2\xa9\xe3\xdeD\xfb\xb3U\xe3\a\xf8W\xd7\t\x1e-\x9c뭫\xaf\xea\xbe\"\xf8\xa7\xb1sp\x1fp\x9d\xc1\xb9\x9a\xcaԧN\xf2\r\x84\xe6\x81U}»1ԇ\xb36W\xf0\x19\x9f\a\xbe\xde(Z\xc4\xf1\x9d\x9aO\xcdĔ\xcf\b\x86*\xacO.q_\x8f\xe2\xbc\xd8\x01m\xd1Us\xbd\uef44\x1b\xaeV[w\xf19\xb0Cl\xfdG\xb9\xf5\a8\t\xad韎z\x8c*\xaeI\xa55\xa6\xb0\x06\xb7\xd4\xd1G\x8bf\xcf\xe5a+!\t6\xbc\xfd\xa5\xdc4\xe5\"\xe1\x8f?Ϛ])\x92\x04\v\x17\x12\xbb\xda\xff\x9aŹ/\xf7Z\xfdc\x15\xfcg\xa2\x95w\xb4\xed\x15\xfc\xe7\u007f\x9fA0\xc0\x0fտHA\x1f\xff7\x00\x00\xff\xffX\x13X\x17\xfbc\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), diff --git a/pkg/apis/velero/v1/pod_volume_restore_type.go b/pkg/apis/velero/v1/pod_volume_restore_type.go index e0370da637..72c3b891c5 100644 --- a/pkg/apis/velero/v1/pod_volume_restore_type.go +++ b/pkg/apis/velero/v1/pod_volume_restore_type.go @@ -43,6 +43,9 @@ type PodVolumeRestoreSpec struct { // SnapshotID is the ID of the volume snapshot to be restored. SnapshotID string `json:"snapshotID"` + + // SourceNamespace is the original namespace for namaspace mapping. + SourceNamespace string `json:"sourceNamespace"` } // PodVolumeRestorePhase represents the lifecycle phase of a PodVolumeRestore. diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index df41b50f04..5292e0a01f 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -249,8 +249,10 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error getting backup storage location") } + // need to check backup repository in source namespace rather than in pod namespace + // such as in case of namespace mapping issue backupRepo, err := repository.GetBackupRepository(ctx, c.Client, req.Namespace, repository.BackupRepositoryKey{ - VolumeNamespace: req.Spec.Pod.Namespace, + VolumeNamespace: req.Spec.SourceNamespace, BackupLocation: req.Spec.BackupStorageLocation, RepositoryType: podvolume.GetPvrRepositoryType(req), }) diff --git a/pkg/podvolume/restorer.go b/pkg/podvolume/restorer.go index 3b66a9be62..09bb3a790f 100644 --- a/pkg/podvolume/restorer.go +++ b/pkg/podvolume/restorer.go @@ -152,7 +152,7 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { } } - volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, backupInfo.snapshotID, repo.Spec.ResticIdentifier, backupInfo.uploaderType, pvc) + volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, backupInfo.snapshotID, repo.Spec.ResticIdentifier, backupInfo.uploaderType, data.SourceNamespace, pvc) if err := errorOnly(r.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) @@ -181,7 +181,7 @@ ForEachVolume: return errs } -func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier, uploaderType string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { +func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier, uploaderType, sourceNamespace string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { pvr := &velerov1api.PodVolumeRestore{ ObjectMeta: metav1.ObjectMeta{ Namespace: restore.Namespace, @@ -213,6 +213,7 @@ func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backu BackupStorageLocation: backupLocation, RepoIdentifier: repoIdentifier, UploaderType: uploaderType, + SourceNamespace: sourceNamespace, }, } if pvc != nil { diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index 1a0a57682a..08dbb0ee52 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -31,6 +31,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/repository/provider" "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) const restoreProgressCheckInterval = 10 * time.Second @@ -81,7 +82,7 @@ func NewUploaderProvider( } return NewKopiaUploaderProvider(ctx, credGetter, backupRepo, log) } else { - if err := provider.NewResticRepositoryProvider(credGetter.FromFile, nil, log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { + if err := provider.NewResticRepositoryProvider(credGetter.FromFile, filesystem.NewFileSystem(), log).ConnectToRepo(ctx, provider.RepoParam{BackupLocation: bsl, BackupRepo: backupRepo}); err != nil { return nil, errors.Wrap(err, "failed to connect repository") } return NewResticUploaderProvider(repoIdentifier, bsl, credGetter, repoKeySelector, log) From eec27e942e45eab3dc7f175407506e471b7fce34 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 29 Sep 2022 15:01:30 +0800 Subject: [PATCH 320/366] Add backup status checking schedule controller. (#5283) Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Co-authored-by: Xun Jiang --- changelogs/unreleased/5283-blackpiglet | 1 + pkg/controller/schedule_controller.go | 73 +++++++++---- pkg/controller/schedule_controller_test.go | 121 +++++++++++++++------ 3 files changed, 144 insertions(+), 51 deletions(-) create mode 100644 changelogs/unreleased/5283-blackpiglet diff --git a/changelogs/unreleased/5283-blackpiglet b/changelogs/unreleased/5283-blackpiglet new file mode 100644 index 0000000000..1d08df3e73 --- /dev/null +++ b/changelogs/unreleased/5283-blackpiglet @@ -0,0 +1 @@ +Add backup status checking in schedule controller. \ No newline at end of file diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index 3f3df4a6f7..ec6721535d 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -26,6 +26,7 @@ import ( "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/clock" ctrl "sigs.k8s.io/controller-runtime" bld "sigs.k8s.io/controller-runtime/pkg/builder" @@ -127,10 +128,14 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } - // check for the schedule being due to run, and submit a Backup if so. + // Check for the schedule being due to run. + // If there are backup created by this schedule still in New or InProgress state, + // skip current backup creation to avoid running overlap backups. // As the schedule must be validated before checking whether it's due, we cannot put the checking log in Predicate - if err := c.submitBackupIfDue(ctx, schedule, cronSchedule); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "error running submitBackupIfDue for schedule %s", req.String()) + if c.ifDue(schedule, cronSchedule) && !c.checkIfBackupInNewOrProgress(schedule) { + if err := c.submitBackup(ctx, schedule); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "error submit backup for schedule %s", req.String()) + } } return ctrl.Result{}, nil @@ -176,35 +181,63 @@ func parseCronSchedule(itm *velerov1.Schedule, logger logrus.FieldLogger) (cron. return schedule, nil } -func (c *scheduleReconciler) submitBackupIfDue(ctx context.Context, item *velerov1.Schedule, cronSchedule cron.Schedule) error { - var ( - now = c.clock.Now() - isDue, nextRunTime = getNextRunTime(item, cronSchedule, now) - log = c.logger.WithField("schedule", kubeutil.NamespaceAndName(item)) - ) +// checkIfBackupInNewOrProgress check whether there are backups created by this schedule still in New or InProgress state +func (c *scheduleReconciler) checkIfBackupInNewOrProgress(schedule *velerov1.Schedule) bool { + log := c.logger.WithField("schedule", kubeutil.NamespaceAndName(schedule)) + backupList := &velerov1.BackupList{} + options := &client.ListOptions{ + Namespace: schedule.Namespace, + LabelSelector: labels.Set(map[string]string{ + velerov1.ScheduleNameLabel: schedule.Name, + }).AsSelector(), + } + + err := c.List(context.Background(), backupList, options) + if err != nil { + log.Errorf("fail to list backup for schedule %s/%s: %s", schedule.Namespace, schedule.Name, err.Error()) + return true + } + + for _, backup := range backupList.Items { + if backup.Status.Phase == velerov1.BackupPhaseNew || backup.Status.Phase == velerov1.BackupPhaseInProgress { + return true + } + } + + log.Debugf("Schedule %s/%s still has backups are in InProgress or New state, skip submitting backup to avoid overlap.", schedule.Namespace, schedule.Name) + return false +} + +// ifDue check whether schedule is due to create a new backup. +func (c *scheduleReconciler) ifDue(schedule *velerov1.Schedule, cronSchedule cron.Schedule) bool { + isDue, nextRunTime := getNextRunTime(schedule, cronSchedule, c.clock.Now()) + log := c.logger.WithField("schedule", kubeutil.NamespaceAndName(schedule)) if !isDue { log.WithField("nextRunTime", nextRunTime).Debug("Schedule is not due, skipping") - return nil + return false } + return true +} + +// submitBackup create a backup from schedule. +func (c *scheduleReconciler) submitBackup(ctx context.Context, schedule *velerov1.Schedule) error { + c.logger.WithField("schedule", schedule.Namespace+"/"+schedule.Name).Info("Schedule is due, going to submit backup.") + + now := c.clock.Now() // Don't attempt to "catch up" if there are any missed or failed runs - simply // trigger a Backup if it's time. - // - // It might also make sense in the future to explicitly check for currently-running - // backups so that we don't overlap runs (for disk snapshots in particular, this can - // lead to performance issues). - log.WithField("nextRunTime", nextRunTime).Info("Schedule is due, submitting Backup") - backup := getBackup(item, now) + backup := getBackup(schedule, now) if err := c.Create(ctx, backup); err != nil { return errors.Wrap(err, "error creating Backup") } - original := item.DeepCopy() - item.Status.LastBackup = &metav1.Time{Time: now} + original := schedule.DeepCopy() + schedule.Status.LastBackup = &metav1.Time{Time: now} - if err := c.Patch(ctx, item, client.MergeFrom(original)); err != nil { - return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", item.Status.LastBackup) + if err := c.Patch(ctx, schedule, client.MergeFrom(original)); err != nil { + return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", schedule.Status.LastBackup) } return nil diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go index c45846a81a..62e28f7a7d 100644 --- a/pkg/controller/schedule_controller_test.go +++ b/pkg/controller/schedule_controller_test.go @@ -31,7 +31,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/metrics" velerotest "github.com/vmware-tanzu/velero/pkg/test" @@ -40,19 +39,20 @@ import ( func TestReconcileOfSchedule(t *testing.T) { require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) - newScheduleBuilder := func(phase velerov1api.SchedulePhase) *builder.ScheduleBuilder { + newScheduleBuilder := func(phase velerov1.SchedulePhase) *builder.ScheduleBuilder { return builder.ForSchedule("ns", "name").Phase(phase) } tests := []struct { name string scheduleKey string - schedule *velerov1api.Schedule + schedule *velerov1.Schedule fakeClockTime string expectedPhase string expectedValidationErrors []string - expectedBackupCreate *velerov1api.Backup + expectedBackupCreate *velerov1.Backup expectedLastBackup string + backup *velerov1.Backup }{ { name: "missing schedule triggers no backup", @@ -60,49 +60,55 @@ func TestReconcileOfSchedule(t *testing.T) { }, { name: "schedule with phase FailedValidation triggers no backup", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseFailedValidation).Result(), + schedule: newScheduleBuilder(velerov1.SchedulePhaseFailedValidation).Result(), }, { name: "schedule with phase New gets validated and failed if invalid", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).Result(), - expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), + schedule: newScheduleBuilder(velerov1.SchedulePhaseNew).Result(), + expectedPhase: string(velerov1.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase gets validated and failed if invalid", - schedule: newScheduleBuilder(velerov1api.SchedulePhase("")).Result(), - expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), + schedule: newScheduleBuilder(velerov1.SchedulePhase("")).Result(), + expectedPhase: string(velerov1.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase Enabled gets re-validated and failed if invalid", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).Result(), - expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), + schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).Result(), + expectedPhase: string(velerov1.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase New gets validated and triggers a backup", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).CronSchedule("@every 5m").Result(), + schedule: newScheduleBuilder(velerov1.SchedulePhaseNew).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedPhase: string(velerov1api.SchedulePhaseEnabled), - expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), + expectedPhase: string(velerov1.SchedulePhaseEnabled), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, { name: "schedule with phase Enabled gets re-validated and triggers a backup if valid", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(), + schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedPhase: string(velerov1api.SchedulePhaseEnabled), - expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), + expectedPhase: string(velerov1.SchedulePhaseEnabled), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, { name: "schedule that's already run gets LastBackup updated", - schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), + schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), fakeClockTime: "2017-01-01 12:00:00", - expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).Result(), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, + { + name: "schedule already has backup in New state.", + schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), + expectedPhase: string(velerov1.SchedulePhaseEnabled), + backup: builder.ForBackup("ns", "name-20220905120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Phase(velerov1.BackupPhaseNew).Result(), + }, } for _, test := range tests { @@ -126,11 +132,15 @@ func TestReconcileOfSchedule(t *testing.T) { require.Nil(t, client.Create(ctx, test.schedule)) } + if test.backup != nil { + require.Nil(t, client.Create(ctx, test.backup)) + } + _, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "ns", Name: "name"}}) require.Nil(t, err) - schedule := &velerov1api.Schedule{} - err = client.Get(ctx, types.NamespacedName{"ns", "name"}, schedule) + schedule := &velerov1.Schedule{} + err = client.Get(ctx, types.NamespacedName{Namespace: "ns", Name: "name"}, schedule) if len(test.expectedPhase) > 0 { require.Nil(t, err) assert.Equal(t, test.expectedPhase, string(schedule.Status.Phase)) @@ -144,8 +154,19 @@ func TestReconcileOfSchedule(t *testing.T) { assert.Equal(t, parseTime(test.expectedLastBackup).Unix(), schedule.Status.LastBackup.Unix()) } - backups := &velerov1api.BackupList{} + backups := &velerov1.BackupList{} + require.Nil(t, client.List(ctx, backups)) + + // If backup associated with schedule's status is in New or InProgress, + // new backup shouldn't be submitted. + if test.backup != nil && + (test.backup.Status.Phase == velerov1.BackupPhaseNew || test.backup.Status.Phase == velerov1.BackupPhaseInProgress) { + assert.Equal(t, 1, len(backups.Items)) + require.Nil(t, client.Delete(ctx, test.backup)) + } + require.Nil(t, client.List(ctx, backups)) + if test.expectedBackupCreate == nil { assert.Equal(t, 0, len(backups.Items)) } else { @@ -161,13 +182,13 @@ func parseTime(timeString string) time.Time { } func TestGetNextRunTime(t *testing.T) { - defaultSchedule := func() *velerov1api.Schedule { + defaultSchedule := func() *velerov1.Schedule { return builder.ForSchedule("velero", "schedule-1").CronSchedule("@every 5m").Result() } tests := []struct { name string - schedule *velerov1api.Schedule + schedule *velerov1.Schedule lastRanOffset string expectedDue bool expectedNextRunTimeOffset string @@ -294,21 +315,21 @@ func TestParseCronSchedule(t *testing.T) { func TestGetBackup(t *testing.T) { tests := []struct { name string - schedule *velerov1api.Schedule + schedule *velerov1.Schedule testClockTime string - expectedBackup *velerov1api.Backup + expectedBackup *velerov1.Backup }{ { name: "ensure name is formatted correctly (AM time)", schedule: builder.ForSchedule("foo", "bar").Result(), testClockTime: "2017-07-25 09:15:00", - expectedBackup: builder.ForBackup("foo", "bar-20170725091500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(), + expectedBackup: builder.ForBackup("foo", "bar-20170725091500").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "bar")).Result(), }, { name: "ensure name is formatted correctly (PM time)", schedule: builder.ForSchedule("foo", "bar").Result(), testClockTime: "2017-07-25 14:15:00", - expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(), + expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "bar")).Result(), }, { name: "ensure schedule backup template is copied", @@ -325,7 +346,7 @@ func TestGetBackup(t *testing.T) { Result(), testClockTime: "2017-07-25 09:15:00", expectedBackup: builder.ForBackup("foo", "bar-20170725091500"). - ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")). + ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "bar")). IncludedNamespaces("ns-1", "ns-2"). ExcludedNamespaces("ns-3"). IncludedResources("foo", "bar"). @@ -338,13 +359,13 @@ func TestGetBackup(t *testing.T) { name: "ensure schedule labels are copied", schedule: builder.ForSchedule("foo", "bar").ObjectMeta(builder.WithLabels("foo", "bar", "bar", "baz")).Result(), testClockTime: "2017-07-25 14:15:00", - expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar", "bar", "baz", "foo", "bar")).Result(), + expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "bar", "bar", "baz", "foo", "bar")).Result(), }, { name: "ensure schedule annotations are copied", schedule: builder.ForSchedule("foo", "bar").ObjectMeta(builder.WithAnnotations("foo", "bar", "bar", "baz")).Result(), testClockTime: "2017-07-25 14:15:00", - expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar"), builder.WithAnnotations("bar", "baz", "foo", "bar")).Result(), + expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "bar"), builder.WithAnnotations("bar", "baz", "foo", "bar")).Result(), }, } @@ -363,3 +384,41 @@ func TestGetBackup(t *testing.T) { }) } } + +func TestCheckIfBackupInNewOrProgress(t *testing.T) { + require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) + + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + logger := velerotest.NewLogger() + + // Create testing schedule + testSchedule := builder.ForSchedule("ns", "name").Phase(velerov1.SchedulePhaseEnabled).Result() + err := client.Create(ctx, testSchedule) + require.NoError(t, err, "fail to create schedule in TestCheckIfBackupInNewOrProgress: %v", err) + + // Create backup in New phase. + newBackup := builder.ForBackup("ns", "backup-1"). + ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")). + Phase(velerov1.BackupPhaseNew).Result() + err = client.Create(ctx, newBackup) + require.NoError(t, err, "fail to create backup in New phase in TestCheckIfBackupInNewOrProgress: %v", err) + + reconciler := NewScheduleReconciler("ns", logger, client, metrics.NewServerMetrics()) + result := reconciler.checkIfBackupInNewOrProgress(testSchedule) + assert.True(t, result) + + // Clean backup in New phase. + err = client.Delete(ctx, newBackup) + require.NoError(t, err, "fail to delete backup in New phase in TestCheckIfBackupInNewOrProgress: %v", err) + + // Create backup in InProgress phase. + inProgressBackup := builder.ForBackup("ns", "backup-2"). + ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")). + Phase(velerov1.BackupPhaseInProgress).Result() + err = client.Create(ctx, inProgressBackup) + require.NoError(t, err, "fail to create backup in InProgress phase in TestCheckIfBackupInNewOrProgress: %v", err) + + reconciler = NewScheduleReconciler("namespace", logger, client, metrics.NewServerMetrics()) + result = reconciler.checkIfBackupInNewOrProgress(testSchedule) + assert.True(t, result) +} From 3f3a5050d69f9b7afc07f68a7db7fe249c4880c3 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 29 Sep 2022 15:24:06 +0800 Subject: [PATCH 321/366] Exclude "csinodes.storage.k8s.io" and "volumeattachments.storage.k8s.io" from backup and restore by default. (#5064) Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> --- changelogs/unreleased/5064-jxun | 1 + pkg/controller/restore_controller.go | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 changelogs/unreleased/5064-jxun diff --git a/changelogs/unreleased/5064-jxun b/changelogs/unreleased/5064-jxun new file mode 100644 index 0000000000..4de8a84605 --- /dev/null +++ b/changelogs/unreleased/5064-jxun @@ -0,0 +1 @@ +Exclude "csinodes.storage.k8s.io" and "volumeattachments.storage.k8s.io" from restore by default. \ No newline at end of file diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 559611fe09..d14917589b 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -78,6 +78,12 @@ var nonRestorableResources = []string{ // https://github.com/vmware-tanzu/velero/issues/1113 "resticrepositories.velero.io", + // CSINode delegates cluster node for CSI operation. + // VolumeAttachement records PV mounts to which node. + // https://github.com/vmware-tanzu/velero/issues/4823 + "csinodes.storage.k8s.io", + "volumeattachments.storage.k8s.io", + // Backup repositories were renamed from Restic repositories "backuprepositories.velero.io", } From e699a3e9f25dbe98bbd0456fbe0091b3cba010ca Mon Sep 17 00:00:00 2001 From: danfengl Date: Wed, 28 Sep 2022 02:29:38 +0000 Subject: [PATCH 322/366] Fix issues of E2E test for API group, migration and pv opt-out backup 1. One of API group test failed due to other PR with fix for treat PartiallyFailed as failure to collect debugbundle without wrap the origin error; 2. Fix migration test issue of wrong velero cli for backup commmand; 3. Fix wrong pararmeter name issue for pv opt-out backup test. Signed-off-by: danfengl --- test/e2e/basic/enable_api_group_versions.go | 6 ++---- test/e2e/e2e_suite_test.go | 2 +- test/e2e/migration/migration.go | 24 ++++++++++++++------- test/e2e/pv-backup/pv-backup-filter.go | 9 ++++---- test/e2e/util/common/common.go | 16 -------------- test/e2e/util/velero/velero_utils.go | 2 +- 6 files changed, 25 insertions(+), 34 deletions(-) diff --git a/test/e2e/basic/enable_api_group_versions.go b/test/e2e/basic/enable_api_group_versions.go index fea32ae9c4..862854a02c 100644 --- a/test/e2e/basic/enable_api_group_versions.go +++ b/test/e2e/basic/enable_api_group_versions.go @@ -36,7 +36,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" . "github.com/vmware-tanzu/velero/test/e2e" - . "github.com/vmware-tanzu/velero/test/e2e/util/common" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) @@ -450,8 +449,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso // No custom resource should have been restored. Expect "no resource found" // error during restore. err := VeleroRestore(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, restore, backup, "") - - if err.Error() != "Unexpected restore phase got PartiallyFailed, expecting Completed" { + if !strings.Contains(err.Error(), "Unexpected restore phase got PartiallyFailed, expecting Completed") { return errors.New("expected error but not none") } } @@ -467,7 +465,7 @@ func runEnableAPIGroupVersionsTests(ctx context.Context, client TestClient, reso func installCRD(ctx context.Context, yaml string) error { fmt.Printf("Install CRD with %s.\n", yaml) - err := KubectlApplyFile(ctx, yaml) + err := KubectlApplyByFile(ctx, yaml) return err } diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 4b733a81f3..1025d02166 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -81,7 +81,7 @@ func init() { flag.StringVar(&VeleroCfg.UploaderType, "uploader-type", "", "Identify persistent volume backup uploader.") } -var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", APIGropuVersionsTest) +var _ = Describe("[APIGroup][Common] Velero tests with various CRD API group versions", APIGropuVersionsTest) var _ = Describe("[APIGroup][APIExtensions] CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", APIExtensionsVersionsTest) // Test backup and restore of Kibishi using restic diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 667acef604..3e81d8dd15 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -113,11 +113,9 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) } }) } - + OriginVeleroCfg := VeleroCfg By(fmt.Sprintf("Install Velero in cluster-A (%s) to backup workload", VeleroCfg.DefaultCluster), func() { Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed()) - - OriginVeleroCfg := VeleroCfg OriginVeleroCfg.MigrateFromVeleroVersion = veleroCLI2Version.VeleroVersion OriginVeleroCfg.VeleroCLI = veleroCLI2Version.VeleroCLI OriginVeleroCfg.ClientToInstallVelero = OriginVeleroCfg.DefaultClient @@ -158,9 +156,14 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupStorageClassCfg.BackupName = backupScName BackupStorageClassCfg.IncludeResources = "StorageClass" BackupStorageClassCfg.IncludeClusterResources = true + //TODO Remove UseRestic parameter once minor version is 1.10 or upper + BackupStorageClassCfg.UseRestic = true + if veleroCLI2Version.VeleroVersion == "self" { + BackupStorageClassCfg.UseRestic = false + } - Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupStorageClassCfg)).To(Succeed(), func() string { + Expect(VeleroBackupNamespace(context.Background(), OriginVeleroCfg.VeleroCLI, + OriginVeleroCfg.VeleroNamespace, BackupStorageClassCfg)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupStorageClassCfg.BackupName, "") return "Fail to backup workload" }) @@ -171,9 +174,14 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupCfg.UseVolumeSnapshots = useVolumeSnapshots BackupCfg.BackupLocation = "" BackupCfg.Selector = "" - Expect(VeleroBackupNamespace(context.Background(), VeleroCfg.VeleroCLI, - VeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { - RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, BackupCfg.BackupName, "") + //TODO Remove UseRestic parameter once minor version is 1.10 or upper + BackupCfg.UseRestic = true + if veleroCLI2Version.VeleroVersion == "self" { + BackupCfg.UseRestic = false + } + Expect(VeleroBackupNamespace(context.Background(), OriginVeleroCfg.VeleroCLI, + OriginVeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { + RunDebug(context.Background(), OriginVeleroCfg.VeleroCLI, OriginVeleroCfg.VeleroNamespace, BackupCfg.BackupName, "") return "Fail to backup workload" }) }) diff --git a/test/e2e/pv-backup/pv-backup-filter.go b/test/e2e/pv-backup/pv-backup-filter.go index 6c5148c979..3de103894b 100644 --- a/test/e2e/pv-backup/pv-backup-filter.go +++ b/test/e2e/pv-backup/pv-backup-filter.go @@ -12,7 +12,6 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/test" - . "github.com/vmware-tanzu/velero/test/e2e/util/common" . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" ) @@ -46,7 +45,7 @@ func (p *PVBackupFiltering) Init() error { } func (p *PVBackupFiltering) StartRun() error { - err := installStorageClass(p.Ctx, fmt.Sprintf("testdata/storage-class/%s.yaml", VeleroCfg.CloudProvider)) + err := installStorageClass(context.Background(), fmt.Sprintf("testdata/storage-class/%s.yaml", VeleroCfg.CloudProvider)) if err != nil { return err } @@ -57,8 +56,10 @@ func (p *PVBackupFiltering) StartRun() error { "--include-namespaces", strings.Join(*p.NSIncluded, ","), "--snapshot-volumes=false", "--wait", } + // "--default-volumes-to-fs-backup" is an overall switch, if it's set, then opt-in + // annotation will be ignored, so it's only set for opt-out test if p.annotation == OPT_OUT_ANN { - p.BackupArgs = append(p.BackupArgs, "--default-volumes-to-restic") + p.BackupArgs = append(p.BackupArgs, "--default-volumes-to-fs-backup") } p.RestoreArgs = []string{ @@ -204,6 +205,6 @@ func fileNotExist(ctx context.Context, namespace, podName, volume string) error func installStorageClass(ctx context.Context, yaml string) error { fmt.Printf("Install storage class with %s.\n", yaml) - err := KubectlApplyFile(ctx, yaml) + err := KubectlApplyByFile(ctx, yaml) return err } diff --git a/test/e2e/util/common/common.go b/test/e2e/util/common/common.go index 9178eda140..f7a43fc71b 100644 --- a/test/e2e/util/common/common.go +++ b/test/e2e/util/common/common.go @@ -6,10 +6,6 @@ import ( "context" "fmt" "os/exec" - - "github.com/pkg/errors" - - veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) type OsCommandLine struct { @@ -56,15 +52,3 @@ func GetListBy2Pipes(ctx context.Context, cmdline1, cmdline2, cmdline3 OsCommand return ret, nil } - -func KubectlApplyFile(ctx context.Context, yaml string) error { - fmt.Printf("Kube apply file %s.\n", yaml) - cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", yaml) - - _, stderr, err := veleroexec.RunCommand(cmd) - if err != nil { - return errors.Wrap(err, stderr) - } - - return nil -} diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index c67598ad19..f8495865c1 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -443,7 +443,7 @@ func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { err := cmd.Run() retAll := outBuf.String() + " " + errBuf.String() if strings.Contains(strings.ToLower(retAll), "failed") { - return errors.New(fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) + return errors.Wrap(err, fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) } if err != nil { return err From a80c96c8f8634ab8b6711bd28d4ac736a52cda57 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Fri, 30 Sep 2022 14:08:43 +0800 Subject: [PATCH 323/366] update velero using klog to version v2.9.0 (#5396) Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang Co-authored-by: Xun Jiang --- changelogs/unreleased/5396-blackpiglet | 1 + cmd/velero/velero.go | 2 +- go.mod | 3 +-- go.sum | 2 -- pkg/cmd/velero/velero.go | 2 +- pkg/controller/suite_test.go | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/5396-blackpiglet diff --git a/changelogs/unreleased/5396-blackpiglet b/changelogs/unreleased/5396-blackpiglet new file mode 100644 index 0000000000..b4808596e7 --- /dev/null +++ b/changelogs/unreleased/5396-blackpiglet @@ -0,0 +1 @@ +update velero using klog to version v2.9.0 \ No newline at end of file diff --git a/cmd/velero/velero.go b/cmd/velero/velero.go index ece94f1617..07be4a8847 100644 --- a/cmd/velero/velero.go +++ b/cmd/velero/velero.go @@ -20,7 +20,7 @@ import ( "os" "path/filepath" - "k8s.io/klog" + "k8s.io/klog/v2" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/velero" diff --git a/go.mod b/go.mod index 6d36169039..75512d71ac 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( k8s.io/apimachinery v0.22.2 k8s.io/cli-runtime v0.22.2 k8s.io/client-go v0.22.2 - k8s.io/klog v1.0.0 + k8s.io/klog/v2 v2.9.0 k8s.io/kube-aggregator v0.19.12 sigs.k8s.io/controller-runtime v0.10.2 sigs.k8s.io/yaml v1.3.0 @@ -139,7 +139,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/component-base v0.22.2 // indirect - k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect diff --git a/go.sum b/go.sum index 8fa8ba1958..75dc52473f 100644 --- a/go.sum +++ b/go.sum @@ -1486,8 +1486,6 @@ k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllun k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index 8775f69cd0..fe712adc02 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -23,7 +23,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - "k8s.io/klog" + "k8s.io/klog/v2" "github.com/vmware-tanzu/velero/pkg/cmd/cli/debug" diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index b5c5b4d087..89e6159a1f 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -28,7 +28,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/persistence" persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" - "k8s.io/klog" + "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/manager" From 47f8eb5f9beffcb4a17bb8e0619fbea1202ecf19 Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Fri, 30 Sep 2022 17:30:18 +0800 Subject: [PATCH 324/366] Change B/R describe cli to support kopia Signed-off-by: allenxu404 --- changelogs/unreleased/5412-allenxu404 | 1 + pkg/cmd/util/output/backup_describer.go | 13 +++++++++++-- pkg/cmd/util/output/restore_describer.go | 13 +++++++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/5412-allenxu404 diff --git a/changelogs/unreleased/5412-allenxu404 b/changelogs/unreleased/5412-allenxu404 new file mode 100644 index 0000000000..3258f8c147 --- /dev/null +++ b/changelogs/unreleased/5412-allenxu404 @@ -0,0 +1 @@ +Change B/R describe CLI to support Kopia \ No newline at end of file diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index f8c05c1567..a6a2f6f996 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -403,10 +403,19 @@ func failedDeletionCount(requests []velerov1api.DeleteBackupRequest) int { // DescribePodVolumeBackups describes pod volume backups in human-readable format. func DescribePodVolumeBackups(d *Describer, backups []velerov1api.PodVolumeBackup, details bool) { + // Get the type of pod volume uploader. Since the uploader only comes from a single source, we can + // take the uploader type from the first element of the array. + var uploaderType string + if len(backups) > 0 { + uploaderType = backups[0].Spec.UploaderType + } else { + return + } + if details { - d.Printf("Restic Backups:\n") + d.Printf("%s Backups:\n", uploaderType) } else { - d.Printf("Restic Backups (specify --details for more information):\n") + d.Printf("%s Backups (specify --details for more information):\n", uploaderType) } // separate backups by phase (combining and New into a single group) diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index 286467d038..f6b5f2b378 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -205,10 +205,19 @@ func describeRestoreResult(d *Describer, name string, result pkgrestore.Result) // describePodVolumeRestores describes pod volume restores in human-readable format. func describePodVolumeRestores(d *Describer, restores []velerov1api.PodVolumeRestore, details bool) { + // Get the type of pod volume uploader. Since the uploader only comes from a single source, we can + // take the uploader type from the first element of the array. + var uploaderType string + if len(restores) > 0 { + uploaderType = restores[0].Spec.UploaderType + } else { + return + } + if details { - d.Printf("Restic Restores:\n") + d.Printf("%s Restores:\n", uploaderType) } else { - d.Printf("Restic Restores (specify --details for more information):\n") + d.Printf("%s Restores (specify --details for more information):\n", uploaderType) } // separate restores by phase (combining and New into a single group) From 0ad2321078ffffe2b8fb83a316af8ae0829532eb Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Mon, 3 Oct 2022 14:12:31 +0300 Subject: [PATCH 325/366] Update Maintainers with the new PM pradeepkchaturvedi Signed-off-by: OrlinVasilev --- MAINTAINERS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index f62eb0f94f..7fa1487e63 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -30,9 +30,9 @@ | Feature Area | Lead | | ----------------------------- | :---------------------: | -| Architect | Dave Smith-Uchida (dsu-igeek) | -| Technical Lead | Daniel Jiang (reasonerjt) | +| Architect | Dave Smith-Uchida [dsu-igeek](https://github.com/dsu-igeek) | +| Technical Lead | Daniel Jiang [reasonerjt](https://github.com/reasonerjt) | | Kubernetes CSI Liaison | | | Deployment | | -| Community Management | Orlin Vasilev (OrlinVasilev) | -| Product Management | Eleanor Millman (eleanor-millman) | +| Community Management | Orlin Vasilev [OrlinVasilev](https://github.com/OrlinVasilev) | +| Product Management | Eleanor Millman [pradeepkchaturvedi](https://github.com/pradeepkchaturvedi) | From 1165c7e5fcd48af65d3ebcc6855a79af2f01ad0c Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Mon, 3 Oct 2022 14:12:31 +0300 Subject: [PATCH 326/366] Update Maintainers with the new PM pradeepkchaturvedi Signed-off-by: OrlinVasilev --- MAINTAINERS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 7fa1487e63..7dc98011ac 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -35,4 +35,5 @@ | Kubernetes CSI Liaison | | | Deployment | | | Community Management | Orlin Vasilev [OrlinVasilev](https://github.com/OrlinVasilev) | -| Product Management | Eleanor Millman [pradeepkchaturvedi](https://github.com/pradeepkchaturvedi) | +| Product Management | Pradeep Kumar Chaturvedi [pradeepkchaturvedi](https://github.com/pradeepkchaturvedi) | + From 28c543a9ecb430cea2b750554129f9bdc40cd32f Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 26 Sep 2022 18:33:02 +0800 Subject: [PATCH 327/366] Skip the exclusion check for additional resources returned by BIA This commit provides a simple contract that if the BackupItemAction plugin sets an annotation in a resource it has handled, the additional items will considered "must include" i.e. each of them will skip the "include-exclude" filter, such that the plugin developer can make sure they are included in the backup disregarding the filter setting in the bakcup CR. Signed-off-by: Daniel Jiang --- changelogs/unreleased/5429-reasonerjt | 1 + pkg/backup/backup.go | 2 +- pkg/backup/item_backupper.go | 71 ++++++++++++++++----------- 3 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 changelogs/unreleased/5429-reasonerjt diff --git a/changelogs/unreleased/5429-reasonerjt b/changelogs/unreleased/5429-reasonerjt new file mode 100644 index 0000000000..2fdd8fe8d8 --- /dev/null +++ b/changelogs/unreleased/5429-reasonerjt @@ -0,0 +1 @@ +Skip the exclusion check for additional resources returned by BIA \ No newline at end of file diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 0b00417fdd..ba20ed0d4a 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -416,7 +416,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, } func (kb *kubernetesBackupper) backupItem(log logrus.FieldLogger, gr schema.GroupResource, itemBackupper *itemBackupper, unstructured *unstructured.Unstructured, preferredGVR schema.GroupVersionResource) bool { - backedUpItem, err := itemBackupper.backupItem(log, unstructured, gr, preferredGVR) + backedUpItem, err := itemBackupper.backupItem(log, unstructured, gr, preferredGVR, false) if aggregate, ok := err.(kubeerrs.Aggregate); ok { log.WithField("name", unstructured.GetName()).Infof("%d errors encountered backup up item", len(aggregate.Errors())) // log each error separately so we get error location info in the log, and an diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 879f4d70c5..0e24fb32cf 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -24,6 +24,8 @@ import ( "strings" "time" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/pkg/errors" "github.com/sirupsen/logrus" corev1api "k8s.io/api/core/v1" @@ -47,6 +49,11 @@ import ( "github.com/vmware-tanzu/velero/pkg/volume" ) +const ( + mustIncludeAdditionalItemAnnotation = "backup.velero.io/must-include-additional-items" + excludeFromBackupLabel = "velero.io/exclude-from-backup" +) + // itemBackupper can back up individual items to a tar writer. type itemBackupper struct { backupRequest *Request @@ -61,16 +68,11 @@ type itemBackupper struct { snapshotLocationVolumeSnapshotters map[string]vsv1.VolumeSnapshotter } -const ( - // veleroExcludeFromBackupLabel labeled item should be exclude by velero in backup job. - veleroExcludeFromBackupLabel = "velero.io/exclude-from-backup" -) - // backupItem backs up an individual item to tarWriter. The item may be excluded based on the // namespaces IncludesExcludes list. // In addition to the error return, backupItem also returns a bool indicating whether the item // was actually backed up. -func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource, preferredGVR schema.GroupVersionResource) (bool, error) { +func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource, preferredGVR schema.GroupVersionResource, mustInclude bool) (bool, error) { metadata, err := meta.Accessor(obj) if err != nil { return false, err @@ -83,28 +85,30 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr log = log.WithField("resource", groupResource.String()) log = log.WithField("namespace", namespace) - if metadata.GetLabels()[veleroExcludeFromBackupLabel] == "true" { - log.Infof("Excluding item because it has label %s=true", veleroExcludeFromBackupLabel) - return false, nil - } - - // NOTE: we have to re-check namespace & resource includes/excludes because it's possible that - // backupItem can be invoked by a custom action. - if namespace != "" && !ib.backupRequest.NamespaceIncludesExcludes.ShouldInclude(namespace) { - log.Info("Excluding item because namespace is excluded") - return false, nil - } - - // NOTE: we specifically allow namespaces to be backed up even if IncludeClusterResources is - // false. - if namespace == "" && groupResource != kuberesource.Namespaces && ib.backupRequest.Spec.IncludeClusterResources != nil && !*ib.backupRequest.Spec.IncludeClusterResources { - log.Info("Excluding item because resource is cluster-scoped and backup.spec.includeClusterResources is false") - return false, nil - } + if mustInclude { + log.Infof("Skipping the exclusion checks for this resource") + } else { + if metadata.GetLabels()[excludeFromBackupLabel] == "true" { + log.Infof("Excluding item because it has label %s=true", excludeFromBackupLabel) + return false, nil + } + // NOTE: we have to re-check namespace & resource includes/excludes because it's possible that + // backupItem can be invoked by a custom action. + if namespace != "" && !ib.backupRequest.NamespaceIncludesExcludes.ShouldInclude(namespace) { + log.Info("Excluding item because namespace is excluded") + return false, nil + } + // NOTE: we specifically allow namespaces to be backed up even if IncludeClusterResources is + // false. + if namespace == "" && groupResource != kuberesource.Namespaces && ib.backupRequest.Spec.IncludeClusterResources != nil && !*ib.backupRequest.Spec.IncludeClusterResources { + log.Info("Excluding item because resource is cluster-scoped and backup.spec.includeClusterResources is false") + return false, nil + } - if !ib.backupRequest.ResourceIncludesExcludes.ShouldInclude(groupResource.String()) { - log.Info("Excluding item because resource is excluded") - return false, nil + if !ib.backupRequest.ResourceIncludesExcludes.ShouldInclude(groupResource.String()) { + log.Info("Excluding item because resource is excluded") + return false, nil + } } if metadata.GetDeletionTimestamp() != nil { @@ -320,7 +324,8 @@ func (ib *itemBackupper) executeActions( if err != nil { return nil, errors.Wrapf(err, "error executing custom action (groupResource=%s, namespace=%s, name=%s)", groupResource.String(), namespace, name) } - obj = updatedItem + u := &unstructured.Unstructured{Object: updatedItem.UnstructuredContent()} + mustInclude := u.GetAnnotations()[mustIncludeAdditionalItemAnnotation] == "true" for _, additionalItem := range additionalItemIdentifiers { gvr, resource, err := ib.discoveryHelper.ResourceFor(additionalItem.GroupResource.WithVersion("")) @@ -334,6 +339,7 @@ func (ib *itemBackupper) executeActions( } item, err := client.Get(additionalItem.Name, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { log.WithFields(logrus.Fields{ "groupResource": additionalItem.GroupResource, @@ -346,12 +352,17 @@ func (ib *itemBackupper) executeActions( return nil, errors.WithStack(err) } - if _, err = ib.backupItem(log, item, gvr.GroupResource(), gvr); err != nil { + if _, err = ib.backupItem(log, item, gvr.GroupResource(), gvr, mustInclude); err != nil { return nil, err } } + // remove the annotation as it's for communication between BIA and velero server, + // we don't want the resource be restored with this annotation. + if _, ok := u.GetAnnotations()[mustIncludeAdditionalItemAnnotation]; ok { + delete(u.GetAnnotations(), mustIncludeAdditionalItemAnnotation) + } + obj = u } - return obj, nil } From 83ea1cc58b4d99710f687eab66d5cc96316747bc Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Tue, 11 Oct 2022 14:18:21 +0800 Subject: [PATCH 328/366] rename daemonset (#5390) Signed-off-by: Lyndon-Li Signed-off-by: Lyndon-Li --- .gitignore | 2 +- Tiltfile | 8 ++-- changelogs/unreleased/5390-lyndon | 1 + .../plugins/{restic.yaml => node-agent.yaml} | 10 ++-- pkg/cmd/cli/debug/debug.go | 2 +- pkg/cmd/cli/install/install.go | 46 +++++++++---------- pkg/cmd/cli/nodeagent/node_agent.go | 37 +++++++++++++++ pkg/cmd/cli/{restic => nodeagent}/server.go | 8 ++-- .../cli/{restic => nodeagent}/server_test.go | 2 +- pkg/cmd/cli/restic/restic.go | 1 - pkg/cmd/velero/velero.go | 3 ++ pkg/install/daemonset.go | 16 +++---- pkg/install/daemonset_test.go | 2 +- pkg/install/install.go | 4 +- pkg/install/resources.go | 26 +++++------ pkg/nodeagent/node_agent.go | 2 +- test/e2e/util/velero/install.go | 28 +++++------ .../examples/{restic.yaml => node-agent.yaml} | 10 ++-- 18 files changed, 124 insertions(+), 84 deletions(-) create mode 100644 changelogs/unreleased/5390-lyndon rename design/CLI/PoC/overlays/plugins/{restic.yaml => node-agent.yaml} (93%) create mode 100644 pkg/cmd/cli/nodeagent/node_agent.go rename pkg/cmd/cli/{restic => nodeagent}/server.go (98%) rename pkg/cmd/cli/{restic => nodeagent}/server_test.go (99%) rename tilt-resources/examples/{restic.yaml => node-agent.yaml} (93%) diff --git a/.gitignore b/.gitignore index 18482277e2..a741e968b8 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,7 @@ _tiltbuild tilt-resources/tilt-settings.json tilt-resources/velero_v1_backupstoragelocation.yaml tilt-resources/deployment.yaml -tilt-resources/restic.yaml +tilt-resources/node-agent.yaml tilt-resources/cloud test/e2e/report.xml diff --git a/Tiltfile b/Tiltfile index 0d9a642632..2a7d97eb41 100644 --- a/Tiltfile +++ b/Tiltfile @@ -17,7 +17,7 @@ k8s_yaml([ # default values settings = { "default_registry": "docker.io/velero", - "enable_restic": False, + "use_node_agent": False, "enable_debug": False, "debug_continue_on_start": True, # Continue the velero process by default when in debug mode "create_backup_locations": False, @@ -34,9 +34,9 @@ k8s_yaml(kustomize('tilt-resources')) k8s_yaml('tilt-resources/deployment.yaml') if settings.get("enable_debug"): k8s_resource('velero', port_forwards = '2345') - # TODO: Need to figure out how to apply port forwards for all restic pods -if settings.get("enable_restic"): - k8s_yaml('tilt-resources/restic.yaml') + # TODO: Need to figure out how to apply port forwards for all node-agent pods +if settings.get("use_node_agent"): + k8s_yaml('tilt-resources/node-agent.yaml') if settings.get("create_backup_locations"): k8s_yaml('tilt-resources/velero_v1_backupstoragelocation.yaml') if settings.get("setup-minio"): diff --git a/changelogs/unreleased/5390-lyndon b/changelogs/unreleased/5390-lyndon new file mode 100644 index 0000000000..091b074c7b --- /dev/null +++ b/changelogs/unreleased/5390-lyndon @@ -0,0 +1 @@ +Rename Velero daemonset from "restic" to "node-agent" \ No newline at end of file diff --git a/design/CLI/PoC/overlays/plugins/restic.yaml b/design/CLI/PoC/overlays/plugins/node-agent.yaml similarity index 93% rename from design/CLI/PoC/overlays/plugins/restic.yaml rename to design/CLI/PoC/overlays/plugins/node-agent.yaml index 576ea2ff50..dbb4ce18db 100644 --- a/design/CLI/PoC/overlays/plugins/restic.yaml +++ b/design/CLI/PoC/overlays/plugins/node-agent.yaml @@ -5,22 +5,22 @@ metadata: creationTimestamp: null labels: component: velero - name: restic + name: node-agent namespace: velero spec: selector: matchLabels: - name: restic + name: node-agent template: metadata: creationTimestamp: null labels: component: velero - name: restic + name: node-agent spec: containers: - args: - - restic + - node-agent - server command: - /velero @@ -43,7 +43,7 @@ spec: value: /credentials/cloud image: velero/velero:latest imagePullPolicy: Always - name: restic + name: node-agent resources: {} volumeMounts: - mountPath: /host_pods diff --git a/pkg/cmd/cli/debug/debug.go b/pkg/cmd/cli/debug/debug.go index e324f26312..fba0a1af35 100644 --- a/pkg/cmd/cli/debug/debug.go +++ b/pkg/cmd/cli/debug/debug.go @@ -152,7 +152,7 @@ func NewCommand(f client.Factory) *cobra.Command { c := &cobra.Command{ Use: "debug", Short: "Generate debug bundle", - Long: `Generate a tarball containing the logs of velero deployment, plugin logs, restic DaemonSet, + Long: `Generate a tarball containing the logs of velero deployment, plugin logs, node-agent DaemonSet, specs of resources created by velero server, and optionally the logs of backup and restore.`, Run: func(c *cobra.Command, args []string) { flags := c.Flags() diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index fe4bebea30..f9cffa48fb 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -55,17 +55,17 @@ type InstallOptions struct { VeleroPodMemRequest string VeleroPodCPULimit string VeleroPodMemLimit string - ResticPodCPURequest string - ResticPodMemRequest string - ResticPodCPULimit string - ResticPodMemLimit string + NodeAgentPodCPURequest string + NodeAgentPodMemRequest string + NodeAgentPodCPULimit string + NodeAgentPodMemLimit string RestoreOnly bool SecretFile string NoSecret bool DryRun bool BackupStorageConfig flag.Map VolumeSnapshotConfig flag.Map - UseRestic bool + UseNodeAgent bool Wait bool UseVolumeSnapshots bool DefaultRepoMaintenanceFrequency time.Duration @@ -95,23 +95,23 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.VeleroPodMemRequest, "velero-pod-mem-request", o.VeleroPodMemRequest, `Memory request for Velero pod. A value of "0" is treated as unbounded. Optional.`) flags.StringVar(&o.VeleroPodCPULimit, "velero-pod-cpu-limit", o.VeleroPodCPULimit, `CPU limit for Velero pod. A value of "0" is treated as unbounded. Optional.`) flags.StringVar(&o.VeleroPodMemLimit, "velero-pod-mem-limit", o.VeleroPodMemLimit, `Memory limit for Velero pod. A value of "0" is treated as unbounded. Optional.`) - flags.StringVar(&o.ResticPodCPURequest, "restic-pod-cpu-request", o.ResticPodCPURequest, `CPU request for restic pod. A value of "0" is treated as unbounded. Optional.`) - flags.StringVar(&o.ResticPodMemRequest, "restic-pod-mem-request", o.ResticPodMemRequest, `Memory request for restic pod. A value of "0" is treated as unbounded. Optional.`) - flags.StringVar(&o.ResticPodCPULimit, "restic-pod-cpu-limit", o.ResticPodCPULimit, `CPU limit for restic pod. A value of "0" is treated as unbounded. Optional.`) - flags.StringVar(&o.ResticPodMemLimit, "restic-pod-mem-limit", o.ResticPodMemLimit, `Memory limit for restic pod. A value of "0" is treated as unbounded. Optional.`) + flags.StringVar(&o.NodeAgentPodCPURequest, "node-agent-pod-cpu-request", o.NodeAgentPodCPURequest, `CPU request for node-agent pod. A value of "0" is treated as unbounded. Optional.`) + flags.StringVar(&o.NodeAgentPodMemRequest, "node-agent-pod-mem-request", o.NodeAgentPodMemRequest, `Memory request for node-agent pod. A value of "0" is treated as unbounded. Optional.`) + flags.StringVar(&o.NodeAgentPodCPULimit, "node-agent-pod-cpu-limit", o.NodeAgentPodCPULimit, `CPU limit for node-agent pod. A value of "0" is treated as unbounded. Optional.`) + flags.StringVar(&o.NodeAgentPodMemLimit, "node-agent-pod-mem-limit", o.NodeAgentPodMemLimit, `Memory limit for node-agent pod. A value of "0" is treated as unbounded. Optional.`) flags.Var(&o.BackupStorageConfig, "backup-location-config", "Configuration to use for the backup storage location. Format is key1=value1,key2=value2") flags.Var(&o.VolumeSnapshotConfig, "snapshot-location-config", "Configuration to use for the volume snapshot location. Format is key1=value1,key2=value2") flags.BoolVar(&o.UseVolumeSnapshots, "use-volume-snapshots", o.UseVolumeSnapshots, "Whether or not to create snapshot location automatically. Set to false if you do not plan to create volume snapshots via a storage provider.") flags.BoolVar(&o.RestoreOnly, "restore-only", o.RestoreOnly, "Run the server in restore-only mode. Optional.") flags.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Generate resources, but don't send them to the cluster. Use with -o. Optional.") - flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "Create restic daemonset. Optional.") + flags.BoolVar(&o.UseNodeAgent, "use-node-agent", o.UseNodeAgent, "Create Velero node-agent daemonset. Optional. Velero node-agent hosts Velero modules that need to run in one or more nodes(i.e. Restic, Kopia).") flags.BoolVar(&o.Wait, "wait", o.Wait, "Wait for Velero deployment to be ready. Optional.") flags.DurationVar(&o.DefaultRepoMaintenanceFrequency, "default-repo-maintain-frequency", o.DefaultRepoMaintenanceFrequency, "How often 'maintain' is run for backup repositories by default. Optional.") flags.DurationVar(&o.GarbageCollectionFrequency, "garbage-collection-frequency", o.GarbageCollectionFrequency, "How often the garbage collection runs for expired backups.(default 1h)") flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment") flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "Only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.") flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "File containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") - flags.StringVar(&o.Features, "features", o.Features, "Comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled") + flags.StringVar(&o.Features, "features", o.Features, "Comma separated list of Velero feature flags to be set on the Velero deployment and the node-agent daemonset, if node-agent is enabled") flags.BoolVar(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", o.DefaultVolumesToFsBackup, "Bool flag to configure Velero server to use pod volume file system backup by default for all volumes on all backups. Optional.") flags.StringVar(&o.UploaderType, "uploader-type", o.UploaderType, fmt.Sprintf("The type of uploader to transfer the data of pod volumes, the supported values are '%s', '%s'", uploader.ResticType, uploader.KopiaType)) } @@ -130,10 +130,10 @@ func NewInstallOptions() *InstallOptions { VeleroPodMemRequest: install.DefaultVeleroPodMemRequest, VeleroPodCPULimit: install.DefaultVeleroPodCPULimit, VeleroPodMemLimit: install.DefaultVeleroPodMemLimit, - ResticPodCPURequest: install.DefaultResticPodCPURequest, - ResticPodMemRequest: install.DefaultResticPodMemRequest, - ResticPodCPULimit: install.DefaultResticPodCPULimit, - ResticPodMemLimit: install.DefaultResticPodMemLimit, + NodeAgentPodCPURequest: install.DefaultNodeAgentPodCPURequest, + NodeAgentPodMemRequest: install.DefaultNodeAgentPodMemRequest, + NodeAgentPodCPULimit: install.DefaultNodeAgentPodCPULimit, + NodeAgentPodMemLimit: install.DefaultNodeAgentPodMemLimit, // Default to creating a VSL unless we're told otherwise UseVolumeSnapshots: true, NoDefaultBackupLocation: false, @@ -171,7 +171,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { if err != nil { return nil, err } - resticPodResources, err := kubeutil.ParseResourceRequirements(o.ResticPodCPURequest, o.ResticPodMemRequest, o.ResticPodCPULimit, o.ResticPodMemLimit) + nodeAgentPodResources, err := kubeutil.ParseResourceRequirements(o.NodeAgentPodCPURequest, o.NodeAgentPodMemRequest, o.NodeAgentPodCPULimit, o.NodeAgentPodMemLimit) if err != nil { return nil, err } @@ -186,10 +186,10 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { PodLabels: o.PodLabels.Data(), ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), VeleroPodResources: veleroPodResources, - ResticPodResources: resticPodResources, + NodeAgentPodResources: nodeAgentPodResources, SecretData: secretData, RestoreOnly: o.RestoreOnly, - UseRestic: o.UseRestic, + UseNodeAgent: o.UseNodeAgent, UseVolumeSnapshots: o.UseVolumeSnapshots, BSLConfig: o.BackupStorageConfig.Data(), VSLConfig: o.VolumeSnapshotConfig.Data(), @@ -216,7 +216,7 @@ A prefix within the bucket and configuration for the backup store location may a Additionally, volume snapshot information for the same provider may be supplied. All required CustomResourceDefinitions will be installed to the server, as well as the -Velero Deployment and associated Restic DaemonSet. +Velero Deployment and associated node-agent DaemonSet. The provided secret data will be created in a Secret named 'cloud-credentials'. @@ -302,8 +302,8 @@ func (o *InstallOptions) Run(c *cobra.Command, f client.Factory) error { return errors.Wrap(err, errorMsg) } - if o.UseRestic { - fmt.Println("Waiting for Velero restic daemonset to be ready.") + if o.UseNodeAgent { + fmt.Println("Waiting for node-agent daemonset to be ready.") if _, err = install.DaemonSetIsReady(dynamicFactory, o.Namespace); err != nil { return errors.Wrap(err, errorMsg) } @@ -393,8 +393,8 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact } } - if o.DefaultVolumesToFsBackup && !o.UseRestic { - return errors.New("--use-restic is required when using --default-volumes-to-fs-backup") + if o.DefaultVolumesToFsBackup && !o.UseNodeAgent { + return errors.New("--use-node-agent is required when using --default-volumes-to-fs-backup") } switch { diff --git a/pkg/cmd/cli/nodeagent/node_agent.go b/pkg/cmd/cli/nodeagent/node_agent.go new file mode 100644 index 0000000000..7b0b6ebf0b --- /dev/null +++ b/pkg/cmd/cli/nodeagent/node_agent.go @@ -0,0 +1,37 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nodeagent + +import ( + "github.com/spf13/cobra" + + "github.com/vmware-tanzu/velero/pkg/client" +) + +func NewCommand(f client.Factory) *cobra.Command { + c := &cobra.Command{ + Use: "node-agent", + Short: "Work with node-agent", + Long: "Work with node-agent", + } + + c.AddCommand( + NewServerCommand(f), + ) + + return c +} diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/nodeagent/server.go similarity index 98% rename from pkg/cmd/cli/restic/server.go rename to pkg/cmd/cli/nodeagent/server.go index e7b133ac69..fed76d1fa8 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package nodeagent import ( "context" @@ -74,15 +74,15 @@ func NewServerCommand(f client.Factory) *cobra.Command { command := &cobra.Command{ Use: "server", - Short: "Run the velero restic server", - Long: "Run the velero restic server", + Short: "Run the velero node-agent server", + Long: "Run the velero node-agent server", Hidden: true, Run: func(c *cobra.Command, args []string) { logLevel := logLevelFlag.Parse() logrus.Infof("Setting log-level to %s", strings.ToUpper(logLevel.String())) logger := logging.DefaultLogger(logLevel, formatFlag.Parse()) - logger.Infof("Starting Velero restic server %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) + logger.Infof("Starting Velero node-agent server %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) s, err := newResticServer(logger, f, defaultMetricsAddress) diff --git a/pkg/cmd/cli/restic/server_test.go b/pkg/cmd/cli/nodeagent/server_test.go similarity index 99% rename from pkg/cmd/cli/restic/server_test.go rename to pkg/cmd/cli/nodeagent/server_test.go index ff222e996f..469d5b8005 100644 --- a/pkg/cmd/cli/restic/server_test.go +++ b/pkg/cmd/cli/nodeagent/server_test.go @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package restic +package nodeagent import ( "context" diff --git a/pkg/cmd/cli/restic/restic.go b/pkg/cmd/cli/restic/restic.go index 39909c23c5..744cdbd5fa 100644 --- a/pkg/cmd/cli/restic/restic.go +++ b/pkg/cmd/cli/restic/restic.go @@ -32,7 +32,6 @@ func NewCommand(f client.Factory) *cobra.Command { c.AddCommand( repo.NewRepositoryCommand(f), - NewServerCommand(f), ) return c diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index fe712adc02..6338299c37 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -49,6 +49,8 @@ import ( runplugin "github.com/vmware-tanzu/velero/pkg/cmd/server/plugin" veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/features" + + "github.com/vmware-tanzu/velero/pkg/cmd/cli/nodeagent" ) func NewCommand(name string) *cobra.Command { @@ -103,6 +105,7 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre schedule.NewCommand(f), restore.NewCommand(f), server.NewCommand(f), + nodeagent.NewCommand(f), version.NewCommand(f), get.NewCommand(f), install.NewCommand(f), diff --git a/pkg/install/daemonset.go b/pkg/install/daemonset.go index fb88647632..7aca8c66db 100644 --- a/pkg/install/daemonset.go +++ b/pkg/install/daemonset.go @@ -43,19 +43,19 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { } - resticArgs := []string{ - "restic", + daemonSetArgs := []string{ + "node-agent", "server", } if len(c.features) > 0 { - resticArgs = append(resticArgs, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) + daemonSetArgs = append(daemonSetArgs, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) } userID := int64(0) mountPropagationMode := corev1.MountPropagationHostToContainer daemonSet := &appsv1.DaemonSet{ - ObjectMeta: objectMeta(namespace, "restic"), + ObjectMeta: objectMeta(namespace, "node-agent"), TypeMeta: metav1.TypeMeta{ Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), @@ -63,13 +63,13 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ - "name": "restic", + "name": "node-agent", }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: podLabels(c.labels, map[string]string{ - "name": "restic", + "name": "node-agent", }), Annotations: c.annotations, }, @@ -96,13 +96,13 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { }, Containers: []corev1.Container{ { - Name: "restic", + Name: "node-agent", Image: c.image, ImagePullPolicy: pullPolicy, Command: []string{ "/velero", }, - Args: resticArgs, + Args: daemonSetArgs, VolumeMounts: []corev1.VolumeMount{ { diff --git a/pkg/install/daemonset_test.go b/pkg/install/daemonset_test.go index db9976fe56..4a3d9f9aa6 100644 --- a/pkg/install/daemonset_test.go +++ b/pkg/install/daemonset_test.go @@ -26,7 +26,7 @@ import ( func TestDaemonSet(t *testing.T) { ds := DaemonSet("velero") - assert.Equal(t, "restic", ds.Spec.Template.Spec.Containers[0].Name) + assert.Equal(t, "node-agent", ds.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "velero", ds.ObjectMeta.Namespace) ds = DaemonSet("velero", WithImage("velero/velero:v0.11")) diff --git a/pkg/install/install.go b/pkg/install/install.go index e163be95ef..d378716ed5 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -207,7 +207,7 @@ func DeploymentIsReady(factory client.DynamicFactory, namespace string) (bool, e return isReady, err } -// DaemonSetIsReady will poll the kubernetes API server to ensure the restic daemonset is ready, i.e. that +// DaemonSetIsReady will poll the kubernetes API server to ensure the node-agent daemonset is ready, i.e. that // pods are scheduled and available on all of the desired nodes. func DaemonSetIsReady(factory client.DynamicFactory, namespace string) (bool, error) { gvk := schema.FromAPIVersionAndKind(appsv1.SchemeGroupVersion.String(), "DaemonSet") @@ -226,7 +226,7 @@ func DaemonSetIsReady(factory client.DynamicFactory, namespace string) (bool, er var readyObservations int32 err = wait.PollImmediate(time.Second, time.Minute, func() (bool, error) { - unstructuredDaemonSet, err := c.Get("restic", metav1.GetOptions{}) + unstructuredDaemonSet, err := c.Get("node-agent", metav1.GetOptions{}) if apierrors.IsNotFound(err) { return false, nil } else if err != nil { diff --git a/pkg/install/resources.go b/pkg/install/resources.go index aa9b5f237e..ddcce46684 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -31,15 +31,15 @@ import ( ) var ( - DefaultVeleroPodCPURequest = "500m" - DefaultVeleroPodMemRequest = "128Mi" - DefaultVeleroPodCPULimit = "1000m" - DefaultVeleroPodMemLimit = "512Mi" - DefaultResticPodCPURequest = "500m" - DefaultResticPodMemRequest = "512Mi" - DefaultResticPodCPULimit = "1000m" - DefaultResticPodMemLimit = "1Gi" - DefaultVeleroNamespace = "velero" + DefaultVeleroPodCPURequest = "500m" + DefaultVeleroPodMemRequest = "128Mi" + DefaultVeleroPodCPULimit = "1000m" + DefaultVeleroPodMemLimit = "512Mi" + DefaultNodeAgentPodCPURequest = "500m" + DefaultNodeAgentPodMemRequest = "512Mi" + DefaultNodeAgentPodCPULimit = "1000m" + DefaultNodeAgentPodMemLimit = "1Gi" + DefaultVeleroNamespace = "velero" ) func Labels() map[string]string { @@ -218,10 +218,10 @@ type VeleroOptions struct { PodLabels map[string]string ServiceAccountAnnotations map[string]string VeleroPodResources corev1.ResourceRequirements - ResticPodResources corev1.ResourceRequirements + NodeAgentPodResources corev1.ResourceRequirements SecretData []byte RestoreOnly bool - UseRestic bool + UseNodeAgent bool UseVolumeSnapshots bool BSLConfig map[string]string VSLConfig map[string]string @@ -311,12 +311,12 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { appendUnstructured(resources, deploy) - if o.UseRestic { + if o.UseNodeAgent { dsOpts := []podTemplateOption{ WithAnnotations(o.PodAnnotations), WithLabels(o.PodLabels), WithImage(o.Image), - WithResources(o.ResticPodResources), + WithResources(o.NodeAgentPodResources), WithSecret(secretPresent), } if len(o.Features) > 0 { diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index fcfd10931f..cf1d32fbb5 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -32,7 +32,7 @@ import ( const ( // daemonSet is the name of the Velero node agent daemonset. - daemonSet = "restic" + daemonSet = "node-agent" ) var ( diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index b40ec17216..fc0389bc55 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -87,7 +87,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps return errors.WithMessagef(err, "Failed to get Velero InstallOptions for plugin provider %s", veleroCfg.ObjectStoreProvider) } veleroInstallOptions.UseVolumeSnapshots = useVolumeSnapshots - veleroInstallOptions.UseRestic = !useVolumeSnapshots + veleroInstallOptions.UseNodeAgent = !useVolumeSnapshots veleroInstallOptions.Image = veleroCfg.VeleroImage veleroInstallOptions.Namespace = veleroCfg.VeleroNamespace veleroInstallOptions.UploaderType = veleroCfg.UploaderType @@ -172,8 +172,8 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption if len(options.Image) > 0 { args = append(args, "--image", options.Image) } - if options.UseRestic { - args = append(args, "--use-restic") + if options.UseNodeAgent { + args = append(args, "--use-node-agent") } if options.UseVolumeSnapshots { args = append(args, "--use-volume-snapshots") @@ -222,7 +222,7 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption return err } - return waitVeleroReady(ctx, namespace, options.UseRestic) + return waitVeleroReady(ctx, namespace, options.UseNodeAgent) } func createVelereResources(ctx context.Context, cli, namespace string, args []string, registryCredentialFile, resticHelperImage string) error { @@ -376,7 +376,7 @@ func toUnstructured(res interface{}) (unstructured.Unstructured, error) { return un, err } -func waitVeleroReady(ctx context.Context, namespace string, useRestic bool) error { +func waitVeleroReady(ctx context.Context, namespace string, useNodeAgent bool) error { fmt.Println("Waiting for Velero deployment to be ready.") // when doing upgrade by the "kubectl apply" the command "kubectl wait --for=condition=available deployment/velero -n velero --timeout=600s" returns directly // use "rollout status" instead to avoid this. For more detail information, refer to https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#complete-deployment @@ -386,25 +386,25 @@ func waitVeleroReady(ctx context.Context, namespace string, useRestic bool) erro return errors.Wrapf(err, "fail to wait for the velero deployment ready, stdout=%s, stderr=%s", stdout, stderr) } - if useRestic { - fmt.Println("Waiting for Velero restic daemonset to be ready.") + if useNodeAgent { + fmt.Println("Waiting for node-agent daemonset to be ready.") err := wait.PollImmediate(5*time.Second, 1*time.Minute, func() (bool, error) { - stdout, stderr, err := velerexec.RunCommand(exec.CommandContext(ctx, "kubectl", "get", "daemonset/restic", + stdout, stderr, err := velerexec.RunCommand(exec.CommandContext(ctx, "kubectl", "get", "daemonset/node-agent", "-o", "json", "-n", namespace)) if err != nil { - return false, errors.Wrapf(err, "failed to get the restic daemonset, stdout=%s, stderr=%s", stdout, stderr) + return false, errors.Wrapf(err, "failed to get the node-agent daemonset, stdout=%s, stderr=%s", stdout, stderr) } - restic := &apps.DaemonSet{} - if err = json.Unmarshal([]byte(stdout), restic); err != nil { - return false, errors.Wrapf(err, "failed to unmarshal the restic daemonset") + daemonset := &apps.DaemonSet{} + if err = json.Unmarshal([]byte(stdout), daemonset); err != nil { + return false, errors.Wrapf(err, "failed to unmarshal the node-agent daemonset") } - if restic.Status.DesiredNumberScheduled == restic.Status.NumberAvailable { + if daemonset.Status.DesiredNumberScheduled == daemonset.Status.NumberAvailable { return true, nil } return false, nil }) if err != nil { - return errors.Wrap(err, "fail to wait for the velero restic ready") + return errors.Wrap(err, "fail to wait for the node-agent ready") } } diff --git a/tilt-resources/examples/restic.yaml b/tilt-resources/examples/node-agent.yaml similarity index 93% rename from tilt-resources/examples/restic.yaml rename to tilt-resources/examples/node-agent.yaml index 86d8a6f06c..d5c10fc47e 100644 --- a/tilt-resources/examples/restic.yaml +++ b/tilt-resources/examples/node-agent.yaml @@ -5,22 +5,22 @@ metadata: creationTimestamp: null labels: component: velero - name: restic + name: node-agent namespace: velero spec: selector: matchLabels: - name: restic + name: node-agent template: metadata: creationTimestamp: null labels: component: velero - name: restic + name: node-agent spec: containers: - args: - - restic + - node-agent - server command: - /velero @@ -43,7 +43,7 @@ spec: value: /credentials/cloud image: velero/velero:latest imagePullPolicy: Always - name: restic + name: node-agent resources: {} volumeMounts: - mountPath: /host_pods From 1f6785275fbdbc264336040fc62cfcb6af29454b Mon Sep 17 00:00:00 2001 From: Wesley Hayutin Date: Mon, 10 Oct 2022 13:59:41 -0600 Subject: [PATCH 329/366] Fix CVE-2022-27191 https://nvd.nist.gov/vuln/detail/CVE-2022-27191 updates to: * golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd adds: * golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 Signed-off-by: Wesley Hayutin --- go.mod | 2 +- go.sum | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 75512d71ac..1bd1df74c4 100644 --- a/go.mod +++ b/go.mod @@ -123,7 +123,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee // indirect golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect diff --git a/go.sum b/go.sum index 75dc52473f..2d2c3aa26d 100644 --- a/go.sum +++ b/go.sum @@ -901,8 +901,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1001,6 +1002,7 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= From 45de8a782fd85d6042cf5aa7057f1972f49131aa Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 12 Oct 2022 12:41:50 -0400 Subject: [PATCH 330/366] moved RIA execute input/output structs back to velero package The RIA refactoring moved velero.RestoreItemAction into a separate (restoreitemaction) v1 package. Unfortunately, this change would require plugins to make code changes to locate the RestoreItemActionExecuteInput and RestoreItemActionExecuteOutput structs. This commit restores those structs to the original velero package, leaving just the RestoreItemAction interface in the new v1 package. Signed-off-by: Scott Seago --- changelogs/unreleased/5441-sseago | 1 + .../v1/restartable_restore_item_action.go | 2 +- .../restartable_restore_item_action_test.go | 5 +- .../framework/restore_item_action_client.go | 4 +- .../framework/restore_item_action_server.go | 2 +- .../restoreitemaction/v1/RestoreItemAction.go | 11 ++-- .../velero/restore_item_action_shared.go | 62 +++++++++++++++++++ .../v1/restore_item_action.go | 44 +------------ pkg/restore/add_pv_from_pvc_action.go | 7 +-- pkg/restore/add_pv_from_pvc_action_test.go | 3 +- pkg/restore/add_pvc_from_pod_action.go | 5 +- pkg/restore/add_pvc_from_pod_action_test.go | 3 +- pkg/restore/admissionwebhook_config_action.go | 9 ++- .../admissionwebhook_config_action_test.go | 4 +- pkg/restore/apiservice_action.go | 5 +- pkg/restore/apiservice_action_test.go | 4 +- pkg/restore/change_pvc_node_selector.go | 15 +++-- pkg/restore/change_pvc_node_selector_test.go | 4 +- pkg/restore/change_storageclass_action.go | 9 ++- .../change_storageclass_action_test.go | 4 +- pkg/restore/clusterrolebinding_action.go | 7 +-- pkg/restore/clusterrolebinding_action_test.go | 3 +- .../crd_v1_preserve_unknown_fields_action.go | 7 +-- ..._v1_preserve_unknown_fields_action_test.go | 4 +- pkg/restore/init_restorehook_pod_action.go | 5 +- .../init_restorehook_pod_action_test.go | 4 +- pkg/restore/job_action.go | 5 +- pkg/restore/job_action_test.go | 4 +- pkg/restore/pod_action.go | 5 +- pkg/restore/pod_action_test.go | 3 +- pkg/restore/restic_restore_action.go | 7 +-- pkg/restore/restic_restore_action_test.go | 4 +- pkg/restore/restore.go | 2 +- pkg/restore/restore_test.go | 36 +++++------ pkg/restore/rolebinding_action.go | 7 +-- pkg/restore/rolebinding_action_test.go | 3 +- pkg/restore/service_account_action.go | 5 +- pkg/restore/service_account_action_test.go | 3 +- pkg/restore/service_action.go | 5 +- pkg/restore/service_action_test.go | 4 +- 40 files changed, 162 insertions(+), 164 deletions(-) create mode 100644 changelogs/unreleased/5441-sseago create mode 100644 pkg/plugin/velero/restore_item_action_shared.go diff --git a/changelogs/unreleased/5441-sseago b/changelogs/unreleased/5441-sseago new file mode 100644 index 0000000000..fd8f7b7d21 --- /dev/null +++ b/changelogs/unreleased/5441-sseago @@ -0,0 +1 @@ +moved RIA execute input/output structs back to velero package diff --git a/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go index a6b595544d..28b319eca1 100644 --- a/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go +++ b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action.go @@ -99,7 +99,7 @@ func (r RestartableRestoreItemAction) AppliesTo() (velero.ResourceSelector, erro } // Execute restarts the plugin's process if needed, then delegates the call. -func (r *RestartableRestoreItemAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (r *RestartableRestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { delegate, err := r.getDelegate() if err != nil { return nil, err diff --git a/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go index 55a0db2781..d16e3bb1fe 100644 --- a/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go +++ b/pkg/plugin/clientmgmt/restoreitemaction/v1/restartable_restore_item_action_test.go @@ -30,7 +30,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/restoreitemaction/v1" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) func TestRestartableGetRestoreItemAction(t *testing.T) { @@ -108,13 +107,13 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) { }, } - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: pv, ItemFromBackup: pv, Restore: new(v1.Restore), } - output := &riav1.RestoreItemActionExecuteOutput{ + output := &velero.RestoreItemActionExecuteOutput{ UpdatedItem: &unstructured.Unstructured{ Object: map[string]interface{}{ "color": "green", diff --git a/pkg/plugin/framework/restore_item_action_client.go b/pkg/plugin/framework/restore_item_action_client.go index 35bafa0893..75490d4c2f 100644 --- a/pkg/plugin/framework/restore_item_action_client.go +++ b/pkg/plugin/framework/restore_item_action_client.go @@ -73,7 +73,7 @@ func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, erro }, nil } -func (c *RestoreItemActionGRPCClient) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { itemJSON, err := json.Marshal(input.Item.UnstructuredContent()) if err != nil { return nil, errors.WithStack(err) @@ -120,7 +120,7 @@ func (c *RestoreItemActionGRPCClient) Execute(input *riav1.RestoreItemActionExec additionalItems = append(additionalItems, newItem) } - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: &updatedItem, AdditionalItems: additionalItems, SkipRestore: res.SkipRestore, diff --git a/pkg/plugin/framework/restore_item_action_server.go b/pkg/plugin/framework/restore_item_action_server.go index 924929c975..bdc149c480 100644 --- a/pkg/plugin/framework/restore_item_action_server.go +++ b/pkg/plugin/framework/restore_item_action_server.go @@ -108,7 +108,7 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re return nil, common.NewGRPCError(errors.WithStack(err)) } - executeOutput, err := impl.Execute(&riav1.RestoreItemActionExecuteInput{ + executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{ Item: &item, ItemFromBackup: &itemFromBackup, Restore: &restoreObj, diff --git a/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go b/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go index 5bf33ed851..1bacf7b161 100644 --- a/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go +++ b/pkg/plugin/velero/mocks/restoreitemaction/v1/RestoreItemAction.go @@ -20,7 +20,6 @@ package v1 import ( mock "github.com/stretchr/testify/mock" velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // RestoreItemAction is an autogenerated mock type for the RestoreItemAction type @@ -50,20 +49,20 @@ func (_m *RestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { } // Execute provides a mock function with given fields: input -func (_m *RestoreItemAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (_m *RestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { ret := _m.Called(input) - var r0 *riav1.RestoreItemActionExecuteOutput - if rf, ok := ret.Get(0).(func(*riav1.RestoreItemActionExecuteInput) *riav1.RestoreItemActionExecuteOutput); ok { + var r0 *velero.RestoreItemActionExecuteOutput + if rf, ok := ret.Get(0).(func(*velero.RestoreItemActionExecuteInput) *velero.RestoreItemActionExecuteOutput); ok { r0 = rf(input) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*riav1.RestoreItemActionExecuteOutput) + r0 = ret.Get(0).(*velero.RestoreItemActionExecuteOutput) } } var r1 error - if rf, ok := ret.Get(1).(func(*riav1.RestoreItemActionExecuteInput) error); ok { + if rf, ok := ret.Get(1).(func(*velero.RestoreItemActionExecuteInput) error); ok { r1 = rf(input) } else { r1 = ret.Error(1) diff --git a/pkg/plugin/velero/restore_item_action_shared.go b/pkg/plugin/velero/restore_item_action_shared.go new file mode 100644 index 0000000000..2714d69ab6 --- /dev/null +++ b/pkg/plugin/velero/restore_item_action_shared.go @@ -0,0 +1,62 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package velero + +import ( + "k8s.io/apimachinery/pkg/runtime" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// RestoreItemActionExecuteInput contains the input parameters for the ItemAction's Execute function. +type RestoreItemActionExecuteInput struct { + // Item is the item being restored. It is likely different from the pristine backed up version + // (metadata reset, changed by various restore item action plugins, etc.). + Item runtime.Unstructured + // ItemFromBackup is the item taken from the pristine backed up version of resource. + ItemFromBackup runtime.Unstructured + // Restore is the representation of the restore resource processed by Velero. + Restore *api.Restore +} + +// RestoreItemActionExecuteOutput contains the output variables for the ItemAction's Execution function. +type RestoreItemActionExecuteOutput struct { + // UpdatedItem is the item being restored mutated by ItemAction. + UpdatedItem runtime.Unstructured + + // AdditionalItems is a list of additional related items that should + // be restored. + AdditionalItems []ResourceIdentifier + + // SkipRestore tells velero to stop executing further actions + // on this item, and skip the restore step. When this field's + // value is true, AdditionalItems will be ignored. + SkipRestore bool +} + +// NewRestoreItemActionExecuteOutput creates a new RestoreItemActionExecuteOutput +func NewRestoreItemActionExecuteOutput(item runtime.Unstructured) *RestoreItemActionExecuteOutput { + return &RestoreItemActionExecuteOutput{ + UpdatedItem: item, + } +} + +// WithoutRestore returns SkipRestore for RestoreItemActionExecuteOutput +func (r *RestoreItemActionExecuteOutput) WithoutRestore() *RestoreItemActionExecuteOutput { + r.SkipRestore = true + return r +} diff --git a/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go b/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go index a50f3021f0..3398af9e1e 100644 --- a/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go +++ b/pkg/plugin/velero/restoreitemaction/v1/restore_item_action.go @@ -17,9 +17,6 @@ limitations under the License. package v1 import ( - "k8s.io/apimachinery/pkg/runtime" - - api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) @@ -36,44 +33,5 @@ type RestoreItemAction interface { // related items that should be restored, a warning (which will be logged but will not prevent // the item from being restored) or error (which will be logged and will prevent the item // from being restored) if applicable. - Execute(input *RestoreItemActionExecuteInput) (*RestoreItemActionExecuteOutput, error) -} - -// RestoreItemActionExecuteInput contains the input parameters for the ItemAction's Execute function. -type RestoreItemActionExecuteInput struct { - // Item is the item being restored. It is likely different from the pristine backed up version - // (metadata reset, changed by various restore item action plugins, etc.). - Item runtime.Unstructured - // ItemFromBackup is the item taken from the pristine backed up version of resource. - ItemFromBackup runtime.Unstructured - // Restore is the representation of the restore resource processed by Velero. - Restore *api.Restore -} - -// RestoreItemActionExecuteOutput contains the output variables for the ItemAction's Execution function. -type RestoreItemActionExecuteOutput struct { - // UpdatedItem is the item being restored mutated by ItemAction. - UpdatedItem runtime.Unstructured - - // AdditionalItems is a list of additional related items that should - // be restored. - AdditionalItems []velero.ResourceIdentifier - - // SkipRestore tells velero to stop executing further actions - // on this item, and skip the restore step. When this field's - // value is true, AdditionalItems will be ignored. - SkipRestore bool -} - -// NewRestoreItemActionExecuteOutput creates a new RestoreItemActionExecuteOutput -func NewRestoreItemActionExecuteOutput(item runtime.Unstructured) *RestoreItemActionExecuteOutput { - return &RestoreItemActionExecuteOutput{ - UpdatedItem: item, - } -} - -// WithoutRestore returns SkipRestore for RestoreItemActionExecuteOutput -func (r *RestoreItemActionExecuteOutput) WithoutRestore() *RestoreItemActionExecuteOutput { - r.SkipRestore = true - return r + Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) } diff --git a/pkg/restore/add_pv_from_pvc_action.go b/pkg/restore/add_pv_from_pvc_action.go index 147ad553f4..04c992357f 100644 --- a/pkg/restore/add_pv_from_pvc_action.go +++ b/pkg/restore/add_pv_from_pvc_action.go @@ -24,7 +24,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type AddPVFromPVCAction struct { @@ -41,7 +40,7 @@ func (a *AddPVFromPVCAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *AddPVFromPVCAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *AddPVFromPVCAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing AddPVFromPVCAction") // use input.ItemFromBackup because we need to look at status fields, which have already been @@ -54,7 +53,7 @@ func (a *AddPVFromPVCAction) Execute(input *riav1.RestoreItemActionExecuteInput) // TODO: consolidate this logic in a helper function to share with backup_pv_action.go if pvc.Status.Phase != corev1api.ClaimBound || pvc.Spec.VolumeName == "" { a.logger.Info("PVC is not bound or its volume name is empty") - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -65,7 +64,7 @@ func (a *AddPVFromPVCAction) Execute(input *riav1.RestoreItemActionExecuteInput) } a.logger.Infof("Adding PV %s as an additional item to restore", pvc.Spec.VolumeName) - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{pv}, }, nil diff --git a/pkg/restore/add_pv_from_pvc_action_test.go b/pkg/restore/add_pv_from_pvc_action_test.go index f8b5b5aaba..b4ceb90a43 100644 --- a/pkg/restore/add_pv_from_pvc_action_test.go +++ b/pkg/restore/add_pv_from_pvc_action_test.go @@ -27,7 +27,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -90,7 +89,7 @@ func TestAddPVFromPVCActionExecute(t *testing.T) { action := &AddPVFromPVCAction{logger: velerotest.NewLogger()} - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: itemData}, ItemFromBackup: &unstructured.Unstructured{Object: itemFromBackupData}, } diff --git a/pkg/restore/add_pvc_from_pod_action.go b/pkg/restore/add_pvc_from_pod_action.go index b7bd60a34c..70f33d985d 100644 --- a/pkg/restore/add_pvc_from_pod_action.go +++ b/pkg/restore/add_pvc_from_pod_action.go @@ -24,7 +24,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type AddPVCFromPodAction struct { @@ -41,7 +40,7 @@ func (a *AddPVCFromPodAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *AddPVCFromPodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *AddPVCFromPodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing AddPVCFromPodAction") var pod corev1api.Pod @@ -64,7 +63,7 @@ func (a *AddPVCFromPodAction) Execute(input *riav1.RestoreItemActionExecuteInput }) } - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: additionalItems, }, nil diff --git a/pkg/restore/add_pvc_from_pod_action_test.go b/pkg/restore/add_pvc_from_pod_action_test.go index 529751b379..b409e5d6ef 100644 --- a/pkg/restore/add_pvc_from_pod_action_test.go +++ b/pkg/restore/add_pvc_from_pod_action_test.go @@ -28,7 +28,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -101,7 +100,7 @@ func TestAddPVCFromPodActionExecute(t *testing.T) { action := &AddPVCFromPodAction{logger: velerotest.NewLogger()} - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: itemData}, } diff --git a/pkg/restore/admissionwebhook_config_action.go b/pkg/restore/admissionwebhook_config_action.go index 68fa089888..8fd5c1693e 100644 --- a/pkg/restore/admissionwebhook_config_action.go +++ b/pkg/restore/admissionwebhook_config_action.go @@ -23,7 +23,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // AdmissionWebhookConfigurationAction is a RestoreItemAction plugin applicable to mutatingwebhookconfiguration and @@ -47,7 +46,7 @@ func (a *AdmissionWebhookConfigurationAction) AppliesTo() (velero.ResourceSelect // Execute will reset the value of "sideEffects" attribute of each item in the "webhooks" list to "None" if they are invalid values for // v1, such as "Unknown" or "Some" -func (a *AdmissionWebhookConfigurationAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ChangeStorageClassAction") defer a.logger.Info("Done executing ChangeStorageClassAction") @@ -60,7 +59,7 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *riav1.RestoreItemAc logger := a.logger.WithField("resource_name", name) if apiVersion != "admissionregistration.k8s.io/v1" { logger.Infof("unable to handle api version: %s, skip", apiVersion) - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } webhooks, ok, err := unstructured.NestedSlice(item.UnstructuredContent(), "webhooks") if err != nil { @@ -68,7 +67,7 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *riav1.RestoreItemAc } if !ok { logger.Info("webhooks is not set, skip") - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } newWebhooks := make([]interface{}, 0) for i, entry := range webhooks { @@ -86,5 +85,5 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *riav1.RestoreItemAc newWebhooks = append(newWebhooks, obj) } item.UnstructuredContent()["webhooks"] = newWebhooks - return riav1.NewRestoreItemActionExecuteOutput(item), nil + return velero.NewRestoreItemActionExecuteOutput(item), nil } diff --git a/pkg/restore/admissionwebhook_config_action_test.go b/pkg/restore/admissionwebhook_config_action_test.go index a6548ae586..c6c31d2219 100644 --- a/pkg/restore/admissionwebhook_config_action_test.go +++ b/pkg/restore/admissionwebhook_config_action_test.go @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -163,7 +163,7 @@ func TestNewAdmissionWebhookConfigurationActionExecute(t *testing.T) { t.Run(tt.name, func(t *testing.T) { o := map[string]interface{}{} json.Unmarshal([]byte(tt.itemJSON), &o) - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: o, }, diff --git a/pkg/restore/apiservice_action.go b/pkg/restore/apiservice_action.go index 0d9568c0b8..7f817a59e0 100644 --- a/pkg/restore/apiservice_action.go +++ b/pkg/restore/apiservice_action.go @@ -21,7 +21,6 @@ import ( "k8s.io/kube-aggregator/pkg/controllers/autoregister" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type APIServiceAction struct { @@ -43,10 +42,10 @@ func (a *APIServiceAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *APIServiceAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *APIServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing APIServiceAction") defer a.logger.Info("Done executing APIServiceAction") a.logger.Infof("Skipping restore of APIService as it is managed by Kubernetes") - return riav1.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil } diff --git a/pkg/restore/apiservice_action_test.go b/pkg/restore/apiservice_action_test.go index 1f9baeae92..81f4a6171c 100644 --- a/pkg/restore/apiservice_action_test.go +++ b/pkg/restore/apiservice_action_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -40,7 +40,7 @@ func TestAPIServiceActionExecuteSkipsRestore(t *testing.T) { require.NoError(t, err) action := NewAPIServiceAction(velerotest.NewLogger()) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredAPIService}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredAPIService}, }) diff --git a/pkg/restore/change_pvc_node_selector.go b/pkg/restore/change_pvc_node_selector.go index 23a3c20c90..d281d318f7 100644 --- a/pkg/restore/change_pvc_node_selector.go +++ b/pkg/restore/change_pvc_node_selector.go @@ -28,7 +28,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ChangePVCNodeSelectorAction updates/reset PVC's node selector @@ -62,23 +61,23 @@ func (p *ChangePVCNodeSelectorAction) AppliesTo() (velero.ResourceSelector, erro // Execute updates the pvc's selected-node annotation: // a) if node mapping found in the config map for the plugin // b) if node mentioned in annotation doesn't exist -func (p *ChangePVCNodeSelectorAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { p.logger.Info("Executing ChangePVCNodeSelectorAction") defer p.logger.Info("Done executing ChangePVCNodeSelectorAction") typeAcc, err := meta.TypeAccessor(input.Item) if err != nil { - return &riav1.RestoreItemActionExecuteOutput{}, err + return &velero.RestoreItemActionExecuteOutput{}, err } metadata, err := meta.Accessor(input.Item) if err != nil { - return &riav1.RestoreItemActionExecuteOutput{}, err + return &velero.RestoreItemActionExecuteOutput{}, err } annotations := metadata.GetAnnotations() if annotations == nil { - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } log := p.logger.WithFields(map[string]interface{}{ @@ -91,7 +90,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *riav1.RestoreItemActionExec node, ok := annotations["volume.kubernetes.io/selected-node"] if !ok { log.Debug("PVC doesn't have node selector") - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } // fetch node mapping from configMap @@ -106,7 +105,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *riav1.RestoreItemActionExec annotations["volume.kubernetes.io/selected-node"] = newNode metadata.SetAnnotations(annotations) log.Infof("Updating selected-node to %s from %s", newNode, node) - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } // configMap doesn't have node-mapping @@ -126,7 +125,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *riav1.RestoreItemActionExec } } - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } func getNewNodeFromConfigMap(client corev1client.ConfigMapInterface, node string) (string, error) { diff --git a/pkg/restore/change_pvc_node_selector_test.go b/pkg/restore/change_pvc_node_selector_test.go index 92a6f4b311..8be3051bac 100644 --- a/pkg/restore/change_pvc_node_selector_test.go +++ b/pkg/restore/change_pvc_node_selector_test.go @@ -31,7 +31,7 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/vmware-tanzu/velero/pkg/builder" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // TestChangePVCNodeSelectorActionExecute runs the ChangePVCNodeSelectorAction's Execute @@ -146,7 +146,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pvc) require.NoError(t, err) - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredMap, }, diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index 3349cc7925..a02b7c484e 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -31,7 +31,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ChangeStorageClassAction updates a PV or PVC's storage class name @@ -65,7 +64,7 @@ func (a *ChangeStorageClassAction) AppliesTo() (velero.ResourceSelector, error) // Execute updates the item's spec.storageClassName if a mapping is found // in the config map for the plugin. -func (a *ChangeStorageClassAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ChangeStorageClassAction") defer a.logger.Info("Done executing ChangeStorageClassAction") @@ -77,7 +76,7 @@ func (a *ChangeStorageClassAction) Execute(input *riav1.RestoreItemActionExecute if config == nil || len(config.Data) == 0 { a.logger.Debug("No storage class mappings found") - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } obj, ok := input.Item.(*unstructured.Unstructured) @@ -129,7 +128,7 @@ func (a *ChangeStorageClassAction) Execute(input *riav1.RestoreItemActionExecute if err != nil { return nil, err } else if !exists { - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } log.Infof("Updating item's storage class name to %s", newStorageClass) @@ -138,7 +137,7 @@ func (a *ChangeStorageClassAction) Execute(input *riav1.RestoreItemActionExecute return nil, errors.Wrap(err, "unable to set item's spec.storageClassName") } } - return riav1.NewRestoreItemActionExecuteOutput(obj), nil + return velero.NewRestoreItemActionExecuteOutput(obj), nil } func (a *ChangeStorageClassAction) isStorageClassExist(log *logrus.Entry, storageClass *string, cm *corev1.ConfigMap) (exists bool, newStorageClass string, err error) { diff --git a/pkg/restore/change_storageclass_action_test.go b/pkg/restore/change_storageclass_action_test.go index 12a663075f..65de052db2 100644 --- a/pkg/restore/change_storageclass_action_test.go +++ b/pkg/restore/change_storageclass_action_test.go @@ -32,7 +32,7 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/vmware-tanzu/velero/pkg/builder" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // TestChangeStorageClassActionExecute runs the ChangeStorageClassAction's Execute @@ -245,7 +245,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pvOrPvcOrSTS) require.NoError(t, err) - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredMap, }, diff --git a/pkg/restore/clusterrolebinding_action.go b/pkg/restore/clusterrolebinding_action.go index eed52ba89c..851b13f098 100644 --- a/pkg/restore/clusterrolebinding_action.go +++ b/pkg/restore/clusterrolebinding_action.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // ClusterRoleBindingAction handle namespace remappings for role bindings @@ -42,10 +41,10 @@ func (a *ClusterRoleBindingAction) AppliesTo() (velero.ResourceSelector, error) }, nil } -func (a *ClusterRoleBindingAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *ClusterRoleBindingAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { namespaceMapping := input.Restore.Spec.NamespaceMapping if len(namespaceMapping) == 0 { - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil } clusterRoleBinding := new(rbac.ClusterRoleBinding) @@ -64,5 +63,5 @@ func (a *ClusterRoleBindingAction) Execute(input *riav1.RestoreItemActionExecute return nil, errors.WithStack(err) } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/clusterrolebinding_action_test.go b/pkg/restore/clusterrolebinding_action_test.go index a68334565a..cea1c57871 100644 --- a/pkg/restore/clusterrolebinding_action_test.go +++ b/pkg/restore/clusterrolebinding_action_test.go @@ -28,7 +28,6 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -90,7 +89,7 @@ func TestClusterRoleBindingActionExecute(t *testing.T) { require.NoError(t, err) action := NewClusterRoleBindingAction(test.NewLogger()) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: roleBindingUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: roleBindingUnstructured}, Restore: &api.Restore{ diff --git a/pkg/restore/crd_v1_preserve_unknown_fields_action.go b/pkg/restore/crd_v1_preserve_unknown_fields_action.go index a9d8768056..f67d47910d 100644 --- a/pkg/restore/crd_v1_preserve_unknown_fields_action.go +++ b/pkg/restore/crd_v1_preserve_unknown_fields_action.go @@ -26,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // CRDV1PreserveUnknownFieldsAction will take a CRD and inspect it for the API version and the PreserveUnknownFields value. @@ -46,7 +45,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) AppliesTo() (velero.ResourceSelector, }, nil } -func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { c.logger.Info("Executing CRDV1PreserveUnknownFieldsAction") name, _, err := unstructured.NestedString(input.Item.UnstructuredContent(), "name") @@ -63,7 +62,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *riav1.RestoreItemActio // We don't want to "fix" anything in beta CRDs at the moment, just v1 versions with preserveunknownfields = true if version != "apiextensions.k8s.io/v1" { - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -103,7 +102,7 @@ func (c *CRDV1PreserveUnknownFieldsAction) Execute(input *riav1.RestoreItemActio return nil, errors.Wrap(err, "unable to convert crd to runtime.Unstructured") } - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: &unstructured.Unstructured{Object: res}, }, nil } diff --git a/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go b/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go index 3840918ad5..77045b596d 100644 --- a/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go +++ b/pkg/restore/crd_v1_preserve_unknown_fields_action_test.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/vmware-tanzu/velero/pkg/builder" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -48,6 +48,6 @@ func TestExecuteForACRDWithAnIntOnAFloat64FieldShouldWork(t *testing.T) { a := NewCRDV1PreserveUnknownFieldsAction(test.NewLogger()) - _, err = a.Execute(&riav1.RestoreItemActionExecuteInput{Item: &u}) + _, err = a.Execute(&velero.RestoreItemActionExecuteInput{Item: &u}) require.NoError(t, err) } diff --git a/pkg/restore/init_restorehook_pod_action.go b/pkg/restore/init_restorehook_pod_action.go index cb72e14d4f..f994d811de 100644 --- a/pkg/restore/init_restorehook_pod_action.go +++ b/pkg/restore/init_restorehook_pod_action.go @@ -24,7 +24,6 @@ import ( "github.com/vmware-tanzu/velero/internal/hook" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // InitRestoreHookPodAction is a RestoreItemAction plugin applicable to pods that runs @@ -46,7 +45,7 @@ func (a *InitRestoreHookPodAction) AppliesTo() (velero.ResourceSelector, error) } // Execute implements the RestoreItemAction plugin interface method. -func (a *InitRestoreHookPodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *InitRestoreHookPodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Infof("Executing InitRestoreHookPodAction") // handle any init container restore hooks for the pod restoreHooks, err := hook.GetRestoreHooksFromSpec(&input.Restore.Spec.Hooks) @@ -61,5 +60,5 @@ func (a *InitRestoreHookPodAction) Execute(input *riav1.RestoreItemActionExecute } a.logger.Infof("Returning from InitRestoreHookPodAction") - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: postHooksItem.UnstructuredContent()}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: postHooksItem.UnstructuredContent()}), nil } diff --git a/pkg/restore/init_restorehook_pod_action_test.go b/pkg/restore/init_restorehook_pod_action_test.go index 058865660a..c69d3c23f9 100644 --- a/pkg/restore/init_restorehook_pod_action_test.go +++ b/pkg/restore/init_restorehook_pod_action_test.go @@ -29,7 +29,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/kuberesource" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -125,7 +125,7 @@ func TestInitContainerRestoreHookPodActionExecute(t *testing.T) { unstructuredPod, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.obj) require.NoError(t, err) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredPod}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredPod}, Restore: tc.restore, diff --git a/pkg/restore/job_action.go b/pkg/restore/job_action.go index 4e20f9cc92..fbaf30b249 100644 --- a/pkg/restore/job_action.go +++ b/pkg/restore/job_action.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type JobAction struct { @@ -41,7 +40,7 @@ func (a *JobAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *JobAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *JobAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { job := new(batchv1api.Job) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), job); err != nil { return nil, errors.WithStack(err) @@ -57,5 +56,5 @@ func (a *JobAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1. return nil, errors.WithStack(err) } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/job_action_test.go b/pkg/restore/job_action_test.go index 0b15bc47f0..606dd089d8 100644 --- a/pkg/restore/job_action_test.go +++ b/pkg/restore/job_action_test.go @@ -27,7 +27,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -138,7 +138,7 @@ func TestJobActionExecute(t *testing.T) { unstructuredJob, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredJob}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredJob}, Restore: nil, diff --git a/pkg/restore/pod_action.go b/pkg/restore/pod_action.go index 35d9d75220..d4bdc13847 100644 --- a/pkg/restore/pod_action.go +++ b/pkg/restore/pod_action.go @@ -27,7 +27,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) type PodAction struct { @@ -44,7 +43,7 @@ func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *PodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *PodAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { pod := new(v1.Pod) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), pod); err != nil { return nil, errors.WithStack(err) @@ -87,7 +86,7 @@ func (a *PodAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1. if err != nil { return nil, errors.WithStack(err) } - restoreExecuteOutput := riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) + restoreExecuteOutput := velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}) if pod.Spec.PriorityClassName != "" { a.logger.Infof("Adding priorityclass %s to AdditionalItems", pod.Spec.PriorityClassName) restoreExecuteOutput.AdditionalItems = []velero.ResourceIdentifier{ diff --git a/pkg/restore/pod_action_test.go b/pkg/restore/pod_action_test.go index c8121df673..f1aa83c1a3 100644 --- a/pkg/restore/pod_action_test.go +++ b/pkg/restore/pod_action_test.go @@ -28,7 +28,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -230,7 +229,7 @@ func TestPodActionExecute(t *testing.T) { unstructuredPod, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredPod}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredPod}, Restore: nil, diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index bf7116b8b9..f2d8df9a1d 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -36,7 +36,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -67,7 +66,7 @@ func (a *ResticRestoreAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ResticRestoreAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ResticRestoreAction") defer a.logger.Info("Done executing ResticRestoreAction") @@ -100,7 +99,7 @@ func (a *ResticRestoreAction) Execute(input *riav1.RestoreItemActionExecuteInput volumeSnapshots := podvolume.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) if len(volumeSnapshots) == 0 { log.Debug("No restic backups found for pod") - return riav1.NewRestoreItemActionExecuteOutput(input.Item), nil + return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } log.Info("Restic backups for pod found") @@ -172,7 +171,7 @@ func (a *ResticRestoreAction) Execute(input *riav1.RestoreItemActionExecuteInput return nil, errors.Wrap(err, "unable to convert pod to runtime.Unstructured") } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } func getCommand(log logrus.FieldLogger, config *corev1.ConfigMap) []string { diff --git a/pkg/restore/restic_restore_action_test.go b/pkg/restore/restic_restore_action_test.go index 660c1fc66c..b218f4b88d 100644 --- a/pkg/restore/restic_restore_action_test.go +++ b/pkg/restore/restic_restore_action_test.go @@ -35,7 +35,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/buildinfo" velerofake "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -278,7 +278,7 @@ func TestResticRestoreActionExecute(t *testing.T) { unstructuredPodFromBackup = unstructuredPod } - input := &riav1.RestoreItemActionExecuteInput{ + input := &velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{ Object: unstructuredPod, }, diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 8705ae2127..02d882d955 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1152,7 +1152,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } ctx.log.Infof("Executing item action for %v", &groupResource) - executeOutput, err := action.RestoreItemAction.Execute(&riav1.RestoreItemActionExecuteInput{ + executeOutput, err := action.RestoreItemAction.Execute(&velero.RestoreItemActionExecuteInput{ Item: obj, ItemFromBackup: itemFromBackup, Restore: ctx.restore, diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index f1575bc185..46ef8cdede 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -1149,17 +1149,17 @@ func (a *recordResourcesAction) AppliesTo() (velero.ResourceSelector, error) { return a.selector, nil } -func (a *recordResourcesAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *recordResourcesAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { metadata, err := meta.Accessor(input.Item) if err != nil { - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: a.additionalItems, }, err } a.ids = append(a.ids, kubeutil.NamespaceAndName(metadata)) - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: a.additionalItems, }, nil @@ -1355,12 +1355,12 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { // function body at runtime. type pluggableAction struct { selector velero.ResourceSelector - executeFunc func(*riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) + executeFunc func(*velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) } -func (a *pluggableAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *pluggableAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { if a.executeFunc == nil { - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } @@ -1385,7 +1385,7 @@ func TestRestoreActionModifications(t *testing.T) { // method modifies the item being passed in by calling the 'modify' function on it. modifyingActionGetter := func(modify func(*unstructured.Unstructured)) *pluggableAction { return &pluggableAction{ - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { obj, ok := input.Item.(*unstructured.Unstructured) if !ok { return nil, errors.Errorf("unexpected type %T", input.Item) @@ -1394,7 +1394,7 @@ func TestRestoreActionModifications(t *testing.T) { res := obj.DeepCopy() modify(res) - return &riav1.RestoreItemActionExecuteOutput{ + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: res, }, nil }, @@ -1532,8 +1532,8 @@ func TestRestoreActionAdditionalItems(t *testing.T) { actions: []riav1.RestoreItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { - return &riav1.RestoreItemActionExecuteOutput{ + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.Pods, Namespace: "ns-2", Name: "pod-2"}, @@ -1554,8 +1554,8 @@ func TestRestoreActionAdditionalItems(t *testing.T) { apiResources: []*test.APIResource{test.Pods()}, actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { - return &riav1.RestoreItemActionExecuteOutput{ + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.Pods, Namespace: "ns-2", Name: "pod-2"}, @@ -1579,8 +1579,8 @@ func TestRestoreActionAdditionalItems(t *testing.T) { apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { - return &riav1.RestoreItemActionExecuteOutput{ + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, @@ -1605,8 +1605,8 @@ func TestRestoreActionAdditionalItems(t *testing.T) { apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { - return &riav1.RestoreItemActionExecuteOutput{ + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, @@ -1631,8 +1631,8 @@ func TestRestoreActionAdditionalItems(t *testing.T) { apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []riav1.RestoreItemAction{ &pluggableAction{ - executeFunc: func(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { - return &riav1.RestoreItemActionExecuteOutput{ + executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, AdditionalItems: []velero.ResourceIdentifier{ {GroupResource: kuberesource.PersistentVolumes, Name: "pv-1"}, diff --git a/pkg/restore/rolebinding_action.go b/pkg/restore/rolebinding_action.go index 55820c21f9..c402075a61 100644 --- a/pkg/restore/rolebinding_action.go +++ b/pkg/restore/rolebinding_action.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" ) // RoleBindingAction handle namespace remappings for role bindings @@ -42,10 +41,10 @@ func (a *RoleBindingAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *RoleBindingAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *RoleBindingAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { namespaceMapping := input.Restore.Spec.NamespaceMapping if len(namespaceMapping) == 0 { - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: input.Item.UnstructuredContent()}), nil } roleBinding := new(rbac.RoleBinding) @@ -64,5 +63,5 @@ func (a *RoleBindingAction) Execute(input *riav1.RestoreItemActionExecuteInput) return nil, errors.WithStack(err) } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/rolebinding_action_test.go b/pkg/restore/rolebinding_action_test.go index 2a62cf6d0d..8995df8c6d 100644 --- a/pkg/restore/rolebinding_action_test.go +++ b/pkg/restore/rolebinding_action_test.go @@ -28,7 +28,6 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -90,7 +89,7 @@ func TestRoleBindingActionExecute(t *testing.T) { require.NoError(t, err) action := NewRoleBindingAction(test.NewLogger()) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: roleBindingUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: roleBindingUnstructured}, Restore: &api.Restore{ diff --git a/pkg/restore/service_account_action.go b/pkg/restore/service_account_action.go index 424985a26e..252d9fc576 100644 --- a/pkg/restore/service_account_action.go +++ b/pkg/restore/service_account_action.go @@ -26,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -44,7 +43,7 @@ func (a *ServiceAccountAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ServiceAccountAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *ServiceAccountAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { a.logger.Info("Executing ServiceAccountAction") defer a.logger.Info("Done executing ServiceAccountAction") @@ -76,5 +75,5 @@ func (a *ServiceAccountAction) Execute(input *riav1.RestoreItemActionExecuteInpu return nil, errors.Wrap(err, "unable to convert serviceaccount to runtime.Unstructured") } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } diff --git a/pkg/restore/service_account_action_test.go b/pkg/restore/service_account_action_test.go index f8bc76fcc8..0bc976a0fa 100644 --- a/pkg/restore/service_account_action_test.go +++ b/pkg/restore/service_account_action_test.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -91,7 +90,7 @@ func TestServiceAccountActionExecute(t *testing.T) { require.NoError(t, err) action := NewServiceAccountAction(test.NewLogger()) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: saUnstructured}, ItemFromBackup: &unstructured.Unstructured{Object: saUnstructured}, Restore: nil, diff --git a/pkg/restore/service_action.go b/pkg/restore/service_action.go index d21690182e..0b22cf0959 100644 --- a/pkg/restore/service_action.go +++ b/pkg/restore/service_action.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -48,7 +47,7 @@ func (a *ServiceAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func (a *ServiceAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*riav1.RestoreItemActionExecuteOutput, error) { +func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { service := new(corev1api.Service) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), service); err != nil { return nil, errors.WithStack(err) @@ -73,7 +72,7 @@ func (a *ServiceAction) Execute(input *riav1.RestoreItemActionExecuteInput) (*ri return nil, errors.WithStack(err) } - return riav1.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil + return velero.NewRestoreItemActionExecuteOutput(&unstructured.Unstructured{Object: res}), nil } func deleteNodePorts(service *corev1api.Service) error { diff --git a/pkg/restore/service_action_test.go b/pkg/restore/service_action_test.go index ccdef0729a..59fef00a15 100644 --- a/pkg/restore/service_action_test.go +++ b/pkg/restore/service_action_test.go @@ -29,7 +29,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) @@ -377,7 +377,7 @@ func TestServiceActionExecute(t *testing.T) { unstructuredSvc, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj) require.NoError(t, err) - res, err := action.Execute(&riav1.RestoreItemActionExecuteInput{ + res, err := action.Execute(&velero.RestoreItemActionExecuteInput{ Item: &unstructured.Unstructured{Object: unstructuredSvc}, ItemFromBackup: &unstructured.Unstructured{Object: unstructuredSvc}, Restore: test.restore, From 5f7f69366c277e12713bc5a5d0eb0c9860e8aaef Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 12 Oct 2022 19:12:40 +0800 Subject: [PATCH 331/366] remove restic from code Signed-off-by: Lyndon-Li --- changelogs/unreleased/5444-lyndon | 1 + .../v1/pod_volume_operation_progress.go | 2 +- pkg/backup/backup.go | 62 ++++++------- pkg/backup/backup_test.go | 32 +++---- pkg/backup/item_backupper.go | 46 +++++----- pkg/backup/pvc_snapshot_tracker.go | 2 +- pkg/buildinfo/buildinfo.go | 2 +- pkg/cmd/cli/backuplocation/delete.go | 16 ++-- pkg/cmd/cli/install/install.go | 10 +-- pkg/cmd/cli/nodeagent/server.go | 26 +++--- pkg/cmd/cli/nodeagent/server_test.go | 2 +- pkg/cmd/server/server.go | 29 ++++-- pkg/cmd/server/server_test.go | 4 +- ...repo_printer.go => backup_repo_printer.go} | 8 +- pkg/cmd/util/output/output.go | 8 +- pkg/controller/backup_deletion_controller.go | 12 +-- .../backup_deletion_controller_test.go | 2 +- ...ler.go => backup_repository_controller.go} | 74 ++++++++-------- ...o => backup_repository_controller_test.go} | 34 +++---- pkg/controller/constants.go | 4 +- .../pod_volume_backup_controller.go | 4 +- .../pod_volume_backup_controller_test.go | 2 +- pkg/metrics/metrics.go | 64 +++++++------- pkg/podvolume/backupper.go | 6 +- pkg/podvolume/restorer.go | 2 +- pkg/podvolume/util_test.go | 88 +++++++++---------- pkg/repository/keys/keys.go | 8 +- pkg/repository/locker.go | 2 +- pkg/repository/manager.go | 6 +- pkg/restore/restore.go | 78 ++++++++-------- pkg/restore/restore_test.go | 16 ++-- tilt-resources/examples/tilt-settings.json | 2 +- 32 files changed, 333 insertions(+), 321 deletions(-) create mode 100644 changelogs/unreleased/5444-lyndon rename pkg/cmd/util/output/{restic_repo_printer.go => backup_repo_printer.go} (87%) rename pkg/controller/{restic_repository_controller.go => backup_repository_controller.go} (76%) rename pkg/controller/{restic_repository_controller_test.go => backup_repository_controller_test.go} (88%) diff --git a/changelogs/unreleased/5444-lyndon b/changelogs/unreleased/5444-lyndon new file mode 100644 index 0000000000..ef6466bf04 --- /dev/null +++ b/changelogs/unreleased/5444-lyndon @@ -0,0 +1 @@ +Remove irrational "Restic" names in Velero code after the PVBR refactor \ No newline at end of file diff --git a/pkg/apis/velero/v1/pod_volume_operation_progress.go b/pkg/apis/velero/v1/pod_volume_operation_progress.go index ceb67a87ea..e5b3344c7c 100644 --- a/pkg/apis/velero/v1/pod_volume_operation_progress.go +++ b/pkg/apis/velero/v1/pod_volume_operation_progress.go @@ -17,7 +17,7 @@ limitations under the License. package v1 // PodVolumeOperationProgress represents the progress of a -// PodVolumeBackup/Restore (restic) operation +// PodVolumeBackup/Restore operation type PodVolumeOperationProgress struct { // +optional TotalBytes int64 `json:"totalBytes,omitempty"` diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index ba20ed0d4a..cdb86bf656 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -71,15 +71,15 @@ type Backupper interface { // kubernetesBackupper implements Backupper. type kubernetesBackupper struct { - backupClient velerov1client.BackupsGetter - dynamicFactory client.DynamicFactory - discoveryHelper discovery.Helper - podCommandExecutor podexec.PodCommandExecutor - resticBackupperFactory podvolume.BackupperFactory - resticTimeout time.Duration - defaultVolumesToFsBackup bool - clientPageSize int - uploaderType string + backupClient velerov1client.BackupsGetter + dynamicFactory client.DynamicFactory + discoveryHelper discovery.Helper + podCommandExecutor podexec.PodCommandExecutor + podVolumeBackupperFactory podvolume.BackupperFactory + podVolumeTimeout time.Duration + defaultVolumesToFsBackup bool + clientPageSize int + uploaderType string } func (i *itemKey) String() string { @@ -102,22 +102,22 @@ func NewKubernetesBackupper( discoveryHelper discovery.Helper, dynamicFactory client.DynamicFactory, podCommandExecutor podexec.PodCommandExecutor, - resticBackupperFactory podvolume.BackupperFactory, - resticTimeout time.Duration, + podVolumeBackupperFactory podvolume.BackupperFactory, + podVolumeTimeout time.Duration, defaultVolumesToFsBackup bool, clientPageSize int, uploaderType string, ) (Backupper, error) { return &kubernetesBackupper{ - backupClient: backupClient, - discoveryHelper: discoveryHelper, - dynamicFactory: dynamicFactory, - podCommandExecutor: podCommandExecutor, - resticBackupperFactory: resticBackupperFactory, - resticTimeout: resticTimeout, - defaultVolumesToFsBackup: defaultVolumesToFsBackup, - clientPageSize: clientPageSize, - uploaderType: uploaderType, + backupClient: backupClient, + discoveryHelper: discoveryHelper, + dynamicFactory: dynamicFactory, + podCommandExecutor: podCommandExecutor, + podVolumeBackupperFactory: podVolumeBackupperFactory, + podVolumeTimeout: podVolumeTimeout, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, + clientPageSize: clientPageSize, + uploaderType: uploaderType, }, nil } @@ -228,7 +228,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, backupRequest.BackedUpItems = map[itemKey]struct{}{} - podVolumeTimeout := kb.resticTimeout + podVolumeTimeout := kb.podVolumeTimeout if val := backupRequest.Annotations[velerov1api.PodVolumeOperationTimeoutAnnotation]; val != "" { parsed, err := time.ParseDuration(val) if err != nil { @@ -241,9 +241,9 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, ctx, cancelFunc := context.WithTimeout(context.Background(), podVolumeTimeout) defer cancelFunc() - var resticBackupper podvolume.Backupper - if kb.resticBackupperFactory != nil { - resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup, kb.uploaderType) + var podVolumeBackupper podvolume.Backupper + if kb.podVolumeBackupperFactory != nil { + podVolumeBackupper, err = kb.podVolumeBackupperFactory.NewBackupper(ctx, backupRequest.Backup, kb.uploaderType) if err != nil { log.WithError(errors.WithStack(err)).Debugf("Error from NewBackupper") return errors.WithStack(err) @@ -278,13 +278,13 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, } itemBackupper := &itemBackupper{ - backupRequest: backupRequest, - tarWriter: tw, - dynamicFactory: kb.dynamicFactory, - discoveryHelper: kb.discoveryHelper, - resticBackupper: resticBackupper, - resticSnapshotTracker: newPVCSnapshotTracker(), - volumeSnapshotterGetter: volumeSnapshotterGetter, + backupRequest: backupRequest, + tarWriter: tw, + dynamicFactory: kb.dynamicFactory, + discoveryHelper: kb.discoveryHelper, + podVolumeBackupper: podVolumeBackupper, + podVolumeSnapshotTracker: newPVCSnapshotTracker(), + volumeSnapshotterGetter: volumeSnapshotterGetter, itemHookHandler: &hook.DefaultItemHookHandler{ PodCommandExecutor: kb.podCommandExecutor, }, diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 387c91c4ac..56cbfe2146 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2595,17 +2595,17 @@ func TestBackupWithHooks(t *testing.T) { } } -type fakeResticBackupperFactory struct{} +type fakePodVolumeBackupperFactory struct{} -func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup, string) (podvolume.Backupper, error) { - return &fakeResticBackupper{}, nil +func (f *fakePodVolumeBackupperFactory) NewBackupper(context.Context, *velerov1.Backup, string) (podvolume.Backupper, error) { + return &fakePodVolumeBackupper{}, nil } -type fakeResticBackupper struct{} +type fakePodVolumeBackupper struct{} // BackupPodVolumes returns one pod volume backup per entry in volumes, with namespace "velero" // and name "pvb---". -func (b *fakeResticBackupper) BackupPodVolumes(backup *velerov1.Backup, pod *corev1.Pod, volumes []string, _ logrus.FieldLogger) ([]*velerov1.PodVolumeBackup, []error) { +func (b *fakePodVolumeBackupper) BackupPodVolumes(backup *velerov1.Backup, pod *corev1.Pod, volumes []string, _ logrus.FieldLogger) ([]*velerov1.PodVolumeBackup, []error) { var res []*velerov1.PodVolumeBackup for _, vol := range volumes { pvb := builder.ForPodVolumeBackup("velero", fmt.Sprintf("pvb-%s-%s-%s", pod.Namespace, pod.Name, vol)).Result() @@ -2615,11 +2615,11 @@ func (b *fakeResticBackupper) BackupPodVolumes(backup *velerov1.Backup, pod *cor return res, nil } -// TestBackupWithRestic runs backups of pods that are annotated for restic backup, -// and ensures that the restic backupper is called, that the returned PodVolumeBackups -// are added to the Request object, and that when PVCs are backed up with restic, the +// TestBackupWithPodVolume runs backups of pods that are annotated for PodVolume backup, +// and ensures that the pod volume backupper is called, that the returned PodVolumeBackups +// are added to the Request object, and that when PVCs are backed up with PodVolume, the // claimed PVs are not also snapshotted using a VolumeSnapshotter. -func TestBackupWithRestic(t *testing.T) { +func TestBackupWithPodVolume(t *testing.T) { tests := []struct { name string backup *velerov1.Backup @@ -2629,7 +2629,7 @@ func TestBackupWithRestic(t *testing.T) { want []*velerov1.PodVolumeBackup }{ { - name: "a pod annotated for restic backup should result in pod volume backups being returned", + name: "a pod annotated for pod volume backup should result in pod volume backups being returned", backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( @@ -2641,7 +2641,7 @@ func TestBackupWithRestic(t *testing.T) { }, }, { - name: "when a PVC is used by two pods and annotated for restic backup on both, only one should be backed up", + name: "when a PVC is used by two pods and annotated for pod volume backup on both, only one should be backed up", backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( @@ -2662,7 +2662,7 @@ func TestBackupWithRestic(t *testing.T) { }, }, { - name: "when PVC pod volumes are backed up using restic, their claimed PVs are not also snapshotted", + name: "when PVC pod volumes are backed up using pod volume backup, their claimed PVs are not also snapshotted", backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( @@ -2707,7 +2707,7 @@ func TestBackupWithRestic(t *testing.T) { backupFile = bytes.NewBuffer([]byte{}) ) - h.backupper.resticBackupperFactory = new(fakeResticBackupperFactory) + h.backupper.podVolumeBackupperFactory = new(fakePodVolumeBackupperFactory) for _, resource := range tc.apiResources { h.addItems(t, resource) @@ -2786,9 +2786,9 @@ func newHarness(t *testing.T) *harness { discoveryHelper: discoveryHelper, // unsupported - podCommandExecutor: nil, - resticBackupperFactory: nil, - resticTimeout: 0, + podCommandExecutor: nil, + podVolumeBackupperFactory: nil, + podVolumeTimeout: 0, }, log: log, } diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 0e24fb32cf..aefbb85ce9 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -56,13 +56,13 @@ const ( // itemBackupper can back up individual items to a tar writer. type itemBackupper struct { - backupRequest *Request - tarWriter tarWriter - dynamicFactory client.DynamicFactory - discoveryHelper discovery.Helper - resticBackupper podvolume.Backupper - resticSnapshotTracker *pvcSnapshotTracker - volumeSnapshotterGetter VolumeSnapshotterGetter + backupRequest *Request + tarWriter tarWriter + dynamicFactory client.DynamicFactory + discoveryHelper discovery.Helper + podVolumeBackupper podvolume.Backupper + podVolumeSnapshotTracker *pvcSnapshotTracker + volumeSnapshotterGetter VolumeSnapshotterGetter itemHookHandler hook.ItemHookHandler snapshotLocationVolumeSnapshotters map[string]vsv1.VolumeSnapshotter @@ -137,9 +137,9 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr } var ( - backupErrs []error - pod *corev1api.Pod - resticVolumesToBackup []string + backupErrs []error + pod *corev1api.Pod + pvbVolumes []string ) if groupResource == kuberesource.Pods { @@ -154,21 +154,21 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. for _, volume := range podvolume.GetVolumesByPod(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToFsBackup)) { - if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { + if found, pvcName := ib.podVolumeSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, "pvcName": pvcName, - }).Info("Pod volume uses a persistent volume claim which has already been backed up with restic from another pod, skipping.") + }).Info("Pod volume uses a persistent volume claim which has already been backed up from another pod, skipping.") continue } - resticVolumesToBackup = append(resticVolumesToBackup, volume) + pvbVolumes = append(pvbVolumes, volume) } // track the volumes that are PVCs using the PVC snapshot tracker, so that when we backup PVCs/PVs // via an item action in the next step, we don't snapshot PVs that will have their data backed up - // with restic. - ib.resticSnapshotTracker.Track(pod, resticVolumesToBackup) + // with pod volume backup. + ib.podVolumeSnapshotTracker.Track(pod, pvbVolumes) } } @@ -207,7 +207,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr if groupResource == kuberesource.Pods && pod != nil { // this function will return partial results, so process podVolumeBackups // even if there are errors. - podVolumeBackups, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup) + podVolumeBackups, errs := ib.backupPodVolumes(log, pod, pvbVolumes) ib.backupRequest.PodVolumeBackups = append(ib.backupRequest.PodVolumeBackups, podVolumeBackups...) backupErrs = append(backupErrs, errs...) @@ -292,19 +292,19 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr return true, nil } -// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a list of PodVolumeBackups +// backupPodVolumes triggers pod volume backups of the specified pod volumes, and returns a list of PodVolumeBackups // for volumes that were successfully backed up, and a slice of any errors that were encountered. func (ib *itemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) ([]*velerov1api.PodVolumeBackup, []error) { if len(volumes) == 0 { return nil, nil } - if ib.resticBackupper == nil { - log.Warn("No restic backupper, not backing up pod's volumes") + if ib.podVolumeBackupper == nil { + log.Warn("No pod volume backupper, not backing up pod's volumes") return nil, nil } - return ib.resticBackupper.BackupPodVolumes(ib.backupRequest.Backup, pod, volumes, log) + return ib.podVolumeBackupper.BackupPodVolumes(ib.backupRequest.Backup, pod, volumes, log) } func (ib *itemBackupper) executeActions( @@ -423,11 +423,11 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie log = log.WithField("persistentVolume", pv.Name) - // If this PV is claimed, see if we've already taken a (restic) snapshot of the contents + // If this PV is claimed, see if we've already taken a (pod volume backup) snapshot of the contents // of this PV. If so, don't take a snapshot. if pv.Spec.ClaimRef != nil { - if ib.resticSnapshotTracker.Has(pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name) { - log.Info("Skipping snapshot of persistent volume because volume is being backed up with restic.") + if ib.podVolumeSnapshotTracker.Has(pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name) { + log.Info("Skipping snapshot of persistent volume because volume is being backed up with pod volume backup.") return nil } } diff --git a/pkg/backup/pvc_snapshot_tracker.go b/pkg/backup/pvc_snapshot_tracker.go index d96264dbd2..3f0cbaeec9 100644 --- a/pkg/backup/pvc_snapshot_tracker.go +++ b/pkg/backup/pvc_snapshot_tracker.go @@ -24,7 +24,7 @@ import ( ) // pvcSnapshotTracker keeps track of persistent volume claims that have been snapshotted -// with restic. +// with pod volume backup. type pvcSnapshotTracker struct { pvcs sets.String } diff --git a/pkg/buildinfo/buildinfo.go b/pkg/buildinfo/buildinfo.go index eca5f1b5aa..b0533d0dbf 100644 --- a/pkg/buildinfo/buildinfo.go +++ b/pkg/buildinfo/buildinfo.go @@ -33,7 +33,7 @@ var ( GitTreeState string // ImageRegistry is the image registry that this build of Velero should use by default to pull the - // Velero and Restic Restore Helper images from. + // Velero and Restore Helper images from. ImageRegistry string ) diff --git a/pkg/cmd/cli/backuplocation/delete.go b/pkg/cmd/cli/backuplocation/delete.go index 1222220f51..c04c74f42e 100644 --- a/pkg/cmd/cli/backuplocation/delete.go +++ b/pkg/cmd/cli/backuplocation/delete.go @@ -130,11 +130,11 @@ func Run(f client.Factory, o *cli.DeleteOptions) error { errs = append(errs, deleteErrs...) } - // Delete Restic repositories associated with the deleted BSL. - resticRepoList, err := findAssociatedResticRepos(kbClient, location.Name, f.Namespace()) + // Delete backup repositories associated with the deleted BSL. + backupRepoList, err := findAssociatedBackupRepos(kbClient, location.Name, f.Namespace()) if err != nil { - errs = append(errs, fmt.Errorf("find Restic repositories associated with BSL %q: %w", location.Name, err)) - } else if deleteErrs := deleteResticRepos(kbClient, resticRepoList); deleteErrs != nil { + errs = append(errs, fmt.Errorf("find backup repositories associated with BSL %q: %w", location.Name, err)) + } else if deleteErrs := deleteBackupRepos(kbClient, backupRepoList); deleteErrs != nil { errs = append(errs, deleteErrs...) } } @@ -151,7 +151,7 @@ func findAssociatedBackups(client kbclient.Client, bslName, ns string) (velerov1 return backups, err } -func findAssociatedResticRepos(client kbclient.Client, bslName, ns string) (velerov1api.BackupRepositoryList, error) { +func findAssociatedBackupRepos(client kbclient.Client, bslName, ns string) (velerov1api.BackupRepositoryList, error) { var repos velerov1api.BackupRepositoryList err := client.List(context.Background(), &repos, &kbclient.ListOptions{ Namespace: ns, @@ -172,14 +172,14 @@ func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []err return errs } -func deleteResticRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { +func deleteBackupRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { var errs []error for _, repo := range repos.Items { if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { - errs = append(errs, errors.WithStack(fmt.Errorf("delete Restic repository %q associated with deleted BSL: %w", repo.Name, err))) + errs = append(errs, errors.WithStack(fmt.Errorf("delete backup repository %q associated with deleted BSL: %w", repo.Name, err))) continue } - fmt.Printf("Restic repository associated with deleted BSL(s) %q deleted successfully.\n", repo.Name) + fmt.Printf("Backup repository associated with deleted BSL(s) %q deleted successfully.\n", repo.Name) } return errs } diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index f9cffa48fb..7c32e9604b 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -86,10 +86,10 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.SecretFile, "secret-file", o.SecretFile, "File containing credentials for backup and volume provider. If not specified, --no-secret must be used for confirmation. Optional.") flags.BoolVar(&o.NoSecret, "no-secret", o.NoSecret, "Flag indicating if a secret should be created. Must be used as confirmation if --secret-file is not provided. Optional.") flags.BoolVar(&o.NoDefaultBackupLocation, "no-default-backup-location", o.NoDefaultBackupLocation, "Flag indicating if a default backup location should be created. Must be used as confirmation if --bucket or --provider are not provided. Optional.") - flags.StringVar(&o.Image, "image", o.Image, "Image to use for the Velero and restic server pods. Optional.") + flags.StringVar(&o.Image, "image", o.Image, "Image to use for the Velero and node agent pods. Optional.") flags.StringVar(&o.Prefix, "prefix", o.Prefix, "Prefix under which all Velero data should be stored within the bucket. Optional.") - flags.Var(&o.PodAnnotations, "pod-annotations", "Annotations to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2") - flags.Var(&o.PodLabels, "pod-labels", "Labels to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2") + flags.Var(&o.PodAnnotations, "pod-annotations", "Annotations to add to the Velero and node agent pods. Optional. Format is key1=value1,key2=value2") + flags.Var(&o.PodLabels, "pod-labels", "Labels to add to the Velero and node agent pods. Optional. Format is key1=value1,key2=value2") flags.Var(&o.ServiceAccountAnnotations, "sa-annotations", "Annotations to add to the Velero ServiceAccount. Add iam.gke.io/gcp-service-account=[GSA_NAME]@[PROJECT_NAME].iam.gserviceaccount.com for workload identity. Optional. Format is key1=value1,key2=value2") flags.StringVar(&o.VeleroPodCPURequest, "velero-pod-cpu-request", o.VeleroPodCPURequest, `CPU request for Velero pod. A value of "0" is treated as unbounded. Optional.`) flags.StringVar(&o.VeleroPodMemRequest, "velero-pod-mem-request", o.VeleroPodMemRequest, `Memory request for Velero pod. A value of "0" is treated as unbounded. Optional.`) @@ -233,7 +233,7 @@ This is useful as a starting point for more customized installations. # velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 - # velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 --use-restic + # velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 --use-node-agent # velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket gcp-backups --secret-file ./gcp-creds.json --wait @@ -241,7 +241,7 @@ This is useful as a starting point for more customized installations. # velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket gcp-backups --secret-file ./gcp-creds.json --velero-pod-cpu-request=1000m --velero-pod-cpu-limit=5000m --velero-pod-mem-request=512Mi --velero-pod-mem-limit=1024Mi - # velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket gcp-backups --secret-file ./gcp-creds.json --restic-pod-cpu-request=1000m --restic-pod-cpu-limit=5000m --restic-pod-mem-request=512Mi --restic-pod-mem-limit=1024Mi + # velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket gcp-backups --secret-file ./gcp-creds.json --node-agent-pod-cpu-request=1000m --node-agent-pod-cpu-limit=5000m --node-agent-pod-mem-request=512Mi --node-agent-pod-mem-limit=1024Mi # velero install --provider azure --plugins velero/velero-plugin-for-microsoft-azure:v1.0.0 --bucket $BLOB_CONTAINER --secret-file ./credentials-velero --backup-location-config resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID[,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID] --snapshot-location-config apiTimeout=[,resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID]`, Run: func(c *cobra.Command, args []string) { diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index fed76d1fa8..373aa1161f 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -85,7 +85,7 @@ func NewServerCommand(f client.Factory) *cobra.Command { logger.Infof("Starting Velero node-agent server %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) - s, err := newResticServer(logger, f, defaultMetricsAddress) + s, err := newNodeAgentServer(logger, f, defaultMetricsAddress) cmd.CheckError(err) s.run() @@ -98,7 +98,7 @@ func NewServerCommand(f client.Factory) *cobra.Command { return command } -type resticServer struct { +type nodeAgentServer struct { logger logrus.FieldLogger ctx context.Context cancelFunc context.CancelFunc @@ -110,7 +110,7 @@ type resticServer struct { nodeName string } -func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAddress string) (*resticServer, error) { +func newNodeAgentServer(logger logrus.FieldLogger, factory client.Factory, metricAddress string) (*nodeAgentServer, error) { ctx, cancelFunc := context.WithCancel(context.Background()) clientConfig, err := factory.ClientConfig() @@ -142,7 +142,7 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAd return nil, err } - s := &resticServer{ + s := &nodeAgentServer{ logger: logger, ctx: ctx, cancelFunc: cancelFunc, @@ -166,20 +166,20 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory, metricAd return s, nil } -func (s *resticServer) run() { +func (s *nodeAgentServer) run() { signals.CancelOnShutdown(s.cancelFunc, s.logger) go func() { metricsMux := http.NewServeMux() metricsMux.Handle("/metrics", promhttp.Handler()) - s.logger.Infof("Starting metric server for restic at address [%s]", s.metricsAddress) + s.logger.Infof("Starting metric server for node agent at address [%s]", s.metricsAddress) if err := http.ListenAndServe(s.metricsAddress, metricsMux); err != nil { - s.logger.Fatalf("Failed to start metric server for restic at [%s]: %v", s.metricsAddress, err) + s.logger.Fatalf("Failed to start metric server for node agent at [%s]: %v", s.metricsAddress, err) } }() - s.metrics = metrics.NewResticServerMetrics() + s.metrics = metrics.NewPodVolumeMetrics() s.metrics.RegisterAllMetrics() - s.metrics.InitResticMetricsForNode(s.nodeName) + s.metrics.InitPodVolumeMetricsForNode(s.nodeName) s.markInProgressCRsFailed() @@ -228,7 +228,7 @@ func (s *resticServer) run() { // validatePodVolumesHostPath validates that the pod volumes path contains a // directory for each Pod running on this node -func (s *resticServer) validatePodVolumesHostPath(client kubernetes.Interface) error { +func (s *nodeAgentServer) validatePodVolumesHostPath(client kubernetes.Interface) error { files, err := s.fileSystem.ReadDir("/host_pods/") if err != nil { return errors.Wrap(err, "could not read pod volumes host path") @@ -275,7 +275,7 @@ func (s *resticServer) validatePodVolumesHostPath(client kubernetes.Interface) e // if there is a restarting during the reconciling of pvbs/pvrs/etc, these CRs may be stuck in progress status // markInProgressCRsFailed tries to mark the in progress CRs as failed when starting the server to avoid the issue -func (s *resticServer) markInProgressCRsFailed() { +func (s *nodeAgentServer) markInProgressCRsFailed() { // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here client, err := ctrlclient.New(s.mgr.GetConfig(), ctrlclient.Options{Scheme: s.mgr.GetScheme()}) if err != nil { @@ -288,7 +288,7 @@ func (s *resticServer) markInProgressCRsFailed() { s.markInProgressPVRsFailed(client) } -func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { +func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvbs := &velerov1api.PodVolumeBackupList{} if err := client.List(s.ctx, pvbs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumebackups") @@ -315,7 +315,7 @@ func (s *resticServer) markInProgressPVBsFailed(client ctrlclient.Client) { } } -func (s *resticServer) markInProgressPVRsFailed(client ctrlclient.Client) { +func (s *nodeAgentServer) markInProgressPVRsFailed(client ctrlclient.Client) { pvrs := &velerov1api.PodVolumeRestoreList{} if err := client.List(s.ctx, pvrs, &ctrlclient.MatchingFields{"metadata.namespace": s.namespace}); err != nil { s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") diff --git a/pkg/cmd/cli/nodeagent/server_test.go b/pkg/cmd/cli/nodeagent/server_test.go index 469d5b8005..f540ac4fb8 100644 --- a/pkg/cmd/cli/nodeagent/server_test.go +++ b/pkg/cmd/cli/nodeagent/server_test.go @@ -94,7 +94,7 @@ func Test_validatePodVolumesHostPath(t *testing.T) { } } - s := &resticServer{ + s := &nodeAgentServer{ logger: testutil.NewLogger(), fileSystem: fs, } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index ccf9f1edd1..238d155708 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -300,7 +300,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s // cancelFunc is not deferred here because if it was, then ctx would immediately // be cancelled once this function exited, making it useless to any informers using later. - // That, in turn, causes the velero server to halt when the first informer tries to use it (probably restic's). + // That, in turn, causes the velero server to halt when the first informer tries to use it. // Therefore, we must explicitly call it on the error paths in this function. ctx, cancelFunc := context.WithCancel(context.Background()) @@ -395,7 +395,9 @@ func (s *server) run() error { return err } - if err := s.initRestic(); err != nil { + s.checkNodeAgent() + + if err := s.initRepoManager(); err != nil { return err } @@ -499,7 +501,7 @@ func (s *server) veleroResourcesExist() error { // - Service accounts go before pods or controllers so pods can use them. // - Limit ranges go before pods or controllers so pods can use them. // - Pods go before controllers so they can be explicitly restored and potentially -// have restic restores run before controllers adopt the pods. +// have pod volume restores run before controllers adopt the pods. // - Replica sets go before deployments/other controllers so they can be explicitly // restored and be adopted by controllers. // - CAPI ClusterClasses go before Clusters. @@ -530,7 +532,16 @@ var defaultRestorePriorities = []string{ "clusterresourcesets.addons.cluster.x-k8s.io", } -func (s *server) initRestic() error { +func (s *server) checkNodeAgent() { + // warn if node agent does not exist + if err := nodeagent.IsRunning(s.ctx, s.kubeClient, s.namespace); err == nodeagent.DaemonsetNotFound { + s.logger.Warn("Velero node agent not found; pod volume backups/restores will not work until it's created") + } else if err != nil { + s.logger.WithError(errors.WithStack(err)).Warn("Error checking for existence of velero node agent") + } +} + +func (s *server) initRepoManager() error { // warn if node agent does not exist if err := nodeagent.IsRunning(s.ctx, s.kubeClient, s.namespace); err == nodeagent.DaemonsetNotFound { s.logger.Warn("Velero node agent not found; pod volume backups/restores will not work until it's created") @@ -664,7 +675,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string // By far, PodVolumeBackup, PodVolumeRestore, BackupStorageLocation controllers // are not included in --disable-controllers list. - // This is because of PVB and PVR are used by Restic DaemonSet, + // This is because of PVB and PVR are used by node agent DaemonSet, // and BSL controller is mandatory for Velero to work. enabledControllers := map[string]func() controllerRunInfo{ controller.Backup: backupControllerRunInfo, @@ -675,7 +686,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string controller.ServerStatusRequest: {}, controller.DownloadRequest: {}, controller.Schedule: {}, - controller.ResticRepo: {}, + controller.BackupRepo: {}, controller.BackupDeletion: {}, controller.GarbageCollection: {}, controller.BackupSync: {}, @@ -748,9 +759,9 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } } - if _, ok := enabledRuntimeControllers[controller.ResticRepo]; ok { - if err := controller.NewResticRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.repoMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { - s.logger.Fatal(err, "unable to create controller", "controller", controller.ResticRepo) + if _, ok := enabledRuntimeControllers[controller.BackupRepo]; ok { + if err := controller.NewBackupRepoReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.config.repoMaintenanceFrequency, s.repoManager).SetupWithManager(s.mgr); err != nil { + s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupRepo) } } diff --git a/pkg/cmd/server/server_test.go b/pkg/cmd/server/server_test.go index e8110793c7..ddf9fb9d37 100644 --- a/pkg/cmd/server/server_test.go +++ b/pkg/cmd/server/server_test.go @@ -94,7 +94,7 @@ func TestRemoveControllers(t *testing.T) { controller.BackupSync, controller.DownloadRequest, controller.GarbageCollection, - controller.ResticRepo, + controller.BackupRepo, controller.Restore, controller.Schedule, controller.ServerStatusRequest, @@ -130,7 +130,7 @@ func TestRemoveControllers(t *testing.T) { controller.ServerStatusRequest: {}, controller.Schedule: {}, controller.BackupDeletion: {}, - controller.ResticRepo: {}, + controller.BackupRepo: {}, controller.DownloadRequest: {}, } diff --git a/pkg/cmd/util/output/restic_repo_printer.go b/pkg/cmd/util/output/backup_repo_printer.go similarity index 87% rename from pkg/cmd/util/output/restic_repo_printer.go rename to pkg/cmd/util/output/backup_repo_printer.go index fd6766087f..1e68cb9499 100644 --- a/pkg/cmd/util/output/restic_repo_printer.go +++ b/pkg/cmd/util/output/backup_repo_printer.go @@ -24,7 +24,7 @@ import ( ) var ( - resticRepoColumns = []metav1.TableColumnDefinition{ + backupRepoColumns = []metav1.TableColumnDefinition{ // name needs Type and Format defined for the decorator to identify it: // https://github.com/kubernetes/kubernetes/blob/v1.15.3/pkg/printers/tableprinter.go#L204 {Name: "Name", Type: "string", Format: "name"}, @@ -33,16 +33,16 @@ var ( } ) -func printResticRepoList(list *v1.BackupRepositoryList) []metav1.TableRow { +func printBackupRepoList(list *v1.BackupRepositoryList) []metav1.TableRow { rows := make([]metav1.TableRow, 0, len(list.Items)) for i := range list.Items { - rows = append(rows, printResticRepo(&list.Items[i])...) + rows = append(rows, printBackupRepo(&list.Items[i])...) } return rows } -func printResticRepo(repo *v1.BackupRepository) []metav1.TableRow { +func printBackupRepo(repo *v1.BackupRepository) []metav1.TableRow { row := metav1.TableRow{ Object: runtime.RawExtension{Object: repo}, } diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index 1b0a992198..bfba88f15e 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -179,13 +179,13 @@ func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) { } case *velerov1api.BackupRepository: table = &metav1.Table{ - ColumnDefinitions: resticRepoColumns, - Rows: printResticRepo(obj.(*velerov1api.BackupRepository)), + ColumnDefinitions: backupRepoColumns, + Rows: printBackupRepo(obj.(*velerov1api.BackupRepository)), } case *velerov1api.BackupRepositoryList: table = &metav1.Table{ - ColumnDefinitions: resticRepoColumns, - Rows: printResticRepoList(obj.(*velerov1api.BackupRepositoryList)), + ColumnDefinitions: backupRepoColumns, + Rows: printBackupRepoList(obj.(*velerov1api.BackupRepositoryList)), } case *velerov1api.BackupStorageLocation: table = &metav1.Table{ diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 826470d703..371179e56c 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -50,7 +50,7 @@ import ( ) const ( - resticTimeout = time.Minute + snapshotDeleteTimeout = time.Minute deleteBackupRequestMaxAge = 24 * time.Hour ) @@ -302,8 +302,8 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } } - log.Info("Removing restic snapshots") - if deleteErrs := r.deleteResticSnapshots(ctx, backup); len(deleteErrs) > 0 { + log.Info("Removing pod volume snapshots") + if deleteErrs := r.deletePodVolumeSnapshots(ctx, backup); len(deleteErrs) > 0 { for _, err := range deleteErrs { errs = append(errs, err.Error()) } @@ -436,7 +436,7 @@ func (r *backupDeletionReconciler) deleteExistingDeletionRequests(ctx context.Co return errs } -func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, backup *velerov1api.Backup) []error { +func (r *backupDeletionReconciler) deletePodVolumeSnapshots(ctx context.Context, backup *velerov1api.Backup) []error { if r.repoMgr == nil { return nil } @@ -446,7 +446,7 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba return []error{err} } - ctx2, cancelFunc := context.WithTimeout(ctx, resticTimeout) + ctx2, cancelFunc := context.WithTimeout(ctx, snapshotDeleteTimeout) defer cancelFunc() var errs []error @@ -493,7 +493,7 @@ func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *vele return backup, nil } -// getSnapshotsInBackup returns a list of all restic snapshot ids associated with +// getSnapshotsInBackup returns a list of all pod volume snapshot ids associated with // a given Velero backup. func getSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]repository.SnapshotIdentifier, error) { podVolumeBackups := &velerov1api.PodVolumeBackupList{} diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 8604c90b83..62be34b0d2 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -91,7 +91,7 @@ func setupBackupDeletionControllerTest(t *testing.T, req *velerov1api.DeleteBack velerotest.NewLogger(), fakeClient, NewBackupTracker(), - nil, // restic repository manager + nil, // repository manager metrics.NewServerMetrics(), nil, // discovery helper func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/backup_repository_controller.go similarity index 76% rename from pkg/controller/restic_repository_controller.go rename to pkg/controller/backup_repository_controller.go index 7328cf78b8..f151a1b68b 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/backup_repository_controller.go @@ -39,7 +39,7 @@ const ( defaultMaintainFrequency = 7 * 24 * time.Hour ) -type ResticRepoReconciler struct { +type BackupRepoReconciler struct { client.Client namespace string logger logrus.FieldLogger @@ -48,9 +48,9 @@ type ResticRepoReconciler struct { repositoryManager repository.Manager } -func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client client.Client, - maintenanceFrequency time.Duration, repositoryManager repository.Manager) *ResticRepoReconciler { - c := &ResticRepoReconciler{ +func NewBackupRepoReconciler(namespace string, logger logrus.FieldLogger, client client.Client, + maintenanceFrequency time.Duration, repositoryManager repository.Manager) *BackupRepoReconciler { + c := &BackupRepoReconciler{ client, namespace, logger, @@ -62,7 +62,7 @@ func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client return c } -func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *BackupRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod, kube.PeriodicalEnqueueSourceOption{}) return ctrl.NewControllerManagedBy(mgr). For(&velerov1api.BackupRepository{}). @@ -70,20 +70,20 @@ func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.logger.WithField("resticRepo", req.String()) - resticRepo := &velerov1api.BackupRepository{} - if err := r.Get(ctx, req.NamespacedName, resticRepo); err != nil { +func (r *BackupRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.logger.WithField("backupRepo", req.String()) + backupRepo := &velerov1api.BackupRepository{} + if err := r.Get(ctx, req.NamespacedName, backupRepo); err != nil { if apierrors.IsNotFound(err) { - log.Warnf("restic repository %s in namespace %s is not found", req.Name, req.Namespace) + log.Warnf("backup repository %s in namespace %s is not found", req.Name, req.Namespace) return ctrl.Result{}, nil } - log.WithError(err).Error("error getting restic repository") + log.WithError(err).Error("error getting backup repository") return ctrl.Result{}, err } - if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { - if err := r.initializeRepo(ctx, resticRepo, log); err != nil { + if backupRepo.Status.Phase == "" || backupRepo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { + if err := r.initializeRepo(ctx, backupRepo, log); err != nil { log.WithError(err).Error("error initialize repository") return ctrl.Result{}, errors.WithStack(err) } @@ -95,22 +95,22 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) // this fails for any reason, it's non-critical so we still continue on to the // rest of the "process" logic. log.Debug("Checking repository for stale locks") - if err := r.repositoryManager.UnlockRepo(resticRepo); err != nil { + if err := r.repositoryManager.UnlockRepo(backupRepo); err != nil { log.WithError(err).Error("Error checking repository for stale locks") } - switch resticRepo.Status.Phase { + switch backupRepo.Status.Phase { case velerov1api.BackupRepositoryPhaseReady: - return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, log) + return ctrl.Result{}, r.runMaintenanceIfDue(ctx, backupRepo, log) case velerov1api.BackupRepositoryPhaseNotReady: - return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, log) + return ctrl.Result{}, r.checkNotReadyRepo(ctx, backupRepo, log) } return ctrl.Result{}, nil } -func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { - log.Info("Initializing restic repository") +func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { + log.Info("Initializing backup repository") // confirm the repo's BackupStorageLocation is valid loc := &velerov1api.BackupStorageLocation{} @@ -119,12 +119,12 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { - return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) + return r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) } repoIdentifier, err := repoconfig.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady @@ -135,7 +135,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Spec.ResticIdentifier = repoIdentifier if rr.Spec.MaintenanceFrequency.Duration <= 0 { @@ -146,16 +146,16 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } if err := ensureRepo(req, r.repositoryManager); err != nil { - return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) + return r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Phase = velerov1api.BackupRepositoryPhaseReady rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) } -func (r *ResticRepoReconciler) getRepositoryMaintenanceFrequency(req *velerov1api.BackupRepository) time.Duration { +func (r *BackupRepoReconciler) getRepositoryMaintenanceFrequency(req *velerov1api.BackupRepository) time.Duration { if r.maintenanceFrequency > 0 { r.logger.WithField("frequency", r.maintenanceFrequency).Info("Set user defined maintenance frequency") return r.maintenanceFrequency @@ -178,8 +178,8 @@ func ensureRepo(repo *velerov1api.BackupRepository, repoManager repository.Manag return repoManager.PrepareRepo(repo) } -func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { - log.Debug("resticRepositoryController.runMaintenanceIfDue") +func (r *BackupRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { + log.Debug("backupRepositoryController.runMaintenanceIfDue") now := r.clock.Now() @@ -188,19 +188,19 @@ func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *vel return nil } - log.Info("Running maintenance on restic repository") + log.Info("Running maintenance on backup repository") // prune failures should be displayed in the `.status.message` field but // should not cause the repo to move to `NotReady`. log.Debug("Pruning repo") if err := r.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() }) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } @@ -209,20 +209,20 @@ func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool { return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now) } -func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { +func (r *BackupRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil } - log.Info("Checking restic repository for readiness") + log.Info("Checking backup repository for readiness") // we need to ensure it (first check, if check fails, attempt to init) // because we don't know if it's been successfully initialized yet. if err := ensureRepo(req, r.repositoryManager); err != nil { - return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) + return r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, repoReady()) + return r.patchBackupRepository(ctx, req, repoReady()) } func repoNotReady(msg string) func(*velerov1api.BackupRepository) { @@ -239,14 +239,14 @@ func repoReady() func(*velerov1api.BackupRepository) { } } -// patchResticRepository mutates req with the provided mutate function, and patches it +// patchBackupRepository mutates req with the provided mutate function, and patches it // through the Kube API. After executing this function, req will be updated with both // the mutation and the results of the Patch() API call. -func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.BackupRepository, mutate func(*velerov1api.BackupRepository)) error { +func (r *BackupRepoReconciler) patchBackupRepository(ctx context.Context, req *velerov1api.BackupRepository, mutate func(*velerov1api.BackupRepository)) error { original := req.DeepCopy() mutate(req) if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil { - return errors.Wrap(err, "error patching ResticRepository") + return errors.Wrap(err, "error patching BackupRepository") } return nil } diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/backup_repository_controller_test.go similarity index 88% rename from pkg/controller/restic_repository_controller_test.go rename to pkg/controller/backup_repository_controller_test.go index 16fa5f983f..0197e1f615 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/backup_repository_controller_test.go @@ -33,12 +33,12 @@ import ( const testMaintenanceFrequency = 10 * time.Minute -func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { +func mockBackupRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *BackupRepoReconciler { mgr := &repomokes.Manager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) } - return NewResticRepoReconciler( + return NewBackupRepoReconciler( velerov1api.DefaultNamespace, velerotest.NewLogger(), velerotest.NewFakeControllerRuntimeClient(t), @@ -47,7 +47,7 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mo ) } -func mockResticRepositoryCR() *velerov1api.BackupRepository { +func mockBackupRepositoryCR() *velerov1api.BackupRepository { return &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, @@ -60,22 +60,22 @@ func mockResticRepositoryCR() *velerov1api.BackupRepository { } -func TestPatchResticRepository(t *testing.T) { - rr := mockResticRepositoryCR() - reconciler := mockResticRepoReconciler(t, rr, "", nil, nil) +func TestPatchBackupRepository(t *testing.T) { + rr := mockBackupRepositoryCR() + reconciler := mockBackupRepoReconciler(t, rr, "", nil, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) - err = reconciler.patchResticRepository(context.Background(), rr, repoReady()) + err = reconciler.patchBackupRepository(context.Background(), rr, repoReady()) assert.NoError(t, err) assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) - err = reconciler.patchResticRepository(context.Background(), rr, repoNotReady("not ready")) + err = reconciler.patchBackupRepository(context.Background(), rr, repoNotReady("not ready")) assert.NoError(t, err) assert.NotEqual(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestCheckNotReadyRepo(t *testing.T) { - rr := mockResticRepositoryCR() - reconciler := mockResticRepoReconciler(t, rr, "PrepareRepo", rr, nil) + rr := mockBackupRepositoryCR() + reconciler := mockBackupRepoReconciler(t, rr, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) @@ -88,8 +88,8 @@ func TestCheckNotReadyRepo(t *testing.T) { } func TestRunMaintenanceIfDue(t *testing.T) { - rr := mockResticRepositoryCR() - reconciler := mockResticRepoReconciler(t, rr, "PruneRepo", rr, nil) + rr := mockBackupRepositoryCR() + reconciler := mockBackupRepoReconciler(t, rr, "PruneRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) lastTm := rr.Status.LastMaintenanceTime @@ -105,9 +105,9 @@ func TestRunMaintenanceIfDue(t *testing.T) { } func TestInitializeRepo(t *testing.T) { - rr := mockResticRepositoryCR() + rr := mockBackupRepositoryCR() rr.Spec.BackupStorageLocation = "default" - reconciler := mockResticRepoReconciler(t, rr, "PrepareRepo", rr, nil) + reconciler := mockBackupRepoReconciler(t, rr, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) locations := &velerov1api.BackupStorageLocation{ @@ -127,7 +127,7 @@ func TestInitializeRepo(t *testing.T) { assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } -func TestResticRepoReconcile(t *testing.T) { +func TestBackupRepoReconcile(t *testing.T) { tests := []struct { name string repo *velerov1api.BackupRepository @@ -178,7 +178,7 @@ func TestResticRepoReconcile(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - reconciler := mockResticRepoReconciler(t, test.repo, "", test.repo, nil) + reconciler := mockBackupRepoReconciler(t, test.repo, "", test.repo, nil) err := reconciler.Client.Create(context.TODO(), test.repo) assert.NoError(t, err) _, err = reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.repo.Namespace, Name: test.repo.Name}}) @@ -227,7 +227,7 @@ func TestGetRepositoryMaintenanceFrequency(t *testing.T) { t.Run(test.name, func(t *testing.T) { mgr := repomokes.Manager{} mgr.On("DefaultMaintenanceFrequency", mock.Anything).Return(test.freqReturn, test.freqError) - reconciler := NewResticRepoReconciler( + reconciler := NewBackupRepoReconciler( velerov1api.DefaultNamespace, velerotest.NewLogger(), velerotest.NewFakeControllerRuntimeClient(t), diff --git a/pkg/controller/constants.go b/pkg/controller/constants.go index 28fc72a0c9..55d1ac7653 100644 --- a/pkg/controller/constants.go +++ b/pkg/controller/constants.go @@ -25,7 +25,7 @@ const ( GarbageCollection = "gc" PodVolumeBackup = "pod-volume-backup" PodVolumeRestore = "pod-volume-restore" - ResticRepo = "restic-repo" + BackupRepo = "backup-repo" Restore = "restore" Schedule = "schedule" ServerStatusRequest = "server-status-request" @@ -38,7 +38,7 @@ var DisableableControllers = []string{ BackupSync, DownloadRequest, GarbageCollection, - ResticRepo, + BackupRepo, Restore, Schedule, ServerStatusRequest, diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 6b4cf22268..250435cd02 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -205,8 +205,8 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ latencySeconds := float64(latencyDuration / time.Second) backupName := fmt.Sprintf("%s/%s", req.Namespace, pvb.OwnerReferences[0].Name) generateOpName := fmt.Sprintf("%s-%s-%s-%s-%s-backup", pvb.Name, backupRepo.Name, pvb.Spec.BackupStorageLocation, pvb.Namespace, pvb.Spec.UploaderType) - r.Metrics.ObserveResticOpLatency(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) - r.Metrics.RegisterResticOpLatencyGauge(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) + r.Metrics.ObservePodVolumeOpLatency(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) + r.Metrics.RegisterPodVolumeOpLatencyGauge(r.NodeName, req.Name, generateOpName, backupName, latencySeconds) r.Metrics.RegisterPodVolumeBackupDequeue(r.NodeName) log.Info("PodVolumeBackup completed") diff --git a/pkg/controller/pod_volume_backup_controller_test.go b/pkg/controller/pod_volume_backup_controller_test.go index f25b31a95f..b30adef5d1 100644 --- a/pkg/controller/pod_volume_backup_controller_test.go +++ b/pkg/controller/pod_volume_backup_controller_test.go @@ -145,7 +145,7 @@ var _ = Describe("PodVolumeBackup Reconciler", func() { r := PodVolumeBackupReconciler{ Client: fakeClient, Clock: clock.NewFakeClock(now), - Metrics: metrics.NewResticServerMetrics(), + Metrics: metrics.NewPodVolumeMetrics(), CredentialGetter: &credentials.CredentialGetter{FromFile: credentialFileStore}, NodeName: "test_node", FileSystem: fakeFS, diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index d486a88bd6..eb74072add 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -28,8 +28,8 @@ type ServerMetrics struct { } const ( - metricNamespace = "velero" - resticMetricsNamespace = "restic" + metricNamespace = "velero" + podVolumeMetricsNamespace = "podVolume" //Velero metrics backupTarballSizeBytesGauge = "backup_tarball_size_bytes" backupTotal = "backup_total" @@ -58,18 +58,18 @@ const ( csiSnapshotSuccessTotal = "csi_snapshot_success_total" csiSnapshotFailureTotal = "csi_snapshot_failure_total" - // Restic metrics - podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" - podVolumeBackupDequeueTotal = "pod_volume_backup_dequeue_count" - resticOperationLatencySeconds = "restic_operation_latency_seconds" - resticOperationLatencyGaugeSeconds = "restic_operation_latency_seconds_gauge" + // pod volume metrics + podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" + podVolumeBackupDequeueTotal = "pod_volume_backup_dequeue_count" + podVolumeOperationLatencySeconds = "pod_volume_operation_latency_seconds" + podVolumeOperationLatencyGaugeSeconds = "pod_volume_operation_latency_seconds_gauge" // Labels - nodeMetricLabel = "node" - resticOperationLabel = "operation" - pvbNameLabel = "pod_volume_backup" - scheduleLabel = "schedule" - backupNameLabel = "backupName" + nodeMetricLabel = "node" + podVolumeOperationLabel = "operation" + pvbNameLabel = "pod_volume_backup" + scheduleLabel = "schedule" + backupNameLabel = "backupName" ) // NewServerMetrics returns new ServerMetrics @@ -297,12 +297,12 @@ func NewServerMetrics() *ServerMetrics { } } -func NewResticServerMetrics() *ServerMetrics { +func NewPodVolumeMetrics() *ServerMetrics { return &ServerMetrics{ metrics: map[string]prometheus.Collector{ podVolumeBackupEnqueueTotal: prometheus.NewCounterVec( prometheus.CounterOpts{ - Namespace: resticMetricsNamespace, + Namespace: podVolumeMetricsNamespace, Name: podVolumeBackupEnqueueTotal, Help: "Total number of pod_volume_backup objects enqueued", }, @@ -310,25 +310,25 @@ func NewResticServerMetrics() *ServerMetrics { ), podVolumeBackupDequeueTotal: prometheus.NewCounterVec( prometheus.CounterOpts{ - Namespace: resticMetricsNamespace, + Namespace: podVolumeMetricsNamespace, Name: podVolumeBackupDequeueTotal, Help: "Total number of pod_volume_backup objects dequeued", }, []string{nodeMetricLabel}, ), - resticOperationLatencyGaugeSeconds: prometheus.NewGaugeVec( + podVolumeOperationLatencyGaugeSeconds: prometheus.NewGaugeVec( prometheus.GaugeOpts{ - Namespace: resticMetricsNamespace, - Name: resticOperationLatencyGaugeSeconds, - Help: "Gauge metric indicating time taken, in seconds, to perform restic operations", + Namespace: podVolumeMetricsNamespace, + Name: podVolumeOperationLatencyGaugeSeconds, + Help: "Gauge metric indicating time taken, in seconds, to perform pod volume operations", }, - []string{nodeMetricLabel, resticOperationLabel, backupNameLabel, pvbNameLabel}, + []string{nodeMetricLabel, podVolumeOperationLabel, backupNameLabel, pvbNameLabel}, ), - resticOperationLatencySeconds: prometheus.NewHistogramVec( + podVolumeOperationLatencySeconds: prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: resticMetricsNamespace, - Name: resticOperationLatencySeconds, - Help: "Time taken to complete restic operations, in seconds", + Namespace: podVolumeMetricsNamespace, + Name: podVolumeOperationLatencySeconds, + Help: "Time taken to complete pod volume operations, in seconds", Buckets: []float64{ toSeconds(1 * time.Minute), toSeconds(5 * time.Minute), @@ -341,7 +341,7 @@ func NewResticServerMetrics() *ServerMetrics { toSeconds(4 * time.Hour), }, }, - []string{nodeMetricLabel, resticOperationLabel, backupNameLabel, pvbNameLabel}, + []string{nodeMetricLabel, podVolumeOperationLabel, backupNameLabel, pvbNameLabel}, ), }, } @@ -422,7 +422,7 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { } // InitSchedule initializes counter metrics for a node. -func (m *ServerMetrics) InitResticMetricsForNode(node string) { +func (m *ServerMetrics) InitPodVolumeMetricsForNode(node string) { if c, ok := m.metrics[podVolumeBackupEnqueueTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(node).Add(0) } @@ -445,16 +445,16 @@ func (m *ServerMetrics) RegisterPodVolumeBackupDequeue(node string) { } } -// ObserveResticOpLatency records the number of seconds a restic operation took. -func (m *ServerMetrics) ObserveResticOpLatency(node, pvbName, opName, backupName string, seconds float64) { - if h, ok := m.metrics[resticOperationLatencySeconds].(*prometheus.HistogramVec); ok { +// ObservePodVolumeOpLatency records the number of seconds a pod volume operation took. +func (m *ServerMetrics) ObservePodVolumeOpLatency(node, pvbName, opName, backupName string, seconds float64) { + if h, ok := m.metrics[podVolumeOperationLatencySeconds].(*prometheus.HistogramVec); ok { h.WithLabelValues(node, opName, backupName, pvbName).Observe(seconds) } } -// RegisterResticOpLatencyGauge registers the restic operation latency as a gauge metric. -func (m *ServerMetrics) RegisterResticOpLatencyGauge(node, pvbName, opName, backupName string, seconds float64) { - if g, ok := m.metrics[resticOperationLatencyGaugeSeconds].(*prometheus.GaugeVec); ok { +// RegisterPodVolumeOpLatencyGauge registers the pod volume operation latency as a gauge metric. +func (m *ServerMetrics) RegisterPodVolumeOpLatencyGauge(node, pvbName, opName, backupName string, seconds float64) { + if g, ok := m.metrics[podVolumeOperationLatencyGaugeSeconds].(*prometheus.GaugeVec); ok { g.WithLabelValues(node, opName, backupName, pvbName).Set(seconds) } } diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index 3a5db6ceb6..2bd1a0b1c4 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -38,7 +38,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/kube" ) -// Backupper can execute restic backups of volumes in a pod. +// Backupper can execute pod volume backups of volumes in a pod. type Backupper interface { // BackupPodVolumes backs up all specified volumes in a pod. BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.Pod, volumesToBackup []string, log logrus.FieldLogger) ([]*velerov1api.PodVolumeBackup, []error) @@ -190,7 +190,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. continue } if isHostPath { - log.Warnf("Volume %s in pod %s/%s is a hostPath volume which is not supported for restic backup, skipping", volumeName, pod.Namespace, pod.Name) + log.Warnf("Volume %s in pod %s/%s is a hostPath volume which is not supported for pod volume backup, skipping", volumeName, pod.Namespace, pod.Name) continue } @@ -304,7 +304,7 @@ func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume c if pvc != nil { // this annotation is used in pkg/restore to identify if a PVC - // has a restic backup. + // has a pod volume backup. pvb.Annotations = map[string]string{ PVCNameAnnotation: pvc.Name, } diff --git a/pkg/podvolume/restorer.go b/pkg/podvolume/restorer.go index 09bb3a790f..1360cdc658 100644 --- a/pkg/podvolume/restorer.go +++ b/pkg/podvolume/restorer.go @@ -41,7 +41,7 @@ type RestoreData struct { SourceNamespace, BackupLocation string } -// Restorer can execute restic restores of volumes in a pod. +// Restorer can execute pod volume restores of volumes in a pod. type Restorer interface { // RestorePodVolumes restores all annotated volumes in a pod. RestorePodVolumes(RestoreData) []error diff --git a/pkg/podvolume/util_test.go b/pkg/podvolume/util_test.go index 936ee26d37..d1f1f73a8f 100644 --- a/pkg/podvolume/util_test.go +++ b/pkg/podvolume/util_test.go @@ -361,11 +361,11 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", + VolumesToBackupAnnotation: "pvbPV1,pvbPV2,pvbPV3", }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should get all pod volumes when defaultVolumesToFsBackup is true and no PVs are excluded", @@ -373,12 +373,12 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should get all pod volumes except ones excluded when defaultVolumesToFsBackup is true", @@ -386,56 +386,56 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, + /// Excluded from PVB through annotation + {Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { - name: "should exclude default service account token from restic backup", + name: "should exclude default service account token from pod volume backup", defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic because colume mounting default service account token + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, + /// Excluded from PVB because colume mounting default service account token {Name: "default-token-5xq45"}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { - name: "should exclude host path volumes from restic backups", + name: "should exclude host path volumes from pod volume backups", defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, + /// Excluded from pod volume backup through annotation + {Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"}, + // Excluded from pod volume backup because hostpath {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should exclude volumes mounting secrets", @@ -443,21 +443,21 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, + /// Excluded from pod volume backup through annotation + {Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"}, + // Excluded from pod volume backup because hostpath {Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should exclude volumes mounting config maps", @@ -465,21 +465,21 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath + // PVB Volumes + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, + /// Excluded from pod volume backup through annotation + {Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"}, + // Excluded from pod volume backup because hostpath {Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}}, }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should exclude projected volumes", @@ -487,12 +487,12 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, { Name: "projected", VolumeSource: corev1api.VolumeSource{ @@ -514,7 +514,7 @@ func TestGetVolumesByPod(t *testing.T) { }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, { name: "should exclude DownwardAPI volumes", @@ -522,12 +522,12 @@ func TestGetVolumesByPod(t *testing.T) { pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3", }, }, Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + {Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"}, { Name: "downwardAPI", VolumeSource: corev1api.VolumeSource{ @@ -547,7 +547,7 @@ func TestGetVolumesByPod(t *testing.T) { }, }, }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + expected: []string{"pvbPV1", "pvbPV2", "pvbPV3"}, }, } diff --git a/pkg/repository/keys/keys.go b/pkg/repository/keys/keys.go index 3da876e28f..2ec8bc0ba3 100644 --- a/pkg/repository/keys/keys.go +++ b/pkg/repository/keys/keys.go @@ -29,7 +29,7 @@ import ( ) const ( - credentialsSecretName = "velero-restic-credentials" + credentialsSecretName = "velero-repo-credentials" credentialsKey = "repository-password" encryptionKey = "static-passw0rd" @@ -65,10 +65,10 @@ func EnsureCommonRepositoryKey(secretClient corev1client.SecretsGetter, namespac } // RepoKeySelector returns the SecretKeySelector which can be used to fetch -// the restic repository key. +// the backup repository key. func RepoKeySelector() *corev1api.SecretKeySelector { - // For now, all restic repos share the same key so we don't need the repoName to fetch it. - // When we move to full-backup encryption, we'll likely have a separate key per restic repo + // For now, all backup repos share the same key so we don't need the repoName to fetch it. + // When we move to full-backup encryption, we'll likely have a separate key per backup repo // (all within the Velero server's namespace) so RepoKeySelector will need to select the key // for that repo. return builder.ForSecretKeySelector(credentialsSecretName, credentialsKey).Result() diff --git a/pkg/repository/locker.go b/pkg/repository/locker.go index 20eea96359..c261a1b359 100644 --- a/pkg/repository/locker.go +++ b/pkg/repository/locker.go @@ -19,7 +19,7 @@ package repository import "sync" // RepoLocker manages exclusive/non-exclusive locks for -// operations against restic repositories. The semantics +// operations against backup repositories. The semantics // of exclusive/non-exclusive locks are the same as for // a sync.RWMutex, where a non-exclusive lock is equivalent // to a read lock, and an exclusive lock is equivalent to diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go index 2ffa147805..fae6877dc4 100644 --- a/pkg/repository/manager.go +++ b/pkg/repository/manager.go @@ -31,18 +31,18 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) -// SnapshotIdentifier uniquely identifies a restic snapshot +// SnapshotIdentifier uniquely identifies a snapshot // taken by Velero. type SnapshotIdentifier struct { // VolumeNamespace is the namespace of the pod/volume that - // the restic snapshot is for. + // the snapshot is for. VolumeNamespace string // BackupStorageLocation is the backup's storage location // name. BackupStorageLocation string - // SnapshotID is the short ID of the restic snapshot. + // SnapshotID is the short ID of the snapshot. SnapshotID string // RepositoryType is the type of the repository where the diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 8705ae2127..cb19e27a74 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -107,8 +107,8 @@ type kubernetesRestorer struct { discoveryHelper discovery.Helper dynamicFactory client.DynamicFactory namespaceClient corev1.NamespaceInterface - resticRestorerFactory podvolume.RestorerFactory - resticTimeout time.Duration + podVolumeRestorerFactory podvolume.RestorerFactory + podVolumeTimeout time.Duration resourceTerminatingTimeout time.Duration resourcePriorities []string fileSystem filesystem.Interface @@ -126,8 +126,8 @@ func NewKubernetesRestorer( dynamicFactory client.DynamicFactory, resourcePriorities []string, namespaceClient corev1.NamespaceInterface, - resticRestorerFactory podvolume.RestorerFactory, - resticTimeout time.Duration, + podVolumeRestorerFactory podvolume.RestorerFactory, + podVolumeTimeout time.Duration, resourceTerminatingTimeout time.Duration, logger logrus.FieldLogger, podCommandExecutor podexec.PodCommandExecutor, @@ -139,8 +139,8 @@ func NewKubernetesRestorer( discoveryHelper: discoveryHelper, dynamicFactory: dynamicFactory, namespaceClient: namespaceClient, - resticRestorerFactory: resticRestorerFactory, - resticTimeout: resticTimeout, + podVolumeRestorerFactory: podVolumeRestorerFactory, + podVolumeTimeout: podVolumeTimeout, resourceTerminatingTimeout: resourceTerminatingTimeout, resourcePriorities: resourcePriorities, logger: logger, @@ -238,7 +238,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( return Result{}, Result{Velero: []string{err.Error()}} } - podVolumeTimeout := kr.resticTimeout + podVolumeTimeout := kr.podVolumeTimeout if val := req.Restore.Annotations[velerov1api.PodVolumeOperationTimeoutAnnotation]; val != "" { parsed, err := time.ParseDuration(val) if err != nil { @@ -254,9 +254,9 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( ctx, cancelFunc := go_context.WithTimeout(go_context.Background(), podVolumeTimeout) defer cancelFunc() - var resticRestorer podvolume.Restorer - if kr.resticRestorerFactory != nil { - resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, req.Restore) + var podVolumeRestorer podvolume.Restorer + if kr.podVolumeRestorerFactory != nil { + podVolumeRestorer, err = kr.podVolumeRestorerFactory.NewRestorer(ctx, req.Restore) if err != nil { return Result{}, Result{Velero: []string{err.Error()}} } @@ -302,8 +302,8 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( restoreItemActions: resolvedActions, itemSnapshotterActions: resolvedItemSnapshotterActions, volumeSnapshotterGetter: volumeSnapshotterGetter, - resticRestorer: resticRestorer, - resticErrs: make(chan error), + podVolumeRestorer: podVolumeRestorer, + podVolumeErrs: make(chan error), pvsToProvision: sets.NewString(), pvRestorer: pvRestorer, volumeSnapshots: req.VolumeSnapshots, @@ -345,9 +345,9 @@ type restoreContext struct { restoreItemActions []framework.RestoreItemResolvedAction itemSnapshotterActions []framework.ItemSnapshotterResolvedAction volumeSnapshotterGetter VolumeSnapshotterGetter - resticRestorer podvolume.Restorer - resticWaitGroup sync.WaitGroup - resticErrs chan error + podVolumeRestorer podvolume.Restorer + podVolumeWaitGroup sync.WaitGroup + podVolumeErrs chan error pvsToProvision sets.String pvRestorer PVRestorer volumeSnapshots []*volume.Snapshot @@ -557,29 +557,29 @@ func (ctx *restoreContext) execute() (Result, Result) { ctx.log.WithError(errors.WithStack((err))).Warn("Updating restore status.progress") } - // Wait for all of the restic restore goroutines to be done, which is + // Wait for all of the pod volume restore goroutines to be done, which is // only possible once all of their errors have been received by the loop - // below, then close the resticErrs channel so the loop terminates. + // below, then close the podVolumeErrs channel so the loop terminates. go func() { - ctx.log.Info("Waiting for all restic restores to complete") + ctx.log.Info("Waiting for all pod volume restores to complete") // TODO timeout? - ctx.resticWaitGroup.Wait() - close(ctx.resticErrs) + ctx.podVolumeWaitGroup.Wait() + close(ctx.podVolumeErrs) }() - // This loop will only terminate when the ctx.resticErrs channel is closed + // This loop will only terminate when the ctx.podVolumeErrs channel is closed // in the above goroutine, *after* all errors from the goroutines have been // received by this loop. - for err := range ctx.resticErrs { + for err := range ctx.podVolumeErrs { // TODO: not ideal to be adding these to Velero-level errors // rather than a specific namespace, but don't have a way // to track the namespace right now. errs.Velero = append(errs.Velero, err.Error()) } - ctx.log.Info("Done waiting for all restic restores to complete") + ctx.log.Info("Done waiting for all pod volume restores to complete") - // Wait for all post-restore exec hooks with same logic as restic wait above. + // Wait for all post-restore exec hooks with same logic as pod volume wait above. go func() { ctx.log.Info("Waiting for all post-restore-exec hooks to complete") @@ -1100,8 +1100,8 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso obj.SetAnnotations(annotations) } - case hasResticBackup(obj, ctx): - ctx.log.Infof("Dynamically re-provisioning persistent volume because it has a restic backup to be restored.") + case hasPodVolumeBackup(obj, ctx): + ctx.log.Infof("Dynamically re-provisioning persistent volume because it has a pod volume backup to be restored.") ctx.pvsToProvision.Insert(name) // Return early because we don't want to restore the PV itself, we @@ -1223,10 +1223,10 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso } if pvc.Spec.VolumeName != "" { - // This used to only happen with restic volumes, but now always remove this binding metadata + // This used to only happen with PVB volumes, but now always remove this binding metadata obj = resetVolumeBindingInfo(obj) - // This is the case for restic volumes, where we need to actually have an empty volume created instead of restoring one. + // This is the case for PVB volumes, where we need to actually have an empty volume created instead of restoring one. // The assumption is that any PV in pvsToProvision doesn't have an associated snapshot. if ctx.pvsToProvision.Has(pvc.Spec.VolumeName) { ctx.log.Infof("Resetting PersistentVolumeClaim %s/%s for dynamic provisioning", namespace, name) @@ -1558,19 +1558,19 @@ func remapClaimRefNS(ctx *restoreContext, obj *unstructured.Unstructured) (bool, // restorePodVolumeBackups restores the PodVolumeBackups for the given restored pod func restorePodVolumeBackups(ctx *restoreContext, createdObj *unstructured.Unstructured, originalNamespace string) { - if ctx.resticRestorer == nil { - ctx.log.Warn("No restic restorer, not restoring pod's volumes") + if ctx.podVolumeRestorer == nil { + ctx.log.Warn("No pod volume restorer, not restoring pod's volumes") } else { - ctx.resticWaitGroup.Add(1) + ctx.podVolumeWaitGroup.Add(1) go func() { // Done() will only be called after all errors have been successfully - // sent on the ctx.resticErrs channel - defer ctx.resticWaitGroup.Done() + // sent on the ctx.podVolumeErrs channel + defer ctx.podVolumeWaitGroup.Done() pod := new(v1.Pod) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(createdObj.UnstructuredContent(), &pod); err != nil { ctx.log.WithError(err).Error("error converting unstructured pod") - ctx.resticErrs <- err + ctx.podVolumeErrs <- err return } @@ -1581,11 +1581,11 @@ func restorePodVolumeBackups(ctx *restoreContext, createdObj *unstructured.Unstr SourceNamespace: originalNamespace, BackupLocation: ctx.backup.Spec.StorageLocation, } - if errs := ctx.resticRestorer.RestorePodVolumes(data); errs != nil { - ctx.log.WithError(kubeerrs.NewAggregate(errs)).Error("unable to successfully complete restic restores of pod's volumes") + if errs := ctx.podVolumeRestorer.RestorePodVolumes(data); errs != nil { + ctx.log.WithError(kubeerrs.NewAggregate(errs)).Error("unable to successfully complete pod volume restores of pod's volumes") for _, err := range errs { - ctx.resticErrs <- err + ctx.podVolumeErrs <- err } } }() @@ -1597,7 +1597,7 @@ func (ctx *restoreContext) waitExec(createdObj *unstructured.Unstructured) { ctx.hooksWaitGroup.Add(1) go func() { // Done() will only be called after all errors have been successfully sent - // on the ctx.resticErrs channel. + // on the ctx.podVolumeErrs channel. defer ctx.hooksWaitGroup.Done() pod := new(v1.Pod) @@ -1639,7 +1639,7 @@ func hasSnapshot(pvName string, snapshots []*volume.Snapshot) bool { return false } -func hasResticBackup(unstructuredPV *unstructured.Unstructured, ctx *restoreContext) bool { +func hasPodVolumeBackup(unstructuredPV *unstructured.Unstructured, ctx *restoreContext) bool { if len(ctx.podVolumeBackups) == 0 { return false } diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index f1575bc185..1df89a03a4 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -2682,17 +2682,17 @@ func TestRestorePersistentVolumes(t *testing.T) { } } -type fakeResticRestorerFactory struct { +type fakePodVolumeRestorerFactory struct { restorer *uploadermocks.Restorer } -func (f *fakeResticRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (podvolume.Restorer, error) { +func (f *fakePodVolumeRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (podvolume.Restorer, error) { return f.restorer, nil } -// TestRestoreWithRestic verifies that a call to RestorePodVolumes was made as and when -// expected for the given pods by using a mock for the restic restorer. -func TestRestoreWithRestic(t *testing.T) { +// TestRestoreWithPodVolume verifies that a call to RestorePodVolumes was made as and when +// expected for the given pods by using a mock for the pod volume restorer. +func TestRestoreWithPodVolume(t *testing.T) { tests := []struct { name string restore *velerov1api.Restore @@ -2753,7 +2753,7 @@ func TestRestoreWithRestic(t *testing.T) { h := newHarness(t) restorer := new(uploadermocks.Restorer) defer restorer.AssertExpectations(t) - h.restorer.resticRestorerFactory = &fakeResticRestorerFactory{ + h.restorer.podVolumeRestorerFactory = &fakePodVolumeRestorerFactory{ restorer: restorer, } @@ -3121,8 +3121,8 @@ func newHarness(t *testing.T) *harness { fileSystem: testutil.NewFakeFileSystem(), // unsupported - resticRestorerFactory: nil, - resticTimeout: 0, + podVolumeRestorerFactory: nil, + podVolumeTimeout: 0, }, log: log, } diff --git a/tilt-resources/examples/tilt-settings.json b/tilt-resources/examples/tilt-settings.json index ffdb37b30e..4e67f00565 100644 --- a/tilt-resources/examples/tilt-settings.json +++ b/tilt-resources/examples/tilt-settings.json @@ -15,7 +15,7 @@ "allowed_contexts": [ "development" ], - "enable_restic": false, + "use_node_agent": false, "create_backup_locations": true, "setup-minio": true, "enable_debug": false, From 154f5551c6045f814c848ec38833d244b513f160 Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Thu, 13 Oct 2022 19:45:52 +0800 Subject: [PATCH 332/366] Change subcommand restic to repo Signed-off-by: allenxu404 --- changelogs/unreleased/5446-allenxu404 | 1 + pkg/cmd/cli/{restic => }/repo/get.go | 2 +- pkg/cmd/cli/{restic => }/repo/repo.go | 6 ++--- pkg/cmd/cli/restic/restic.go | 38 --------------------------- pkg/cmd/velero/velero.go | 4 +-- 5 files changed, 7 insertions(+), 44 deletions(-) create mode 100644 changelogs/unreleased/5446-allenxu404 rename pkg/cmd/cli/{restic => }/repo/get.go (98%) rename pkg/cmd/cli/{restic => }/repo/repo.go (84%) delete mode 100644 pkg/cmd/cli/restic/restic.go diff --git a/changelogs/unreleased/5446-allenxu404 b/changelogs/unreleased/5446-allenxu404 new file mode 100644 index 0000000000..6d7675deed --- /dev/null +++ b/changelogs/unreleased/5446-allenxu404 @@ -0,0 +1 @@ +Change subcommand `velero restic repo` to `velero repo` \ No newline at end of file diff --git a/pkg/cmd/cli/restic/repo/get.go b/pkg/cmd/cli/repo/get.go similarity index 98% rename from pkg/cmd/cli/restic/repo/get.go rename to pkg/cmd/cli/repo/get.go index 24692f3552..3a730c1038 100644 --- a/pkg/cmd/cli/restic/repo/get.go +++ b/pkg/cmd/cli/repo/get.go @@ -33,7 +33,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { c := &cobra.Command{ Use: use, - Short: "Get restic repositories", + Short: "Get repositories", Run: func(c *cobra.Command, args []string) { err := output.ValidateFlags(c) cmd.CheckError(err) diff --git a/pkg/cmd/cli/restic/repo/repo.go b/pkg/cmd/cli/repo/repo.go similarity index 84% rename from pkg/cmd/cli/restic/repo/repo.go rename to pkg/cmd/cli/repo/repo.go index 2f754d8691..957dad7930 100644 --- a/pkg/cmd/cli/restic/repo/repo.go +++ b/pkg/cmd/cli/repo/repo.go @@ -22,11 +22,11 @@ import ( "github.com/vmware-tanzu/velero/pkg/client" ) -func NewRepositoryCommand(f client.Factory) *cobra.Command { +func NewCommand(f client.Factory) *cobra.Command { c := &cobra.Command{ Use: "repo", - Short: "Work with restic repositories", - Long: "Work with restic repositories", + Short: "Work with repositories", + Long: "Work with repositories", } c.AddCommand( diff --git a/pkg/cmd/cli/restic/restic.go b/pkg/cmd/cli/restic/restic.go deleted file mode 100644 index 744cdbd5fa..0000000000 --- a/pkg/cmd/cli/restic/restic.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2018 the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package restic - -import ( - "github.com/spf13/cobra" - - "github.com/vmware-tanzu/velero/pkg/client" - "github.com/vmware-tanzu/velero/pkg/cmd/cli/restic/repo" -) - -func NewCommand(f client.Factory) *cobra.Command { - c := &cobra.Command{ - Use: "restic", - Short: "Work with restic", - Long: "Work with restic", - } - - c.AddCommand( - repo.NewRepositoryCommand(f), - ) - - return c -} diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index 6338299c37..982761d2a9 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -39,7 +39,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd/cli/get" "github.com/vmware-tanzu/velero/pkg/cmd/cli/install" "github.com/vmware-tanzu/velero/pkg/cmd/cli/plugin" - "github.com/vmware-tanzu/velero/pkg/cmd/cli/restic" + "github.com/vmware-tanzu/velero/pkg/cmd/cli/repo" "github.com/vmware-tanzu/velero/pkg/cmd/cli/restore" "github.com/vmware-tanzu/velero/pkg/cmd/cli/schedule" "github.com/vmware-tanzu/velero/pkg/cmd/cli/snapshotlocation" @@ -117,7 +117,7 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre delete.NewCommand(f), cliclient.NewCommand(), completion.NewCommand(), - restic.NewCommand(f), + repo.NewCommand(f), bug.NewCommand(), backuplocation.NewCommand(f), snapshotlocation.NewCommand(f), From cf32cabddd181c71434f679f2b7e23b4a9fdb570 Mon Sep 17 00:00:00 2001 From: danfengl Date: Sat, 1 Oct 2022 02:28:59 +0000 Subject: [PATCH 333/366] fix-ctx-issue 1. Fix issue of kubectl client and server mismatch version in GitAction E2E job, refer to https://github.com/elastic/cloud-on-k8s/issues/4737; 2. Adapt to the changing of keyword for involing Kpoia as fs backupper, new installtion breaked upgrade and migration tests; 3. Accept multi-labels of Ginkgo focus as input of E2E make command; 4. Distinguish workload namespace from each tests; 5. Fix issues of not using Velero util to perform Velero commands; 6. Add snapshot test case for NamespaceMapping E2E test; 7. Collect debug bundle after catching error of Velero backup or restore command; Signed-off-by: danfengl --- .github/workflows/e2e-test-kind.yaml | 7 ++- pkg/cmd/cli/install/install.go | 48 ++++++++++---------- test/e2e/Makefile | 3 +- test/e2e/backups/schedule.go | 8 ++-- test/e2e/basic/namespace-mapping.go | 32 +++++++++---- test/e2e/basic/resources-check/namespaces.go | 1 + test/e2e/e2e_suite_test.go | 7 ++- test/e2e/migration/migration.go | 12 +++-- test/e2e/privilegesmgmt/ssr.go | 18 ++++---- test/e2e/pv-backup/pv-backup-filter.go | 6 +-- test/e2e/test/test.go | 47 ++++++++++++------- test/e2e/types.go | 4 +- test/e2e/upgrade/upgrade.go | 8 +++- test/e2e/util/kibishii/kibishii_utils.go | 7 +-- test/e2e/util/providers/azure_utils.go | 4 +- test/e2e/util/providers/common.go | 10 ++-- test/e2e/util/velero/install.go | 10 +++- test/e2e/util/velero/velero_utils.go | 38 +++++++++------- 18 files changed, 164 insertions(+), 106 deletions(-) diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 3f079d3339..1a260c1b0b 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -117,12 +117,17 @@ jobs: aws_access_key_id=minio aws_secret_access_key=minio123 EOF + + # Match kubectl version to k8s server version + curl -LO https://dl.k8s.io/release/v${{ matrix.k8s }}/bin/linux/amd64/kubectl + sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl + GOPATH=~/go CLOUD_PROVIDER=kind \ OBJECT_STORE_PROVIDER=aws BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ CREDS_FILE=/tmp/credential BSL_BUCKET=bucket \ ADDITIONAL_OBJECT_STORE_PROVIDER=aws ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ ADDITIONAL_CREDS_FILE=/tmp/credential ADDITIONAL_BSL_BUCKET=additional-bucket \ - GINKGO_FOCUS='Basic\].+\[ClusterResource' VELERO_IMAGE=velero:pr-test \ + GINKGO_FOCUS='Basic\]\[ClusterResource' VELERO_IMAGE=velero:pr-test \ make -C test/e2e run timeout-minutes: 30 - name: Upload debug bundle diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index f9cffa48fb..a7782aa1e4 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -43,29 +43,31 @@ import ( // InstallOptions collects all the options for installing Velero into a Kubernetes cluster. type InstallOptions struct { - Namespace string - Image string - BucketName string - Prefix string - ProviderName string - PodAnnotations flag.Map - PodLabels flag.Map - ServiceAccountAnnotations flag.Map - VeleroPodCPURequest string - VeleroPodMemRequest string - VeleroPodCPULimit string - VeleroPodMemLimit string - NodeAgentPodCPURequest string - NodeAgentPodMemRequest string - NodeAgentPodCPULimit string - NodeAgentPodMemLimit string - RestoreOnly bool - SecretFile string - NoSecret bool - DryRun bool - BackupStorageConfig flag.Map - VolumeSnapshotConfig flag.Map - UseNodeAgent bool + Namespace string + Image string + BucketName string + Prefix string + ProviderName string + PodAnnotations flag.Map + PodLabels flag.Map + ServiceAccountAnnotations flag.Map + VeleroPodCPURequest string + VeleroPodMemRequest string + VeleroPodCPULimit string + VeleroPodMemLimit string + NodeAgentPodCPURequest string + NodeAgentPodMemRequest string + NodeAgentPodCPULimit string + NodeAgentPodMemLimit string + RestoreOnly bool + SecretFile string + NoSecret bool + DryRun bool + BackupStorageConfig flag.Map + VolumeSnapshotConfig flag.Map + UseNodeAgent bool + //TODO remove UseRestic when migration test out of using it + UseRestic bool Wait bool UseVolumeSnapshots bool DefaultRepoMaintenanceFrequency time.Duration diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 8f703cc003..6b76b8ad15 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -48,6 +48,7 @@ OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin GINKGO_FOCUS ?= GINKGO_SKIP ?= SKIP_STR := $(foreach var, $(subst ., ,$(GINKGO_SKIP)),-skip "$(var)") +FOCUS_STR := $(foreach var, $(subst ., ,$(GINKGO_FOCUS)),-focus "$(var)") VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) @@ -110,7 +111,7 @@ run: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plug-in provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v -focus="$(GINKGO_FOCUS)" $(SKIP_STR) . -- -velerocli=$(VELERO_CLI) \ + @$(GINKGO) -v $(FOCUS_STR) $(SKIP_STR) . -- -velerocli=$(VELERO_CLI) \ -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ diff --git a/test/e2e/backups/schedule.go b/test/e2e/backups/schedule.go index 8cabe529c6..a30ba761e6 100644 --- a/test/e2e/backups/schedule.go +++ b/test/e2e/backups/schedule.go @@ -25,7 +25,7 @@ type ScheduleBackup struct { verifyTimes int } -var ScheduleBackupTest func() = TestFunc(&ScheduleBackup{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1"}}}) +var ScheduleBackupTest func() = TestFunc(&ScheduleBackup{TestCase: TestCase{NSBaseName: "schedule-test-ns", NSIncluded: &[]string{"ns1"}}}) func (n *ScheduleBackup) Init() error { n.Client = TestClientInstance @@ -45,7 +45,6 @@ func (n *ScheduleBackup) StartRun() error { n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() n.ScheduleArgs = []string{ - "schedule", "create", "--namespace", VeleroCfg.VeleroNamespace, n.ScheduleName, "--include-namespaces", strings.Join(*n.NSIncluded, ","), "--schedule=*/" + fmt.Sprintf("%v", n.Period) + " * * * *", } @@ -78,7 +77,10 @@ func (n *ScheduleBackup) Backup() error { now := time.Now().Minute() triggerNow := now % n.Period if triggerNow == 0 { - Expect(VeleroCmdExec(n.Ctx, VeleroCfg.VeleroCLI, n.ScheduleArgs)).To(Succeed()) + Expect(VeleroScheduleCreate(n.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, n.ScheduleName, n.ScheduleArgs)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", "") + return "Fail to restore workload" + }) break } } diff --git a/test/e2e/basic/namespace-mapping.go b/test/e2e/basic/namespace-mapping.go index 3a8d56ccef..e0b61fcf13 100644 --- a/test/e2e/basic/namespace-mapping.go +++ b/test/e2e/basic/namespace-mapping.go @@ -21,17 +21,24 @@ type NamespaceMapping struct { kibishiiData *KibishiiData } -var OneNamespaceMappingTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1"}}}) -var MultiNamespacesMappingTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: "ns", NSIncluded: &[]string{"ns1", "ns2"}}}) +const NamespaceBaseName string = "ns-mp-" + +var OneNamespaceMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1"}, UseVolumeSnapshots: false}}) +var MultiNamespacesMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1", NamespaceBaseName + "2"}, UseVolumeSnapshots: false}}) +var OneNamespaceMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1"}, UseVolumeSnapshots: true}}) +var MultiNamespacesMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1", NamespaceBaseName + "2"}, UseVolumeSnapshots: true}}) func (n *NamespaceMapping) Init() error { n.Client = TestClientInstance n.kibishiiData = &KibishiiData{2, 10, 10, 1024, 1024, 0, 2} - + backupType := "restic" + if n.UseVolumeSnapshots { + backupType = "snapshot" + } n.TestMsg = &TestMSG{ - Desc: "Backup resources with include namespace test", - FailedMSG: "Failed to backup with namespace include", - Text: fmt.Sprintf("should backup namespaces %s", *n.NSIncluded), + Desc: fmt.Sprintf("Restore namespace %s with namespace mapping by %s test", *n.NSIncluded, backupType), + FailedMSG: "Failed to restore with namespace mapping", + Text: fmt.Sprintf("should restore namespace %s with namespace mapping by %s", *n.NSIncluded, backupType), } return nil } @@ -49,15 +56,20 @@ func (n *NamespaceMapping) StartRun() error { n.BackupName = n.BackupName + ns n.RestoreName = n.RestoreName + ns } - n.BackupName = n.BackupName + "backup-ns-mapping-" + UUIDgen.String() - n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() + n.BackupName = n.BackupName + UUIDgen.String() + n.RestoreName = n.RestoreName + UUIDgen.String() n.MappedNamespaceList = mappedNSList fmt.Println(mappedNSList) n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, - "--include-namespaces", strings.Join(*n.NSIncluded, ","), - "--default-volumes-to-fs-backup", "--wait", + "--include-namespaces", strings.Join(*n.NSIncluded, ","), "--wait", + } + if n.UseVolumeSnapshots { + n.BackupArgs = append(n.BackupArgs, "--snapshot-volumes") + } else { + n.BackupArgs = append(n.BackupArgs, "--snapshot-volumes=false") + n.BackupArgs = append(n.BackupArgs, "--default-volumes-to-fs-backup") } n.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index 7c1d6ad4f6..67da3481bd 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -121,6 +121,7 @@ func (m *MultiNSBackup) Verify() error { } func (m *MultiNSBackup) Destroy() error { + m.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute) err := CleanupNamespaces(m.Ctx, m.Client, m.NSBaseName) if err != nil { return errors.Wrap(err, "Could cleanup retrieve namespaces") diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 1025d02166..ef35b7f5da 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -37,6 +37,7 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/privilegesmgmt" . "github.com/vmware-tanzu/velero/test/e2e/pv-backup" . "github.com/vmware-tanzu/velero/test/e2e/resource-filtering" + . "github.com/vmware-tanzu/velero/test/e2e/scale" . "github.com/vmware-tanzu/velero/test/e2e/upgrade" @@ -126,8 +127,10 @@ var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots) var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) -var _ = Describe("[NamespaceMapping][Single] Backup resources should follow the specific order in schedule", OneNamespaceMappingTest) -var _ = Describe("[NamespaceMapping][Multiple] Backup resources should follow the specific order in schedule", MultiNamespacesMappingTest) +var _ = Describe("[NamespaceMapping][Single][Restic] Backup resources should follow the specific order in schedule", OneNamespaceMappingResticTest) +var _ = Describe("[NamespaceMapping][Multiple][Restic] Backup resources should follow the specific order in schedule", MultiNamespacesMappingResticTest) +var _ = Describe("[NamespaceMapping][Single][Snapshot] Backup resources should follow the specific order in schedule", OneNamespaceMappingSnapshotTest) +var _ = Describe("[NamespaceMapping][Multiple][Snapshot] Backup resources should follow the specific order in schedule", MultiNamespacesMappingSnapshotTest) var _ = Describe("[pv-backup][Opt-In] Backup resources should follow the specific order in schedule", OptInPVBackupTest) var _ = Describe("[pv-backup][Opt-Out] Backup resources should follow the specific order in schedule", OptOutPVBackupTest) diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 3e81d8dd15..e957ace265 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -126,6 +126,8 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) OriginVeleroCfg.Plugins = "" //TODO: Remove this once origin Velero version is 1.10 and upper OriginVeleroCfg.UploaderType = "" + OriginVeleroCfg.UseNodeAgent = false + OriginVeleroCfg.UseRestic = !useVolumeSnapshots } fmt.Println(OriginVeleroCfg) Expect(VeleroInstall(context.Background(), &OriginVeleroCfg, useVolumeSnapshots)).To(Succeed()) @@ -157,9 +159,9 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupStorageClassCfg.IncludeResources = "StorageClass" BackupStorageClassCfg.IncludeClusterResources = true //TODO Remove UseRestic parameter once minor version is 1.10 or upper - BackupStorageClassCfg.UseRestic = true + BackupStorageClassCfg.UseResticIfFSBackup = true if veleroCLI2Version.VeleroVersion == "self" { - BackupStorageClassCfg.UseRestic = false + BackupStorageClassCfg.UseResticIfFSBackup = false } Expect(VeleroBackupNamespace(context.Background(), OriginVeleroCfg.VeleroCLI, @@ -175,9 +177,9 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) BackupCfg.BackupLocation = "" BackupCfg.Selector = "" //TODO Remove UseRestic parameter once minor version is 1.10 or upper - BackupCfg.UseRestic = true + BackupCfg.UseResticIfFSBackup = true if veleroCLI2Version.VeleroVersion == "self" { - BackupCfg.UseRestic = false + BackupCfg.UseResticIfFSBackup = false } Expect(VeleroBackupNamespace(context.Background(), OriginVeleroCfg.VeleroCLI, OriginVeleroCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { @@ -235,6 +237,8 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) VeleroCfg.ObjectStoreProvider = "" VeleroCfg.ClientToInstallVelero = VeleroCfg.StandbyClient + VeleroCfg.UseNodeAgent = !useVolumeSnapshots + VeleroCfg.UseRestic = false Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) }) diff --git a/test/e2e/privilegesmgmt/ssr.go b/test/e2e/privilegesmgmt/ssr.go index 526efb1e84..428cacc815 100644 --- a/test/e2e/privilegesmgmt/ssr.go +++ b/test/e2e/privilegesmgmt/ssr.go @@ -62,16 +62,14 @@ func SSRTest() { Expect(CreateNamespace(ctx, *VeleroCfg.ClientToInstallVelero, testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", testNS)) - By(fmt.Sprintf("Get version in %s namespace", testNS)) - cmd := []string{"version", "-n", testNS} - Expect(VeleroCmdExec(context.Background(), VeleroCfg.VeleroCLI, cmd)).To(Succeed(), - fmt.Sprintf("Failed to create an ssr object in the %s namespace", testNS)) - - By(fmt.Sprintf("Get version in %s namespace", VeleroCfg.VeleroNamespace)) - cmd = []string{"version", "-n", VeleroCfg.VeleroNamespace} - Expect(VeleroCmdExec(context.Background(), VeleroCfg.VeleroCLI, cmd)).To(Succeed(), - fmt.Sprintf("Failed to create an ssr object in %s namespace", VeleroCfg.VeleroNamespace)) - + By(fmt.Sprintf("Get version in %s namespace", testNS), func() { + Expect(VeleroVersion(context.Background(), VeleroCfg.VeleroCLI, testNS)).To(Succeed(), + fmt.Sprintf("Failed to create an ssr object in the %s namespace", testNS)) + }) + By(fmt.Sprintf("Get version in %s namespace", VeleroCfg.VeleroNamespace), func() { + Expect(VeleroVersion(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace)).To(Succeed(), + fmt.Sprintf("Failed to create an ssr object in %s namespace", VeleroCfg.VeleroNamespace)) + }) ssrListResp := new(v1.ServerStatusRequestList) By(fmt.Sprintf("Check ssr object in %s namespace", VeleroCfg.VeleroNamespace)) err = waitutil.PollImmediate(5*time.Second, time.Minute, diff --git a/test/e2e/pv-backup/pv-backup-filter.go b/test/e2e/pv-backup/pv-backup-filter.go index 3de103894b..4cef609d3a 100644 --- a/test/e2e/pv-backup/pv-backup-filter.go +++ b/test/e2e/pv-backup/pv-backup-filter.go @@ -49,8 +49,8 @@ func (p *PVBackupFiltering) StartRun() error { if err != nil { return err } - p.BackupName = p.BackupName + "backup-opt-in-" + UUIDgen.String() - p.RestoreName = p.RestoreName + "restore-opt-in-" + UUIDgen.String() + p.BackupName = p.BackupName + "backup-" + p.id + "-" + UUIDgen.String() + p.RestoreName = p.RestoreName + "restore-" + p.id + "-" + UUIDgen.String() p.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", p.BackupName, "--include-namespaces", strings.Join(*p.NSIncluded, ","), @@ -84,7 +84,7 @@ func (p *PVBackupFiltering) CreateResources() error { for j := 0; j <= VOLUME_COUNT_PER_POD-1; j++ { volume := fmt.Sprintf("volume-%s-%d-%d", p.id, i, j) volumes = append(volumes, volume) - //Volumes cherry pick policy for opt-in/out annotation to pods + //Volumes cherry-pick policy for opt-in/out annotation to apply if j%2 == 0 { volumeToAnnotationList = append(volumeToAnnotationList, volume) } diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index ee9218a0c1..4a754efb88 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -20,6 +20,7 @@ import ( "context" "flag" "fmt" + "time" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -47,6 +48,7 @@ type VeleroBackupRestoreTest interface { Verify() error Clean() error GetTestMsg() *TestMSG + GetTestCase() *TestCase } type TestMSG struct { @@ -56,16 +58,17 @@ type TestMSG struct { } type TestCase struct { - BackupName string - RestoreName string - NSBaseName string - BackupArgs []string - RestoreArgs []string - NamespacesTotal int - TestMsg *TestMSG - Client TestClient - Ctx context.Context - NSIncluded *[]string + BackupName string + RestoreName string + NSBaseName string + BackupArgs []string + RestoreArgs []string + NamespacesTotal int + TestMsg *TestMSG + Client TestClient + Ctx context.Context + NSIncluded *[]string + UseVolumeSnapshots bool } var TestClientInstance TestClient @@ -79,7 +82,7 @@ func TestFunc(test VeleroBackupRestoreTest) func() { BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { - Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, test.GetTestCase().UseVolumeSnapshots)).To(Succeed()) } }) AfterEach(func() { @@ -101,16 +104,17 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { By("Create test client instance", func() { TestClientInstance = *VeleroCfg.ClientToInstallVelero }) - + var useVolumeSnapshots bool for k := range tests { Expect(tests[k].Init()).To(Succeed(), fmt.Sprintf("Failed to instantiate test %s case", tests[k].GetTestMsg().Desc)) + useVolumeSnapshots = tests[k].GetTestCase().UseVolumeSnapshots } BeforeEach(func() { flag.Parse() if VeleroCfg.InstallVelero { if countIt == 0 { - Expect(VeleroInstall(context.Background(), &VeleroCfg, false)).To(Succeed()) + Expect(VeleroInstall(context.Background(), &VeleroCfg, useVolumeSnapshots)).To(Succeed()) } countIt++ } @@ -148,7 +152,7 @@ func (t *TestCase) StartRun() error { } func (t *TestCase) Backup() error { - if err := VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.BackupArgs); err != nil { + if err := VeleroBackupExec(t.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, t.BackupArgs); err != nil { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "") return errors.Wrapf(err, "Failed to backup resources") } @@ -164,9 +168,17 @@ func (t *TestCase) Destroy() error { } func (t *TestCase) Restore() error { + // the snapshots of AWS may be still in pending status when do the restore, wait for a while + // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 + // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed + if t.UseVolumeSnapshots { + fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") + time.Sleep(5 * time.Minute) + } + By("Start to restore ......", func() { - Expect(VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.RestoreArgs)).To(Succeed(), func() string { - RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "") + Expect(VeleroRestoreExec(t.Ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.RestoreName, t.RestoreArgs)).To(Succeed(), func() string { + RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", t.RestoreName) return "Fail to restore workload" }) }) @@ -193,6 +205,9 @@ func (t *TestCase) GetTestMsg() *TestMSG { return t.TestMsg } +func (t *TestCase) GetTestCase() *TestCase { + return t +} func RunTestCase(test VeleroBackupRestoreTest) error { fmt.Printf("Running test case %s\n", test.GetTestMsg().Desc) if test == nil { diff --git a/test/e2e/types.go b/test/e2e/types.go index 068d957ebb..5215170706 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -64,6 +64,8 @@ type VerleroConfig struct { DefaultClient *TestClient StandbyClient *TestClient UploaderType string + UseNodeAgent bool + UseRestic bool } type SnapshotCheckPoint struct { @@ -87,7 +89,7 @@ type BackupConfig struct { ExcludeResources string IncludeClusterResources bool OrderedResources string - UseRestic bool + UseResticIfFSBackup bool } type VeleroCLI2Version struct { diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index a46231b464..120deb9787 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -113,6 +113,8 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC tmpCfgForOldVeleroInstall.ResticHelperImage = "" tmpCfgForOldVeleroInstall.Plugins = "" tmpCfgForOldVeleroInstall.UploaderType = "" + tmpCfgForOldVeleroInstall.UseNodeAgent = false + tmpCfgForOldVeleroInstall.UseRestic = !useVolumeSnapshots Expect(VeleroInstall(context.Background(), &tmpCfgForOldVeleroInstall, useVolumeSnapshots)).To(Succeed()) @@ -144,8 +146,8 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC BackupCfg.BackupLocation = "" BackupCfg.UseVolumeSnapshots = useVolumeSnapshots BackupCfg.Selector = "" - //TODO: pay attention to this param - BackupCfg.UseRestic = true + //TODO: pay attention to this param, remove it when restic is not the default backup tool any more. + BackupCfg.UseResticIfFSBackup = true Expect(VeleroBackupNamespace(oneHourTimeout, tmpCfg.UpgradeFromVeleroCLI, tmpCfg.VeleroNamespace, BackupCfg)).To(Succeed(), func() string { RunDebug(context.Background(), tmpCfg.UpgradeFromVeleroCLI, tmpCfg.VeleroNamespace, @@ -196,6 +198,8 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC By(fmt.Sprintf("Upgrade Velero by CLI %s", tmpCfg.VeleroCLI), func() { tmpCfg.GCFrequency = "" + tmpCfg.UseNodeAgent = !useVolumeSnapshots + tmpCfg.UseRestic = false Expect(VeleroInstall(context.Background(), &tmpCfg, useVolumeSnapshots)).To(Succeed()) Expect(CheckVeleroVersion(context.Background(), tmpCfg.VeleroCLI, tmpCfg.VeleroVersion)).To(Succeed()) diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index b87f3e03bf..016fa072de 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -120,12 +120,11 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re if err := DeleteNamespace(oneHourTimeout, client, kibishiiNamespace, true); err != nil { return errors.Wrapf(err, "failed to delete namespace %s", kibishiiNamespace) } - time.Sleep(5 * time.Minute) // the snapshots of AWS may be still in pending status when do the restore, wait for a while // to avoid this https://github.com/vmware-tanzu/velero/issues/1799 // TODO remove this after https://github.com/vmware-tanzu/velero/issues/3533 is fixed - if providerName == "aws" && useVolumeSnapshots { + if useVolumeSnapshots { fmt.Println("Waiting 5 minutes to make sure the snapshots are ready...") time.Sleep(5 * time.Minute) } @@ -138,7 +137,6 @@ func RunKibishiiTests(client TestClient, veleroCfg VerleroConfig, backupName, re if err := KibishiiVerifyAfterRestore(client, kibishiiNamespace, oneHourTimeout, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Error verifying kibishii after restore") } - fmt.Printf("kibishii test completed successfully\n") return nil } @@ -151,8 +149,7 @@ func installKibishii(ctx context.Context, namespace string, cloudPlatform, veler } // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories kibishiiInstallCmd := exec.CommandContext(ctx, "kubectl", "apply", "-n", namespace, "-k", - kibishiiDirectory+cloudPlatform) - + kibishiiDirectory+cloudPlatform, "--timeout=90s") _, stderr, err := veleroexec.RunCommand(kibishiiInstallCmd) fmt.Printf("Install Kibishii cmd: %s\n", kibishiiInstallCmd) if err != nil { diff --git a/test/e2e/util/providers/azure_utils.go b/test/e2e/util/providers/azure_utils.go index 8c579c74fb..1dce4cb5b9 100644 --- a/test/e2e/util/providers/azure_utils.go +++ b/test/e2e/util/providers/azure_utils.go @@ -364,10 +364,10 @@ func (s AzureStorage) IsSnapshotExisted(cloudCredentialsFile, bslConfig, backupN snapshotCountFound := 0 backupNameInSnapshot := "" if err != nil { - errors.Wrap(err, fmt.Sprintf("Fail to list snapshots %s\n", envVars[resourceGroupEnvVar])) + return errors.Wrap(err, fmt.Sprintf("Fail to list snapshots %s\n", envVars[resourceGroupEnvVar])) } if result.Value == nil { - errors.New(fmt.Sprintf("No snapshots in Azure resource group %s\n", envVars[resourceGroupEnvVar])) + return errors.New(fmt.Sprintf("No snapshots in Azure resource group %s\n", envVars[resourceGroupEnvVar])) } for _, v := range *result.Value { if snapshotCheck.EnableCSI { diff --git a/test/e2e/util/providers/common.go b/test/e2e/util/providers/common.go index 61ffd1bf97..8c37d79d9e 100644 --- a/test/e2e/util/providers/common.go +++ b/test/e2e/util/providers/common.go @@ -46,7 +46,7 @@ func ObjectsShouldBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bsl func ObjectsShouldNotBeInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix string, retryTimes int) error { var err error var exist bool - fmt.Printf("|| VERIFICATION || - %s %s should not exist in storage %s\n", subPrefix, backupName, bslPrefix) + fmt.Printf("|| VERIFICATION || - %s %s should not exist in object store %s\n", subPrefix, backupName, bslPrefix) for i := 0; i < retryTimes; i++ { exist, err = IsObjectsInBucket(cloudProvider, cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, backupName, subPrefix) if err != nil { @@ -114,9 +114,9 @@ func SnapshotsShouldNotExistInCloud(cloudProvider, cloudCredentialsFile, bslBuck snapshotCheckPoint.ExpectCount = 0 err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { - return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s is existed in cloud after backup as expected", backupName)) + return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s exist in cloud after backup as expected", backupName)) } - fmt.Printf("|| EXPECTED || - Snapshots are not existed in cloud, backup %s\n", backupName) + fmt.Printf("|| EXPECTED || - Snapshots do not exist in cloud, backup %s\n", backupName) return nil } @@ -124,9 +124,9 @@ func SnapshotsShouldBeCreatedInCloud(cloudProvider, cloudCredentialsFile, bslBuc fmt.Printf("|| VERIFICATION || - Snapshots should exist in cloud, backup %s\n", backupName) err := IsSnapshotExisted(cloudProvider, cloudCredentialsFile, bslBucket, bslConfig, backupName, snapshotCheckPoint) if err != nil { - return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s are not existed in cloud after backup as expected", backupName)) + return errors.Wrapf(err, fmt.Sprintf("|| UNEXPECTED ||Snapshots %s do not exist in cloud after backup as expected", backupName)) } - fmt.Printf("|| EXPECTED || - Snapshots are existed in cloud, backup %s\n", backupName) + fmt.Printf("|| EXPECTED || - Snapshots exist in cloud, backup %s\n", backupName) return nil } diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index fc0389bc55..d3730b0bb8 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -87,7 +87,10 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps return errors.WithMessagef(err, "Failed to get Velero InstallOptions for plugin provider %s", veleroCfg.ObjectStoreProvider) } veleroInstallOptions.UseVolumeSnapshots = useVolumeSnapshots - veleroInstallOptions.UseNodeAgent = !useVolumeSnapshots + if !veleroCfg.UseRestic { + veleroInstallOptions.UseNodeAgent = !useVolumeSnapshots + } + veleroInstallOptions.UseRestic = veleroCfg.UseRestic veleroInstallOptions.Image = veleroCfg.VeleroImage veleroInstallOptions.Namespace = veleroCfg.VeleroNamespace veleroInstallOptions.UploaderType = veleroCfg.UploaderType @@ -175,6 +178,9 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption if options.UseNodeAgent { args = append(args, "--use-node-agent") } + if options.UseRestic { + args = append(args, "--use-restic") + } if options.UseVolumeSnapshots { args = append(args, "--use-volume-snapshots") } @@ -285,7 +291,7 @@ func createVelereResources(ctx context.Context, cli, namespace string, args []st cmd.Stderr = os.Stderr fmt.Printf("Running cmd %q \n", cmd.String()) if err = cmd.Run(); err != nil { - return errors.Wrapf(err, "failed to apply velere resources") + return errors.Wrapf(err, "failed to apply Velero resources") } return nil diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index f8495865c1..ded37dd46c 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -323,7 +323,7 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes") } else { - if backupCfg.UseRestic { + if backupCfg.UseResticIfFSBackup { args = append(args, "--default-volumes-to-restic") } else { args = append(args, "--default-volumes-to-fs-backup") @@ -399,6 +399,7 @@ func VeleroRestoreExec(ctx context.Context, veleroCLI, veleroNamespace, restoreN if err := VeleroCmdExec(ctx, veleroCLI, args); err != nil { return err } + return checkRestorePhase(ctx, veleroCLI, veleroNamespace, restoreName, velerov1api.RestorePhaseCompleted) } @@ -436,19 +437,14 @@ func VeleroScheduleCreate(ctx context.Context, veleroCLI string, veleroNamespace func VeleroCmdExec(ctx context.Context, veleroCLI string, args []string) error { cmd := exec.CommandContext(ctx, veleroCLI, args...) - var errBuf, outBuf bytes.Buffer - cmd.Stderr = io.MultiWriter(os.Stderr, &errBuf) - cmd.Stdout = io.MultiWriter(os.Stdout, &outBuf) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr fmt.Printf("velero cmd =%v\n", cmd) err := cmd.Run() - retAll := outBuf.String() + " " + errBuf.String() - if strings.Contains(strings.ToLower(retAll), "failed") { - return errors.Wrap(err, fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) + if strings.Contains(fmt.Sprint(cmd.Stdout), "Failed") { + return errors.New(fmt.Sprintf("velero cmd =%v return with failure\n", cmd)) } - if err != nil { - return err - } - return nil + return err } func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error { @@ -511,6 +507,16 @@ func VeleroCreateBackupLocation(ctx context.Context, return VeleroCmdExec(ctx, veleroCLI, args) } +func VeleroVersion(ctx context.Context, veleroCLI, veleroNamespace string) error { + args := []string{ + "version", "--namespace", veleroNamespace, + } + if err := VeleroCmdExec(ctx, veleroCLI, args); err != nil { + return err + } + return nil +} + func getProviderPlugins(ctx context.Context, veleroCLI, objectStoreProvider, providerPlugins, feature string) ([]string, error) { // Fetch the plugins for the provider before checking for the object store provider below. var plugins []string @@ -788,12 +794,11 @@ func GetBackup(ctx context.Context, veleroCLI string, backupName string) (string func IsBackupExist(ctx context.Context, veleroCLI string, backupName string) (bool, error) { out, outerr, err := GetBackup(ctx, veleroCLI, backupName) if err != nil { - if err != nil { - if strings.Contains(outerr, "not found") { - return false, nil - } - return false, err + if strings.Contains(outerr, "not found") { + fmt.Printf("Backup CR %s was not found\n", backupName) + return false, nil } + return false, err } fmt.Printf("Backup <%s> exist locally according to output \n[%s]\n", backupName, out) return true, nil @@ -807,6 +812,7 @@ func WaitBackupDeleted(ctx context.Context, veleroCLI string, backupName string, if exist { return false, nil } else { + fmt.Printf("Backup %s does not exist\n", backupName) return true, nil } } From d52ec8c079b731acf924402c4fe0ad61165151f9 Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:42:09 +0800 Subject: [PATCH 334/366] Pod Volume Backup/Restore Refactor: Rename Init Helper (#5432) * restore helper refactor Signed-off-by: Lyndon-Li * resolve codespell Signed-off-by: Lyndon-Li Signed-off-by: Lyndon-Li --- .github/workflows/pr-codespell.yml | 2 +- Makefile | 6 +- changelogs/unreleased/5432-lyndon | 1 + .../velero-restore-helper.go} | 0 design/Implemented/backup-resources-order.md | 6 +- ...ting-velero-crds-with-structural-schema.md | 2 +- internal/hook/item_hook_handler.go | 8 +-- internal/hook/item_hook_handler_test.go | 20 +++--- internal/velero/images.go | 6 +- internal/velero/images_test.go | 4 +- pkg/cmd/server/plugin/plugin.go | 6 +- .../pod_volume_restore_controller.go | 25 ++++---- .../pod_volume_restore_controller_test.go | 62 +++++++++---------- pkg/podvolume/util.go | 14 ++--- ...action.go => pod_volume_restore_action.go} | 39 ++++++------ ...t.go => pod_volume_restore_action_test.go} | 34 +++++----- pkg/restorehelper/util.go | 33 ++++++++++ pkg/uploader/provider/provider.go | 6 +- test/e2e/Makefile | 4 +- test/e2e/e2e_suite_test.go | 2 +- test/e2e/migration/migration.go | 2 +- test/e2e/types.go | 2 +- test/e2e/upgrade/upgrade.go | 6 +- test/e2e/util/velero/install.go | 16 ++--- 24 files changed, 169 insertions(+), 137 deletions(-) create mode 100644 changelogs/unreleased/5432-lyndon rename cmd/{velero-restic-restore-helper/velero-restic-restore-helper.go => velero-restore-helper/velero-restore-helper.go} (100%) rename pkg/restore/{restic_restore_action.go => pod_volume_restore_action.go} (86%) rename pkg/restore/{restic_restore_action_test.go => pod_volume_restore_action_test.go} (89%) create mode 100644 pkg/restorehelper/util.go diff --git a/.github/workflows/pr-codespell.yml b/.github/workflows/pr-codespell.yml index 0305b84acb..36cb779f45 100644 --- a/.github/workflows/pr-codespell.yml +++ b/.github/workflows/pr-codespell.yml @@ -15,6 +15,6 @@ jobs: with: # ignore the config/.../crd.go file as it's generated binary data that is edited elswhere. skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./go.sum,./LICENSE - ignore_words_list: iam,aks,ist,bridget,ue + ignore_words_list: iam,aks,ist,bridget,ue,shouldnot check_filenames: true check_hidden: true diff --git a/Makefile b/Makefile index b3a6a32f21..4e8e728659 100644 --- a/Makefile +++ b/Makefile @@ -112,17 +112,17 @@ GOPROXY ?= https://proxy.golang.org # If you want to build all containers, see the 'all-containers' rule. all: @$(MAKE) build - @$(MAKE) build BIN=velero-restic-restore-helper + @$(MAKE) build BIN=velero-restore-helper build-%: @$(MAKE) --no-print-directory ARCH=$* build - @$(MAKE) --no-print-directory ARCH=$* build BIN=velero-restic-restore-helper + @$(MAKE) --no-print-directory ARCH=$* build BIN=velero-restore-helper all-build: $(addprefix build-, $(CLI_PLATFORMS)) all-containers: container-builder-env @$(MAKE) --no-print-directory container - @$(MAKE) --no-print-directory container BIN=velero-restic-restore-helper + @$(MAKE) --no-print-directory container BIN=velero-restore-helper local: build-dirs # Add DEBUG=1 to enable debug locally diff --git a/changelogs/unreleased/5432-lyndon b/changelogs/unreleased/5432-lyndon new file mode 100644 index 0000000000..f6eb6708e7 --- /dev/null +++ b/changelogs/unreleased/5432-lyndon @@ -0,0 +1 @@ +Rename Velero pod volume restore init helper from "velero-restic-restore-helper" to "velero-restore-helper" \ No newline at end of file diff --git a/cmd/velero-restic-restore-helper/velero-restic-restore-helper.go b/cmd/velero-restore-helper/velero-restore-helper.go similarity index 100% rename from cmd/velero-restic-restore-helper/velero-restic-restore-helper.go rename to cmd/velero-restore-helper/velero-restore-helper.go diff --git a/design/Implemented/backup-resources-order.md b/design/Implemented/backup-resources-order.md index 46521b76a1..d888ad2371 100644 --- a/design/Implemented/backup-resources-order.md +++ b/design/Implemented/backup-resources-order.md @@ -2,7 +2,7 @@ This document proposes a solution that allows user to specify a backup order for resources of specific resource type. ## Background -During backup process, user may need to back up resources of specific type in some specific order to ensure the resources were backup properly because these resources are related and ordering might be required to preserve the consistency for the apps to recover itself �from the backup image +During backup process, user may need to back up resources of specific type in some specific order to ensure the resources were backup properly because these resources are related and ordering might be required to preserve the consistency for the apps to recover itself �from the backup image (Ex: primary-secondary database pods in a cluster). ## Goals @@ -12,7 +12,7 @@ During backup process, user may need to back up resources of specific type in so - Use a plugin to backup an resources and all the sub resources. For example use a plugin for StatefulSet and backup pods belong to the StatefulSet in specific order. This plugin solution is not generic and requires plugin for each resource type. ## High-Level Design -User will specify a map of resource type to list resource names (separate by semicolons). Each name will be in the format "namespaceName/resourceName" to enable ordering accross namespaces. Based on this map, the resources of each resource type will be sorted by the order specified in the list of resources. If a resource instance belong to that specific type but its name is not in the order list, then it will be put behind other resources that are in the list. +User will specify a map of resource type to list resource names (separate by semicolons). Each name will be in the format "namespaceName/resourceName" to enable ordering across namespaces. Based on this map, the resources of each resource type will be sorted by the order specified in the list of resources. If a resource instance belong to that specific type but its name is not in the order list, then it will be put behind other resources that are in the list. ### Changes to BackupSpec Add new field to BackupSpec @@ -36,5 +36,5 @@ Example: >velero backup create mybackup --ordered-resources "pod=ns1/pod1,ns1/pod2;persistentvolumeclaim=n2/slavepod,ns2/primarypod" ## Open Issues -- In the CLI, the design proposes to use commas to separate items of a resource type and semicolon to separate key-value pairs. This follows the convention of using commas to separate items in a list (For example: --include-namespaces ns1,ns2). However, the syntax for map in labels and annotations use commas to seperate key-value pairs. So it introduces some inconsistency. +- In the CLI, the design proposes to use commas to separate items of a resource type and semicolon to separate key-value pairs. This follows the convention of using commas to separate items in a list (For example: --include-namespaces ns1,ns2). However, the syntax for map in labels and annotations use commas to separate key-value pairs. So it introduces some inconsistency. - For pods that managed by Deployment or DaemonSet, this design may not work because the pods' name is randomly generated and if pods are restarted, they would have different names so the Backup operation may not consider the restarted pods in the sorting algorithm. This problem will be addressed when we enhance the design to use regular expression to specify the OrderResources instead of exact match. diff --git a/design/Implemented/generating-velero-crds-with-structural-schema.md b/design/Implemented/generating-velero-crds-with-structural-schema.md index 7c8c5a127e..fe4e820de3 100644 --- a/design/Implemented/generating-velero-crds-with-structural-schema.md +++ b/design/Implemented/generating-velero-crds-with-structural-schema.md @@ -28,7 +28,7 @@ This document proposes adding _controller-tools_ to the project to automatically _controller-tools_ works by reading the Go files that contain the API type definitions. It uses a combination of the struct fields, types, tags and comments to build the OpenAPIv3 schema for the CRDs. The tooling makes some assumptions based on conventions followed in upstream Kubernetes and the ecosystem, which involves some changes to the Velero API type definitions, especially around optional fields. -In order for _controller-tools_ to read the Go files containing Velero API type defintiions, the CRDs need to be generated at build time, as these files are not available at runtime (i.e. the Go files are not accessible by the compiled binary). +In order for _controller-tools_ to read the Go files containing Velero API type definitions, the CRDs need to be generated at build time, as these files are not available at runtime (i.e. the Go files are not accessible by the compiled binary). These generated CRD manifests (YAML) will then need to be available to the `pkg/install` package for it to include when installing Velero resources. ## Detailed Design diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 062922a056..0bced9484a 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -36,7 +36,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/restorehelper" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -121,12 +121,12 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( } initContainers := []corev1api.Container{} - // If this pod had pod volumes backed up using restic, then we want to the pod volumes restored prior to + // If this pod is backed up with data movement, then we want to the pod volumes restored prior to // running the restore hook init containers. This allows the restore hook init containers to prepare the // restored data to be consumed by the application container(s). - // So if there is a "restic-wait" init container already on the pod at index 0, we'll preserve that and run + // So if there is a "restore-wait" init container already on the pod at index 0, we'll preserve that and run // it before running any other init container. - if len(pod.Spec.InitContainers) > 0 && pod.Spec.InitContainers[0].Name == podvolume.InitContainer { + if len(pod.Spec.InitContainers) > 0 && pod.Spec.InitContainers[0].Name == restorehelper.WaitInitContainer { initContainers = append(initContainers, pod.Spec.InitContainers[0]) pod.Spec.InitContainers = pod.Spec.InitContainers[1:] } diff --git a/internal/hook/item_hook_handler_test.go b/internal/hook/item_hook_handler_test.go index 9f8267808c..ea2c518897 100644 --- a/internal/hook/item_hook_handler_test.go +++ b/internal/hook/item_hook_handler_test.go @@ -1675,7 +1675,7 @@ func TestHandleRestoreHooks(t *testing.T) { }, }, { - name: "should preserve restic-wait init container when it is the only existing init container", + name: "should preserve restore-wait init container when it is the only existing init container", podInput: corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "app1", @@ -1683,8 +1683,8 @@ func TestHandleRestoreHooks(t *testing.T) { }, Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ - *builder.ForContainer("restic-wait", "bus-box"). - Command([]string{"restic-restore"}).Result(), + *builder.ForContainer("restore-wait", "bus-box"). + Command([]string{"pod-volume-restore"}).Result(), }, }, }, @@ -1696,8 +1696,8 @@ func TestHandleRestoreHooks(t *testing.T) { }, Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ - *builder.ForContainer("restic-wait", "bus-box"). - Command([]string{"restic-restore"}).Result(), + *builder.ForContainer("restore-wait", "bus-box"). + Command([]string{"pod-volume-restore"}).Result(), *builder.ForContainer("restore-init-container-1", "nginx"). Command([]string{"a", "b", "c"}).Result(), *builder.ForContainer("restore-init-container-2", "nginx"). @@ -1729,7 +1729,7 @@ func TestHandleRestoreHooks(t *testing.T) { }, { - name: "should preserve restic-wait init container when it exits with other init containers", + name: "should preserve restore-wait init container when it exits with other init containers", podInput: corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "app1", @@ -1737,8 +1737,8 @@ func TestHandleRestoreHooks(t *testing.T) { }, Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ - *builder.ForContainer("restic-wait", "bus-box"). - Command([]string{"restic-restore"}).Result(), + *builder.ForContainer("restore-wait", "bus-box"). + Command([]string{"pod-volume-restore"}).Result(), *builder.ForContainer("init-app-step1", "busy-box"). Command([]string{"init-step1"}).Result(), *builder.ForContainer("init-app-step2", "busy-box"). @@ -1754,8 +1754,8 @@ func TestHandleRestoreHooks(t *testing.T) { }, Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ - *builder.ForContainer("restic-wait", "bus-box"). - Command([]string{"restic-restore"}).Result(), + *builder.ForContainer("restore-wait", "bus-box"). + Command([]string{"pod-volume-restore"}).Result(), *builder.ForContainer("restore-init-container-1", "nginx"). Command([]string{"a", "b", "c"}).Result(), *builder.ForContainer("restore-init-container-2", "nginx"). diff --git a/internal/velero/images.go b/internal/velero/images.go index c6319aaddd..b21d4aff8c 100644 --- a/internal/velero/images.go +++ b/internal/velero/images.go @@ -44,8 +44,8 @@ func DefaultVeleroImage() string { return fmt.Sprintf("%s/%s:%s", imageRegistry(), "velero", ImageTag()) } -// DefaultResticRestoreHelperImage returns the default container image to use for the restic restore helper +// DefaultRestoreHelperImage returns the default container image to use for the restore helper // for this version of Velero. -func DefaultResticRestoreHelperImage() string { - return fmt.Sprintf("%s/%s:%s", imageRegistry(), "velero-restic-restore-helper", ImageTag()) +func DefaultRestoreHelperImage() string { + return fmt.Sprintf("%s/%s:%s", imageRegistry(), "velero-restore-helper", ImageTag()) } diff --git a/internal/velero/images_test.go b/internal/velero/images_test.go index d04c66b9dc..7d6bdf8cd9 100644 --- a/internal/velero/images_test.go +++ b/internal/velero/images_test.go @@ -135,6 +135,6 @@ func TestDefaultVeleroImage(t *testing.T) { testDefaultImage(t, DefaultVeleroImage, "velero") } -func TestDefaultResticRestoreHelperImage(t *testing.T) { - testDefaultImage(t, DefaultResticRestoreHelperImage, "velero-restic-restore-helper") +func TestDefaultRestoreHelperImage(t *testing.T) { + testDefaultImage(t, DefaultRestoreHelperImage, "velero-restore-helper") } diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 64dbe4572a..bb2c1c6004 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -45,7 +45,7 @@ func NewCommand(f client.Factory) *cobra.Command { RegisterBackupItemAction("velero.io/service-account", newServiceAccountBackupItemAction(f)). RegisterRestoreItemAction("velero.io/job", newJobRestoreItemAction). RegisterRestoreItemAction("velero.io/pod", newPodRestoreItemAction). - RegisterRestoreItemAction("velero.io/restic", newResticRestoreItemAction(f)). + RegisterRestoreItemAction("velero.io/pod-volume-restore", newPodVolumeRestoreItemAction(f)). RegisterRestoreItemAction("velero.io/init-restore-hook", newInitRestoreHookPodAction). RegisterRestoreItemAction("velero.io/service", newServiceRestoreItemAction). RegisterRestoreItemAction("velero.io/service-account", newServiceAccountRestoreItemAction). @@ -139,7 +139,7 @@ func newInitRestoreHookPodAction(logger logrus.FieldLogger) (interface{}, error) return restore.NewInitRestoreHookPodAction(logger), nil } -func newResticRestoreItemAction(f client.Factory) plugincommon.HandlerInitializer { +func newPodVolumeRestoreItemAction(f client.Factory) plugincommon.HandlerInitializer { return func(logger logrus.FieldLogger) (interface{}, error) { client, err := f.KubeClient() if err != nil { @@ -151,7 +151,7 @@ func newResticRestoreItemAction(f client.Factory) plugincommon.HandlerInitialize return nil, err } - return restore.NewResticRestoreAction(logger, client.CoreV1().ConfigMaps(f.Namespace()), veleroClient.VeleroV1().PodVolumeBackups(f.Namespace())), nil + return restore.NewPodVolumeRestoreAction(logger, client.CoreV1().ConfigMaps(f.Namespace()), veleroClient.VeleroV1().PodVolumeBackups(f.Namespace())), nil } } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 5292e0a01f..9c78171ae9 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -42,6 +42,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" + "github.com/vmware-tanzu/velero/pkg/restorehelper" "github.com/vmware-tanzu/velero/pkg/uploader" "github.com/vmware-tanzu/velero/pkg/uploader/provider" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -105,10 +106,10 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, nil } - resticInitContainerIndex := getResticInitContainerIndex(pod) - if resticInitContainerIndex > 0 { + initContainerIndex := getInitContainerIndex(pod) + if initContainerIndex > 0 { log.Warnf(`Init containers before the %s container may cause issues - if they interfere with volumes being restored: %s index %d`, podvolume.InitContainer, podvolume.InitContainer, resticInitContainerIndex) + if they interfere with volumes being restored: %s index %d`, restorehelper.WaitInitContainer, restorehelper.WaitInitContainer, initContainerIndex) } log.Info("Restore starting") @@ -162,8 +163,8 @@ func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logr return false, nil, err } - if !isResticInitContainerRunning(pod) { - log.Debug("Pod is not running restic-wait init container, skip") + if !isInitContainerRunning(pod) { + log.Debug("Pod is not running restore-wait init container, skip") return false, nil, nil } @@ -207,18 +208,18 @@ func isPVRNew(pvr *velerov1api.PodVolumeRestore) bool { return pvr.Status.Phase == "" || pvr.Status.Phase == velerov1api.PodVolumeRestorePhaseNew } -func isResticInitContainerRunning(pod *corev1api.Pod) bool { - // Restic wait container can be anywhere in the list of init containers, but must be running. - i := getResticInitContainerIndex(pod) +func isInitContainerRunning(pod *corev1api.Pod) bool { + // Pod volume wait container can be anywhere in the list of init containers, but must be running. + i := getInitContainerIndex(pod) return i >= 0 && len(pod.Status.InitContainerStatuses)-1 >= i && pod.Status.InitContainerStatuses[i].State.Running != nil } -func getResticInitContainerIndex(pod *corev1api.Pod) int { - // Restic wait container can be anywhere in the list of init containers so locate it. +func getInitContainerIndex(pod *corev1api.Pod) int { + // Pod volume wait container can be anywhere in the list of init containers so locate it. for i, initContainer := range pod.Spec.InitContainers { - if initContainer.Name == podvolume.InitContainer { + if initContainer.Name == restorehelper.WaitInitContainer { return i } } @@ -299,7 +300,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve } // Write a done file with name= into the just-created .velero dir - // within the volume. The velero restic init container on the pod is waiting + // within the volume. The velero init container on the pod is waiting // for this file to exist in each restored volume before completing. if err := ioutil.WriteFile(filepath.Join(volumePath, ".velero", string(restoreUID)), nil, 0644); err != nil { return errors.Wrap(err, "error writing done file") diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index d9cdc5d264..eeec55b544 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/restorehelper" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -120,7 +120,7 @@ func TestShouldProcess(t *testing.T) { NodeName: controllerNode, InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, }, }, @@ -160,7 +160,7 @@ func TestShouldProcess(t *testing.T) { NodeName: controllerNode, InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, }, }, @@ -205,7 +205,7 @@ func TestShouldProcess(t *testing.T) { } } -func TestIsResticContainerRunning(t *testing.T) { +func TestIsInitContainerRunning(t *testing.T) { tests := []struct { name string pod *corev1api.Pod @@ -222,7 +222,7 @@ func TestIsResticContainerRunning(t *testing.T) { expected: false, }, { - name: "pod with running init container that's not restic should return false", + name: "pod with running init container that's not restore init should return false", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -231,7 +231,7 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -248,7 +248,7 @@ func TestIsResticContainerRunning(t *testing.T) { expected: false, }, { - name: "pod with running restic init container that's not first should still work", + name: "pod with running init container that's not first should still work", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -257,10 +257,10 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: "non-restic-init", + Name: "non-restore-init", }, { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, }, }, @@ -282,7 +282,7 @@ func TestIsResticContainerRunning(t *testing.T) { expected: true, }, { - name: "pod with restic init container as first initContainer that's not running should return false", + name: "pod with init container as first initContainer that's not running should return false", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -291,10 +291,10 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -314,7 +314,7 @@ func TestIsResticContainerRunning(t *testing.T) { expected: false, }, { - name: "pod with running restic init container as first initContainer should return true", + name: "pod with running init container as first initContainer should return true", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -323,10 +323,10 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -348,7 +348,7 @@ func TestIsResticContainerRunning(t *testing.T) { expected: true, }, { - name: "pod with restic init container with empty InitContainerStatuses should return 0", + name: "pod with init container with empty InitContainerStatuses should return 0", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -357,7 +357,7 @@ func TestIsResticContainerRunning(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, }, }, @@ -371,12 +371,12 @@ func TestIsResticContainerRunning(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - assert.Equal(t, test.expected, isResticInitContainerRunning(test.pod)) + assert.Equal(t, test.expected, isInitContainerRunning(test.pod)) }) } } -func TestGetResticInitContainerIndex(t *testing.T) { +func TestGetInitContainerIndex(t *testing.T) { tests := []struct { name string pod *corev1api.Pod @@ -393,7 +393,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { expected: -1, }, { - name: "pod with no restic init container return -1", + name: "pod with no init container return -1", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -402,7 +402,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -410,7 +410,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { expected: -1, }, { - name: "pod with restic container as second initContainern should return 1", + name: "pod with container as second initContainern should return 1", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -419,10 +419,10 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: "non-restic-init", + Name: "non-restore-init", }, { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, }, }, @@ -430,7 +430,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { expected: 1, }, { - name: "pod with restic init container as first initContainer should return 0", + name: "pod with init container as first initContainer should return 0", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -439,10 +439,10 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -450,7 +450,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { expected: 0, }, { - name: "pod with restic init container as first initContainer should return 0", + name: "pod with init container as first initContainer should return 0", pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns-1", @@ -459,10 +459,10 @@ func TestGetResticInitContainerIndex(t *testing.T) { Spec: corev1api.PodSpec{ InitContainers: []corev1api.Container{ { - Name: podvolume.InitContainer, + Name: restorehelper.WaitInitContainer, }, { - Name: "non-restic-init", + Name: "non-restore-init", }, }, }, @@ -473,7 +473,7 @@ func TestGetResticInitContainerIndex(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - assert.Equal(t, test.expected, getResticInitContainerIndex(test.pod)) + assert.Equal(t, test.expected, getInitContainerIndex(test.pod)) }) } } diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index d75b393153..e8c1d202c4 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -38,17 +38,13 @@ const ( podAnnotationPrefix = "snapshot.velero.io/" // VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes - // need to be backed up using restic. + // need to be backed up using pod volume backup. VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes - // should be excluded from restic backup. + // should be excluded from pod volume backup. VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" - // InitContainer is the name of the init container added - // to workload pods to help with restores. - InitContainer = "restic-wait" - // DefaultVolumesToFsBackup specifies whether pod volume backup should be used, by default, to // take backup of all pod volumes. DefaultVolumesToFsBackup = false @@ -201,7 +197,7 @@ func volumeHasNonRestorableSource(volumeName string, podVolumes []corev1api.Volu // getPodSnapshotAnnotations returns a map, of volume name -> snapshot id, // of all snapshots for this pod. // TODO(2.0) to remove -// Deprecated: we will stop using pod annotations to record restic snapshot IDs after they're taken, +// Deprecated: we will stop using pod annotations to record pod volume snapshot IDs after they're taken, // therefore we won't need to check if these annotations exist. func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { var res map[string]string @@ -224,7 +220,7 @@ func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { // GetVolumesToBackup returns a list of volume names to backup for // the provided pod. -// Deprecated: Use GetPodVolumesUsingRestic instead. +// Deprecated: Use GetVolumesByPod instead. func GetVolumesToBackup(obj metav1.Object) []string { annotations := obj.GetAnnotations() if annotations == nil { @@ -267,7 +263,7 @@ func GetVolumesByPod(pod *corev1api.Pod, defaultVolumesToFsBackup bool) []string podVolumes := []string{} for _, pv := range pod.Spec.Volumes { // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods - // and therefore not accessible to the restic daemon set. + // and therefore not accessible to the node agent daemon set. if pv.HostPath != nil { continue } diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/pod_volume_restore_action.go similarity index 86% rename from pkg/restore/restic_restore_action.go rename to pkg/restore/pod_volume_restore_action.go index f2d8df9a1d..3f419c0aca 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/pod_volume_restore_action.go @@ -37,38 +37,39 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/restorehelper" "github.com/vmware-tanzu/velero/pkg/util/kube" ) const ( defaultCPURequestLimit = "100m" defaultMemRequestLimit = "128Mi" - defaultCommand = "/velero-restic-restore-helper" + defaultCommand = "/velero-restore-helper" ) -type ResticRestoreAction struct { +type PodVolumeRestoreAction struct { logger logrus.FieldLogger client corev1client.ConfigMapInterface podVolumeBackupClient velerov1client.PodVolumeBackupInterface } -func NewResticRestoreAction(logger logrus.FieldLogger, client corev1client.ConfigMapInterface, podVolumeBackupClient velerov1client.PodVolumeBackupInterface) *ResticRestoreAction { - return &ResticRestoreAction{ +func NewPodVolumeRestoreAction(logger logrus.FieldLogger, client corev1client.ConfigMapInterface, podVolumeBackupClient velerov1client.PodVolumeBackupInterface) *PodVolumeRestoreAction { + return &PodVolumeRestoreAction{ logger: logger, client: client, podVolumeBackupClient: podVolumeBackupClient, } } -func (a *ResticRestoreAction) AppliesTo() (velero.ResourceSelector, error) { +func (a *PodVolumeRestoreAction) AppliesTo() (velero.ResourceSelector, error) { return velero.ResourceSelector{ IncludedResources: []string{"pods"}, }, nil } -func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { - a.logger.Info("Executing ResticRestoreAction") - defer a.logger.Info("Done executing ResticRestoreAction") +func (a *PodVolumeRestoreAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + a.logger.Info("Executing PodVolumeRestoreAction") + defer a.logger.Info("Done executing PodVolumeRestoreAction") var pod corev1.Pod if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), &pod); err != nil { @@ -98,16 +99,16 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu } volumeSnapshots := podvolume.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) if len(volumeSnapshots) == 0 { - log.Debug("No restic backups found for pod") + log.Debug("No pod volume backups found for pod") return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } - log.Info("Restic backups for pod found") + log.Info("Pod volume backups for pod found") // TODO we might want/need to get plugin config at the top of this method at some point; for now, wait // until we know we're doing a restore before getting config. log.Debugf("Getting plugin config") - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/restic", a.client) + config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/pod-volume-restore", a.client) if err != nil { return nil, err } @@ -146,7 +147,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu log.Errorf("Using default securityContext values, couldn't parse securityContext requirements: %s.", err) } - initContainerBuilder := newResticInitContainerBuilder(image, string(input.Restore.UID)) + initContainerBuilder := newRestoreInitContainerBuilder(image, string(input.Restore.UID)) initContainerBuilder.Resources(&resourceReqs) initContainerBuilder.SecurityContext(&securityContext) @@ -160,7 +161,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu initContainerBuilder.Command(getCommand(log, config)) initContainer := *initContainerBuilder.Result() - if len(pod.Spec.InitContainers) == 0 || pod.Spec.InitContainers[0].Name != podvolume.InitContainer { + if len(pod.Spec.InitContainers) == 0 || (pod.Spec.InitContainers[0].Name != restorehelper.WaitInitContainer && pod.Spec.InitContainers[0].Name != restorehelper.WaitInitContainerLegacy) { pod.Spec.InitContainers = append([]corev1.Container{initContainer}, pod.Spec.InitContainers...) } else { pod.Spec.InitContainers[0] = initContainer @@ -192,13 +193,13 @@ func getCommand(log logrus.FieldLogger, config *corev1.ConfigMap) []string { func getImage(log logrus.FieldLogger, config *corev1.ConfigMap) string { if config == nil { log.Debug("No config found for plugin") - return veleroimage.DefaultResticRestoreHelperImage() + return veleroimage.DefaultRestoreHelperImage() } image := config.Data["image"] if image == "" { log.Debugf("No custom image configured") - return veleroimage.DefaultResticRestoreHelperImage() + return veleroimage.DefaultRestoreHelperImage() } log = log.WithField("image", image) @@ -206,7 +207,7 @@ func getImage(log logrus.FieldLogger, config *corev1.ConfigMap) string { parts := strings.Split(image, "/") if len(parts) == 1 { - defaultImage := veleroimage.DefaultResticRestoreHelperImage() + defaultImage := veleroimage.DefaultRestoreHelperImage() // Image supplied without registry part log.Infof("Plugin config contains image name without registry name. Using default init container image: %q", defaultImage) return defaultImage @@ -264,7 +265,7 @@ func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (strin func getPluginConfig(kind common.PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { opts := metav1.ListOptions{ // velero.io/plugin-config: true - // velero.io/restic: RestoreItemAction + // velero.io/pod-volume-restore: RestoreItemAction LabelSelector: fmt.Sprintf("velero.io/plugin-config,%s=%s", name, kind), } @@ -288,8 +289,8 @@ func getPluginConfig(kind common.PluginKind, name string, client corev1client.Co return &list.Items[0], nil } -func newResticInitContainerBuilder(image, restoreUID string) *builder.ContainerBuilder { - return builder.ForContainer(podvolume.InitContainer, image). +func newRestoreInitContainerBuilder(image, restoreUID string) *builder.ContainerBuilder { + return builder.ForContainer(restorehelper.WaitInitContainer, image). Args(restoreUID). Env([]*corev1.EnvVar{ { diff --git a/pkg/restore/restic_restore_action_test.go b/pkg/restore/pod_volume_restore_action_test.go similarity index 89% rename from pkg/restore/restic_restore_action_test.go rename to pkg/restore/pod_volume_restore_action_test.go index b218f4b88d..f57ffdc69f 100644 --- a/pkg/restore/restic_restore_action_test.go +++ b/pkg/restore/pod_volume_restore_action_test.go @@ -49,7 +49,7 @@ func TestGetImage(t *testing.T) { } } - defaultImage := veleroimage.DefaultResticRestoreHelperImage() + defaultImage := veleroimage.DefaultRestoreHelperImage() tests := []struct { name string @@ -110,8 +110,8 @@ func TestGetImage(t *testing.T) { } } -// TestResticRestoreActionExecute tests the restic restore item action plugin's Execute method. -func TestResticRestoreActionExecute(t *testing.T) { +// TestPodVolumeRestoreActionExecute tests the pod volume restore item action plugin's Execute method. +func TestPodVolumeRestoreActionExecute(t *testing.T) { resourceReqs, _ := kube.ParseResourceRequirements( defaultCPURequestLimit, defaultMemRequestLimit, // requests defaultCPURequestLimit, defaultMemRequestLimit, // limits @@ -125,7 +125,7 @@ func TestResticRestoreActionExecute(t *testing.T) { veleroNs = "velero" ) - defaultResticRestoreHelperImage := veleroimage.DefaultResticRestoreHelperImage() + defaultRestoreHelperImage := veleroimage.DefaultRestoreHelperImage() tests := []struct { name string @@ -135,7 +135,7 @@ func TestResticRestoreActionExecute(t *testing.T) { want *corev1api.Pod }{ { - name: "Restoring pod with no other initContainers adds the restic initContainer", + name: "Restoring pod with no other initContainers adds the restore initContainer", pod: builder.ForPod("ns-1", "my-pod").ObjectMeta( builder.WithAnnotations("snapshot.velero.io/myvol", "")). Result(), @@ -143,14 +143,14 @@ func TestResticRestoreActionExecute(t *testing.T) { ObjectMeta( builder.WithAnnotations("snapshot.velero.io/myvol", "")). InitContainers( - newResticInitContainerBuilder(defaultResticRestoreHelperImage, ""). + newRestoreInitContainerBuilder(defaultRestoreHelperImage, ""). Resources(&resourceReqs). SecurityContext(&securityContext). VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()). - Command([]string{"/velero-restic-restore-helper"}).Result()).Result(), + Command([]string{"/velero-restore-helper"}).Result()).Result(), }, { - name: "Restoring pod with other initContainers adds the restic initContainer as the first one", + name: "Restoring pod with other initContainers adds the restore initContainer as the first one", pod: builder.ForPod("ns-1", "my-pod"). ObjectMeta( builder.WithAnnotations("snapshot.velero.io/myvol", "")). @@ -160,16 +160,16 @@ func TestResticRestoreActionExecute(t *testing.T) { ObjectMeta( builder.WithAnnotations("snapshot.velero.io/myvol", "")). InitContainers( - newResticInitContainerBuilder(defaultResticRestoreHelperImage, ""). + newRestoreInitContainerBuilder(defaultRestoreHelperImage, ""). Resources(&resourceReqs). SecurityContext(&securityContext). VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()). - Command([]string{"/velero-restic-restore-helper"}).Result(), + Command([]string{"/velero-restore-helper"}).Result(), builder.ForContainer("first-container", "").Result()). Result(), }, { - name: "Restoring pod with other initContainers adds the restic initContainer as the first one using PVB to identify the volumes and not annotations", + name: "Restoring pod with other initContainers adds the restore initContainer as the first one using PVB to identify the volumes and not annotations", pod: builder.ForPod("ns-1", "my-pod"). Volumes( builder.ForVolume("vol-1").PersistentVolumeClaimSource("pvc-1").Result(), @@ -203,16 +203,16 @@ func TestResticRestoreActionExecute(t *testing.T) { ObjectMeta( builder.WithAnnotations("snapshot.velero.io/not-used", "")). InitContainers( - newResticInitContainerBuilder(defaultResticRestoreHelperImage, ""). + newRestoreInitContainerBuilder(defaultRestoreHelperImage, ""). Resources(&resourceReqs). SecurityContext(&securityContext). VolumeMounts(builder.ForVolumeMount("vol-1", "/restores/vol-1").Result(), builder.ForVolumeMount("vol-2", "/restores/vol-2").Result()). - Command([]string{"/velero-restic-restore-helper"}).Result(), + Command([]string{"/velero-restore-helper"}).Result(), builder.ForContainer("first-container", "").Result()). Result(), }, { - name: "Restoring pod in another namespace adds the restic initContainer and uses the namespace of the backup pod for matching PVBs", + name: "Restoring pod in another namespace adds the restore initContainer and uses the namespace of the backup pod for matching PVBs", pod: builder.ForPod("new-ns", "my-pod"). Volumes( builder.ForVolume("vol-1").PersistentVolumeClaimSource("pvc-1").Result(), @@ -247,11 +247,11 @@ func TestResticRestoreActionExecute(t *testing.T) { builder.ForVolume("vol-2").PersistentVolumeClaimSource("pvc-2").Result(), ). InitContainers( - newResticInitContainerBuilder(defaultResticRestoreHelperImage, ""). + newRestoreInitContainerBuilder(defaultRestoreHelperImage, ""). Resources(&resourceReqs). SecurityContext(&securityContext). VolumeMounts(builder.ForVolumeMount("vol-1", "/restores/vol-1").Result(), builder.ForVolumeMount("vol-2", "/restores/vol-2").Result()). - Command([]string{"/velero-restic-restore-helper"}).Result()). + Command([]string{"/velero-restore-helper"}).Result()). Result(), }, } @@ -291,7 +291,7 @@ func TestResticRestoreActionExecute(t *testing.T) { Result(), } - a := NewResticRestoreAction( + a := NewPodVolumeRestoreAction( logrus.StandardLogger(), clientset.CoreV1().ConfigMaps(veleroNs), clientsetVelero.VeleroV1().PodVolumeBackups(veleroNs), diff --git a/pkg/restorehelper/util.go b/pkg/restorehelper/util.go new file mode 100644 index 0000000000..f42d520085 --- /dev/null +++ b/pkg/restorehelper/util.go @@ -0,0 +1,33 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restorehelper + +const ( + // WaitInitContainer is the name of the init container added + // to workload pods to help with restores. + // If Velero needs to further process the volume data after PVC is + // provisioned, this init container is used to block Pod from running + // until the volume data is ready + WaitInitContainer = "restore-wait" + + // This is the name of the init container added by pre-v1.10 for the same + // purpose with WaitInitContainer. + // For compatibility, we need to check it when restoring backups created by + // old releases. The pods backed up by old releases may contain this init container + // since the init container is not deleted after pod is restored. + WaitInitContainerLegacy = "restic-wait" +) diff --git a/pkg/uploader/provider/provider.go b/pkg/uploader/provider/provider.go index 08dbb0ee52..3d0c452dc9 100644 --- a/pkg/uploader/provider/provider.go +++ b/pkg/uploader/provider/provider.go @@ -37,9 +37,9 @@ import ( const restoreProgressCheckInterval = 10 * time.Second const backupProgressCheckInterval = 10 * time.Second -// Provider which is designed for one pod volumn to do the backup or restore +// Provider which is designed for one pod volume to do the backup or restore type Provider interface { - // RunBackup which will do backup for one specific volumn and return snapshotID, isSnapshotEmpty, error + // RunBackup which will do backup for one specific volume and return snapshotID, isSnapshotEmpty, error // updater is used for updating backup progress which implement by third-party RunBackup( ctx context.Context, @@ -47,7 +47,7 @@ type Provider interface { tags map[string]string, parentSnapshot string, updater uploader.ProgressUpdater) (string, bool, error) - // RunRestore which will do restore for one specific volumn with given snapshot id and return error + // RunRestore which will do restore for one specific volume with given snapshot id and return error // updater is used for updating backup progress which implement by third-party RunRestore( ctx context.Context, diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 6b76b8ad15..f33d330676 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -53,7 +53,7 @@ VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main VELERO_VERSION ?= $(VERSION) PLUGINS ?= -RESTIC_HELPER_IMAGE ?= +RESTORE_HELPER_IMAGE ?= #Released version only UPGRADE_FROM_VELERO_VERSION ?= v1.7.1,v1.8.1 # UPGRADE_FROM_VELERO_CLI can has the same format(a list divided by comma) with UPGRADE_FROM_VELERO_VERSION @@ -115,7 +115,7 @@ run: ginkgo -velero-image=$(VELERO_IMAGE) \ -plugins=$(PLUGINS) \ -velero-version=$(VELERO_VERSION) \ - -restic-helper-image=$(RESTIC_HELPER_IMAGE) \ + -restore-helper-image=$(RESTORE_HELPER_IMAGE) \ -upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ -upgrade-from-velero-version=$(UPGRADE_FROM_VELERO_VERSION) \ -migrate-from-velero-cli=$(MIGRATE_FROM_VELERO_CLI) \ diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index ef35b7f5da..0eb3697145 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -55,7 +55,7 @@ func init() { flag.StringVar(&VeleroCfg.Plugins, "plugins", "", "provider plugins to be tested.") flag.StringVar(&VeleroCfg.AddBSLPlugins, "additional-bsl-plugins", "", "additional plugins to be tested.") flag.StringVar(&VeleroCfg.VeleroVersion, "velero-version", "main", "image version for the velero server to be tested with.") - flag.StringVar(&VeleroCfg.ResticHelperImage, "restic-helper-image", "", "image for the velero restic restore helper to be tested.") + flag.StringVar(&VeleroCfg.RestoreHelperImage, "restore-helper-image", "", "image for the velero restore helper to be tested.") flag.StringVar(&VeleroCfg.UpgradeFromVeleroCLI, "upgrade-from-velero-cli", "", "path to the pre-upgrade velero application to use.") flag.StringVar(&VeleroCfg.UpgradeFromVeleroVersion, "upgrade-from-velero-version", "v1.7.1", "image for the pre-upgrade velero server to be tested.") flag.StringVar(&VeleroCfg.MigrateFromVeleroCLI, "migrate-from-velero-cli", "", "path to the origin velero application to use.") diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index e957ace265..8b163b111a 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -122,7 +122,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) if veleroCLI2Version.VeleroVersion != "self" { fmt.Printf("Using default images address of Velero CLI %s\n", veleroCLI2Version.VeleroVersion) OriginVeleroCfg.VeleroImage = "" - OriginVeleroCfg.ResticHelperImage = "" + OriginVeleroCfg.RestoreHelperImage = "" OriginVeleroCfg.Plugins = "" //TODO: Remove this once origin Velero version is 1.10 and upper OriginVeleroCfg.UploaderType = "" diff --git a/test/e2e/types.go b/test/e2e/types.go index 5215170706..95376e04a0 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -46,7 +46,7 @@ type VerleroConfig struct { AdditionalBSLConfig string AdditionalBSLCredentials string RegistryCredentialFile string - ResticHelperImage string + RestoreHelperImage string UpgradeFromVeleroVersion string UpgradeFromVeleroCLI string MigrateFromVeleroVersion string diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index 120deb9787..d91cae4395 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -104,13 +104,13 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC VeleroCfg.GCFrequency = "" By(fmt.Sprintf("Install the expected old version Velero (%s) for upgrade", veleroCLI2Version.VeleroVersion), func() { - //Set VeleroImage and ResticHelperImage to blank - //VeleroImage and ResticHelperImage should be the default value in originalCli + //Set VeleroImage and RestoreHelperImage to blank + //VeleroImage and RestoreHelperImage should be the default value in originalCli tmpCfgForOldVeleroInstall := VeleroCfg tmpCfgForOldVeleroInstall.UpgradeFromVeleroVersion = veleroCLI2Version.VeleroVersion tmpCfgForOldVeleroInstall.VeleroCLI = veleroCLI2Version.VeleroCLI tmpCfgForOldVeleroInstall.VeleroImage = "" - tmpCfgForOldVeleroInstall.ResticHelperImage = "" + tmpCfgForOldVeleroInstall.RestoreHelperImage = "" tmpCfgForOldVeleroInstall.Plugins = "" tmpCfgForOldVeleroInstall.UploaderType = "" tmpCfgForOldVeleroInstall.UseNodeAgent = false diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index d3730b0bb8..7f50d780cc 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -46,7 +46,7 @@ import ( type installOptions struct { *install.InstallOptions RegistryCredentialFile string - ResticHelperImage string + RestoreHelperImage string } func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnapshots bool) error { @@ -100,7 +100,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VerleroConfig, useVolumeSnaps err = installVeleroServer(ctx, veleroCfg.VeleroCLI, &installOptions{ InstallOptions: veleroInstallOptions, RegistryCredentialFile: veleroCfg.RegistryCredentialFile, - ResticHelperImage: veleroCfg.ResticHelperImage, + RestoreHelperImage: veleroCfg.RestoreHelperImage, }) if err != nil { return errors.WithMessagef(err, "Failed to install Velero in the cluster") @@ -224,14 +224,14 @@ func installVeleroServer(ctx context.Context, cli string, options *installOption args = append(args, fmt.Sprintf("--uploader-type=%v", options.UploaderType)) } - if err := createVelereResources(ctx, cli, namespace, args, options.RegistryCredentialFile, options.ResticHelperImage); err != nil { + if err := createVelereResources(ctx, cli, namespace, args, options.RegistryCredentialFile, options.RestoreHelperImage); err != nil { return err } return waitVeleroReady(ctx, namespace, options.UseNodeAgent) } -func createVelereResources(ctx context.Context, cli, namespace string, args []string, registryCredentialFile, resticHelperImage string) error { +func createVelereResources(ctx context.Context, cli, namespace string, args []string, registryCredentialFile, RestoreHelperImage string) error { args = append(args, "--dry-run", "--output", "json", "--crds-only") // get the CRD definitions @@ -276,7 +276,7 @@ func createVelereResources(ctx context.Context, cli, namespace string, args []st return errors.Wrapf(err, "failed to unmarshal the resources: %s", string(stdout)) } - if err = patchResources(ctx, resources, namespace, registryCredentialFile, VeleroCfg.ResticHelperImage); err != nil { + if err = patchResources(ctx, resources, namespace, registryCredentialFile, VeleroCfg.RestoreHelperImage); err != nil { return errors.Wrapf(err, "failed to patch resources") } @@ -298,7 +298,7 @@ func createVelereResources(ctx context.Context, cli, namespace string, args []st } // patch the velero resources -func patchResources(ctx context.Context, resources *unstructured.UnstructuredList, namespace, registryCredentialFile, resticHelperImage string) error { +func patchResources(ctx context.Context, resources *unstructured.UnstructuredList, namespace, registryCredentialFile, RestoreHelperImage string) error { // apply the image pull secret to avoid the image pull limit of Docker Hub if len(registryCredentialFile) > 0 { credential, err := ioutil.ReadFile(registryCredentialFile) @@ -342,7 +342,7 @@ func patchResources(ctx context.Context, resources *unstructured.UnstructuredLis } // customize the restic restore helper image - if len(VeleroCfg.ResticHelperImage) > 0 { + if len(VeleroCfg.RestoreHelperImage) > 0 { restoreActionConfig := corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", @@ -357,7 +357,7 @@ func patchResources(ctx context.Context, resources *unstructured.UnstructuredLis }, }, Data: map[string]string{ - "image": VeleroCfg.ResticHelperImage, + "image": VeleroCfg.RestoreHelperImage, }, } From 9cb46deb73f91ce4c36a412edcb1e20742b68b3f Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Tue, 18 Oct 2022 14:58:03 +0800 Subject: [PATCH 335/366] Add CSI VolumeSnapshot client back. (#5449) Signed-off-by: Xun Jiang --- changelogs/unreleased/5449-blackpiglet | 1 + pkg/cmd/server/server.go | 32 +++++++++++++++++- pkg/controller/backup_controller.go | 47 +++++++++++++++----------- pkg/util/csi/reset.go | 2 +- 4 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/5449-blackpiglet diff --git a/changelogs/unreleased/5449-blackpiglet b/changelogs/unreleased/5449-blackpiglet new file mode 100644 index 0000000000..3ab9934eee --- /dev/null +++ b/changelogs/unreleased/5449-blackpiglet @@ -0,0 +1 @@ +Add VolumeSnapshot client back. \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 238d155708..85c57da7c6 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -35,6 +35,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" corev1api "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/clock" @@ -49,6 +50,7 @@ import ( snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" @@ -562,6 +564,32 @@ func (s *server) initRepoManager() error { return nil } +func (s *server) getCSIVolumeSnapshotListers() snapshotv1listers.VolumeSnapshotLister { + // Make empty listers that will only be populated if CSI is properly enabled. + var vsLister snapshotv1listers.VolumeSnapshotLister + var err error + + // If CSI is enabled, check for the CSI groups and generate the listers + // If CSI isn't enabled, return empty listers. + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + _, err = s.discoveryClient.ServerResourcesForGroupVersion(snapshotv1api.SchemeGroupVersion.String()) + switch { + case apierrors.IsNotFound(err): + // CSI is enabled, but the required CRDs aren't installed, so halt. + s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", velerov1api.CSIFeatureFlag, snapshotv1api.SchemeGroupVersion.String()) + case err == nil: + // CSI is enabled, and the resources were found. + // Instantiate the listers fully + s.logger.Debug("Creating CSI listers") + // Access the wrapped factory directly here since we've already done the feature flag check above to know it's safe. + vsLister = s.csiSnapshotterSharedInformerFactory.factory.Snapshot().V1().VolumeSnapshots().Lister() + case err != nil: + cmd.CheckError(err) + } + } + return vsLister +} + func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string) error { s.logger.Info("Starting controllers") @@ -621,8 +649,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), defaultVolumeSnapshotLocations, s.metrics, - s.config.formatFlag.Parse(), backupStoreGetter, + s.config.formatFlag.Parse(), + s.getCSIVolumeSnapshotListers(), + s.csiSnapshotClient, s.credentialFileStore, ) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 39444459e4..c874201fd3 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -46,6 +46,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/csi" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" @@ -92,6 +94,8 @@ type backupController struct { metrics *metrics.ServerMetrics backupStoreGetter persistence.ObjectBackupStoreGetter formatFlag logging.Format + volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister + volumeSnapshotClient *snapshotterClientSet.Clientset credentialFileStore credentials.FileStore } @@ -112,8 +116,10 @@ func NewBackupController( volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, defaultSnapshotLocations map[string]string, metrics *metrics.ServerMetrics, - formatFlag logging.Format, backupStoreGetter persistence.ObjectBackupStoreGetter, + formatFlag logging.Format, + volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, + volumeSnapshotClient *snapshotterClientSet.Clientset, credentialStore credentials.FileStore, ) Interface { c := &backupController{ @@ -134,8 +140,10 @@ func NewBackupController( snapshotLocationLister: volumeSnapshotLocationLister, defaultSnapshotLocations: defaultSnapshotLocations, metrics: metrics, - formatFlag: formatFlag, backupStoreGetter: backupStoreGetter, + formatFlag: formatFlag, + volumeSnapshotLister: volumeSnapshotLister, + volumeSnapshotClient: volumeSnapshotClient, credentialFileStore: credentialStore, } @@ -653,19 +661,19 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { var volumeSnapshotClasses []snapshotv1api.VolumeSnapshotClass if features.IsEnabled(velerov1api.CSIFeatureFlag) { selector := label.NewSelectorForBackup(backup.Name) - // Listers are wrapped in a nil check out of caution, since they may not be populated based on the - // EnableCSI feature flag. This is more to guard against programmer error, as they shouldn't be nil - // when EnableCSI is on. - vsList := &snapshotv1api.VolumeSnapshotList{} vscList := &snapshotv1api.VolumeSnapshotContentList{} - err = c.kbClient.List(context.Background(), vsList, &kbclient.ListOptions{LabelSelector: selector}) - if err != nil { - backupLog.Error(err) - } - if len(vsList.Items) >= 0 { - volumeSnapshots = vsList.Items + if c.volumeSnapshotLister != nil { + tmpVSs, err := c.volumeSnapshotLister.List(selector) + if err != nil { + backupLog.Error(err) + } + + for _, vs := range tmpVSs { + volumeSnapshots = append(volumeSnapshots, *vs) + } } + err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots, backup.Spec.CSISnapshotTimeout.Duration) if err != nil { backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) @@ -681,19 +689,19 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } vsClassSet := sets.NewString() - for _, vsc := range volumeSnapshotContents { + for index := range volumeSnapshotContents { // persist the volumesnapshotclasses referenced by vsc - if vsc.Spec.VolumeSnapshotClassName != nil && !vsClassSet.Has(*vsc.Spec.VolumeSnapshotClassName) { + if volumeSnapshotContents[index].Spec.VolumeSnapshotClassName != nil && !vsClassSet.Has(*volumeSnapshotContents[index].Spec.VolumeSnapshotClassName) { vsClass := &snapshotv1api.VolumeSnapshotClass{} - if err := c.kbClient.Get(context.TODO(), kbclient.ObjectKey{Name: *vsc.Spec.VolumeSnapshotClassName}, vsClass); err != nil { + if err := c.kbClient.Get(context.TODO(), kbclient.ObjectKey{Name: *volumeSnapshotContents[index].Spec.VolumeSnapshotClassName}, vsClass); err != nil { backupLog.Error(err) } else { - vsClassSet.Insert(*vsc.Spec.VolumeSnapshotClassName) + vsClassSet.Insert(*volumeSnapshotContents[index].Spec.VolumeSnapshotClassName) volumeSnapshotClasses = append(volumeSnapshotClasses, *vsClass) } } - if err := csi.ResetVolumeSnapshotContent(vsc); err != nil { + if err := csi.ResetVolumeSnapshotContent(&volumeSnapshotContents[index]); err != nil { backupLog.Error(err) } } @@ -917,8 +925,7 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo volumeSnapshot := vs eg.Go(func() error { err := wait.PollImmediate(interval, timeout, func() (bool, error) { - tmpVS := &snapshotv1api.VolumeSnapshot{} - err := c.kbClient.Get(ctx, kbclient.ObjectKey{Name: volumeSnapshot.Name, Namespace: volumeSnapshot.Namespace}, tmpVS) + tmpVS, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(volumeSnapshot.Namespace).Get(ctx, volumeSnapshot.Name, metav1.GetOptions{}) if err != nil { return false, errors.Wrapf(err, fmt.Sprintf("failed to get volumesnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)) } @@ -995,7 +1002,7 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api. // Delete VolumeSnapshot from cluster logger.Debugf("Deleting VolumeSnapshotContent %s", vsc.Name) - err := c.kbClient.Delete(context.TODO(), &vs) + err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(vs.Namespace).Delete(context.TODO(), vs.Name, metav1.DeleteOptions{}) if err != nil { logger.Errorf("fail to delete VolumeSnapshot %s/%s: %s", vs.Namespace, vs.Name, err.Error()) } diff --git a/pkg/util/csi/reset.go b/pkg/util/csi/reset.go index f762f8aaa6..5065aae780 100644 --- a/pkg/util/csi/reset.go +++ b/pkg/util/csi/reset.go @@ -27,7 +27,7 @@ import ( // It will move the snapshot Handle to the source to avoid the snapshot-controller creating a snapshot when it's // synced by the backup sync controller. // It will return an error if the snapshot handle is not set, which should not happen when this func is called. -func ResetVolumeSnapshotContent(snapCont snapshotv1api.VolumeSnapshotContent) error { +func ResetVolumeSnapshotContent(snapCont *snapshotv1api.VolumeSnapshotContent) error { if snapCont.Status != nil && snapCont.Status.SnapshotHandle != nil && len(*snapCont.Status.SnapshotHandle) > 0 { v := *snapCont.Status.SnapshotHandle snapCont.Spec.Source = snapshotv1api.VolumeSnapshotContentSource{ From df5436b38025cb0b0e06a4cceffa3c863307428d Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 18 Oct 2022 19:38:28 +0800 Subject: [PATCH 336/366] upgrade velero docker image Signed-off-by: Lyndon-Li --- golangci.yaml | 12 ++++++------ hack/build-image/Dockerfile | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/golangci.yaml b/golangci.yaml index 0b6da27f7c..8c6d87b63e 100644 --- a/golangci.yaml +++ b/golangci.yaml @@ -25,8 +25,8 @@ run: # from this option's value (see skip-dirs-use-default). # "/" will be replaced by current OS file path separator to properly work # on Windows. - #skip-dirs: - # - src/external_libs + skip-dirs: + - test/e2e/* # - autogenerated_by_my_lib # default is true. Enables skipping of directories: @@ -39,8 +39,8 @@ run: # autogenerated files. If it's not please let us know. # "/" will be replaced by current OS file path separator to properly work # on Windows. - # skip-files: - # - ".*\\.my\\.go$" + skip-files: + - ".*_test.go$" # - lib/bad.go # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": @@ -320,7 +320,7 @@ linters: fast: false -#issues: +issues: # # List of regexps of issue texts to exclude, empty list by default. # # But independently from this option we use default exclude patterns, # # it can be disabled by `exclude-use-default: false`. To list all @@ -359,7 +359,7 @@ linters: # it can be disabled by this option. To list all # excluded by default patterns execute `golangci-lint run --help`. # Default value for this option is true. - exclude-use-default: false + exclude-use-default: true # The default value is false. If set to true exclude and exclude-rules # regular expressions become case sensitive. diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 5baa4365e2..b3c5e42a04 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.17 +FROM golang:1.18 ARG GOPROXY @@ -36,11 +36,11 @@ RUN wget --quiet https://github.com/kubernetes-sigs/kubebuilder/releases/downloa chmod +x /usr/local/kubebuilder/bin/kubebuilder # get controller-tools -RUN go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 +RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 # get goimports (the revision is pinned so we don't indiscriminately update, but the particular commit # is not important) -RUN go get golang.org/x/tools/cmd/goimports@11e9d9cc0042e6bd10337d4d2c3e5d9295508e7d +RUN go install golang.org/x/tools/cmd/goimports@11e9d9cc0042e6bd10337d4d2c3e5d9295508e7d # get protoc compiler and golang plugin WORKDIR /root @@ -49,7 +49,7 @@ RUN wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v unzip protoc-3.14.0-linux-x86_64.zip && \ mv bin/protoc /usr/bin/protoc && \ chmod +x /usr/bin/protoc -RUN go get github.com/golang/protobuf/protoc-gen-go@v1.4.3 +RUN go install github.com/golang/protobuf/protoc-gen-go@v1.4.3 # get goreleaser RUN wget --quiet https://github.com/goreleaser/goreleaser/releases/download/v0.120.8/goreleaser_Linux_x86_64.tar.gz && \ @@ -58,7 +58,7 @@ RUN wget --quiet https://github.com/goreleaser/goreleaser/releases/download/v0.1 chmod +x /usr/bin/goreleaser # get golangci-lint -RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.0 # install kubectl RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl From d7b4583b2b22b4df0c1c0d3c54b412ca4ae77d0e Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 18 Oct 2022 21:08:55 +0800 Subject: [PATCH 337/366] fix lint errors Signed-off-by: Lyndon-Li --- pkg/archive/extractor.go | 2 +- pkg/backup/item_collector.go | 4 +- pkg/cmd/cli/backup/create.go | 10 +++-- pkg/cmd/cli/backup/describe.go | 2 +- pkg/cmd/cli/backuplocation/create.go | 2 +- pkg/cmd/cli/backuplocation/delete.go | 6 +-- pkg/cmd/cli/backuplocation/set.go | 2 +- pkg/cmd/cli/nodeagent/server.go | 4 +- pkg/cmd/cli/restore/create.go | 12 ++++-- pkg/cmd/cli/restore/describe.go | 2 +- pkg/cmd/cli/schedule/describe.go | 2 +- pkg/cmd/server/server.go | 4 +- .../util/downloadrequest/downloadrequest.go | 2 +- pkg/cmd/util/output/backup_describer.go | 14 +++---- pkg/cmd/util/output/output.go | 5 ++- pkg/cmd/util/output/restore_describer.go | 8 ++-- pkg/controller/backup_controller.go | 7 ++-- pkg/controller/backup_deletion_controller.go | 8 ++-- pkg/controller/backup_sync_controller.go | 8 ++-- .../pod_volume_restore_controller.go | 2 +- pkg/controller/restore_controller.go | 8 ++-- pkg/nodeagent/node_agent.go | 2 +- .../clientmgmt/process/client_builder.go | 2 +- pkg/repository/config/aws.go | 1 + pkg/repository/config/azure.go | 38 +++++++++---------- pkg/repository/config/gcp.go | 1 + pkg/repository/keys/keys.go | 1 + pkg/repository/provider/unified_repo.go | 6 +-- pkg/restic/command.go | 2 +- pkg/restore/admissionwebhook_config_action.go | 4 +- pkg/restore/restore.go | 2 +- pkg/util/kube/utils.go | 2 +- 32 files changed, 94 insertions(+), 81 deletions(-) diff --git a/pkg/archive/extractor.go b/pkg/archive/extractor.go index 7a0dfce74b..1bd16c2586 100644 --- a/pkg/archive/extractor.go +++ b/pkg/archive/extractor.go @@ -84,7 +84,7 @@ func (e *Extractor) readBackup(tarRdr *tar.Reader) (string, error) { return "", err } - target := filepath.Join(dir, header.Name) + target := filepath.Join(dir, header.Name) //nolint:gosec switch header.Typeflag { case tar.TypeDir: diff --git a/pkg/backup/item_collector.go b/pkg/backup/item_collector.go index 474fbabee3..88be4e0789 100644 --- a/pkg/backup/item_collector.go +++ b/pkg/backup/item_collector.go @@ -151,7 +151,7 @@ func sortResourcesByOrder(log logrus.FieldLogger, items []*kubernetesResource, o } // getOrderedResourcesForType gets order of resourceType from orderResources. -func getOrderedResourcesForType(log logrus.FieldLogger, orderedResources map[string]string, resourceType string) []string { +func getOrderedResourcesForType(orderedResources map[string]string, resourceType string) []string { if orderedResources == nil { return nil } @@ -175,7 +175,7 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group clusterScoped = !resource.Namespaced ) - orders := getOrderedResourcesForType(log, r.backupRequest.Backup.Spec.OrderedResources, resource.Name) + orders := getOrderedResourcesForType(r.backupRequest.Backup.Spec.OrderedResources, resource.Name) // Getting the preferred group version of this resource preferredGVR, _, err := r.discoveryHelper.ResourceFor(gr.WithVersion("")) if err != nil { diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 52aeab3d52..27c0be3729 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -112,6 +112,10 @@ func NewCreateOptions() *CreateOptions { } } +const ( + strTrue = "true" +) + func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.DurationVar(&o.TTL, "ttl", o.TTL, "How long before the backup can be garbage collected.") flags.Var(&o.IncludeNamespaces, "include-namespaces", "Namespaces to include in the backup (use '*' for all namespaces).") @@ -127,13 +131,13 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f := flags.VarPF(&o.SnapshotVolumes, "snapshot-volumes", "", "Take snapshots of PersistentVolumes as part of the backup. If the parameter is not set, it is treated as setting to 'true'.") // this allows the user to just specify "--snapshot-volumes" as shorthand for "--snapshot-volumes=true" // like a normal bool flag - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the backup") - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue f = flags.VarPF(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", "", "Use pod volume file system backup by default for volumes") - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue } // BindWait binds the wait flag separately so it is not called by other create diff --git a/pkg/cmd/cli/backup/describe.go b/pkg/cmd/cli/backup/describe.go index 82ca472980..3e5ebbded3 100644 --- a/pkg/cmd/cli/backup/describe.go +++ b/pkg/cmd/cli/backup/describe.go @@ -102,7 +102,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { } } - s := output.DescribeBackup(context.Background(), kbClient, &backup, deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) + s := output.DescribeBackup(context.Background(), kbClient, &backup, deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) //nolint if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index a90564be3a..ec19b6e56d 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -212,7 +212,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { for _, location := range locations.Items { if location.Spec.Default { location.Spec.Default = false - if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { + if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { //nolint return errors.WithStack(err) } break diff --git a/pkg/cmd/cli/backuplocation/delete.go b/pkg/cmd/cli/backuplocation/delete.go index c04c74f42e..d67ed54369 100644 --- a/pkg/cmd/cli/backuplocation/delete.go +++ b/pkg/cmd/cli/backuplocation/delete.go @@ -116,7 +116,7 @@ func Run(f client.Factory, o *cli.DeleteOptions) error { // Create a backup-location deletion request for each for _, location := range locations.Items { - if err := kbClient.Delete(context.Background(), &location, &kbclient.DeleteOptions{}); err != nil { + if err := kbClient.Delete(context.Background(), &location, &kbclient.DeleteOptions{}); err != nil { //nolint errs = append(errs, errors.WithStack(err)) continue } @@ -163,7 +163,7 @@ func findAssociatedBackupRepos(client kbclient.Client, bslName, ns string) (vele func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []error { var errs []error for _, backup := range backups.Items { - if err := client.Delete(context.Background(), &backup, &kbclient.DeleteOptions{}); err != nil { + if err := client.Delete(context.Background(), &backup, &kbclient.DeleteOptions{}); err != nil { //nolint errs = append(errs, errors.WithStack(fmt.Errorf("delete backup %q associated with deleted BSL: %w", backup.Name, err))) continue } @@ -175,7 +175,7 @@ func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []err func deleteBackupRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { var errs []error for _, repo := range repos.Items { - if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { + if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { //nolint errs = append(errs, errors.WithStack(fmt.Errorf("delete backup repository %q associated with deleted BSL: %w", repo.Name, err))) continue } diff --git a/pkg/cmd/cli/backuplocation/set.go b/pkg/cmd/cli/backuplocation/set.go index dda731ac6d..44730d659d 100644 --- a/pkg/cmd/cli/backuplocation/set.go +++ b/pkg/cmd/cli/backuplocation/set.go @@ -129,7 +129,7 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error { break } location.Spec.Default = false - if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { + if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { //nolint return errors.WithStack(err) } break diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 373aa1161f..7fb3dfc827 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -307,7 +307,7 @@ func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed pvb.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) pvb.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(s.ctx, &pvb, ctrlclient.MergeFrom(original)); err != nil { + if err := client.Patch(s.ctx, &pvb, ctrlclient.MergeFrom(original)); err != nil { //nolint s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) continue } @@ -345,7 +345,7 @@ func (s *nodeAgentServer) markInProgressPVRsFailed(client ctrlclient.Client) { pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed pvr.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) pvr.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(s.ctx, &pvr, ctrlclient.MergeFrom(original)); err != nil { + if err := client.Patch(s.ctx, &pvr, ctrlclient.MergeFrom(original)); err != nil { //nolint s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) continue } diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index 7625c1f762..2ac45e8ee8 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -107,6 +107,10 @@ func NewCreateOptions() *CreateOptions { } } +const ( + strTrue = "true" +) + func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.BackupName, "from-backup", "", "Backup to restore from") flags.StringVar(&o.ScheduleName, "from-schedule", "", "Schedule to restore from") @@ -123,18 +127,18 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f := flags.VarPF(&o.RestoreVolumes, "restore-volumes", "", "Whether to restore volumes from snapshots.") // this allows the user to just specify "--restore-volumes" as shorthand for "--restore-volumes=true" // like a normal bool flag - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue f = flags.VarPF(&o.PreserveNodePorts, "preserve-nodeports", "", "Whether to preserve nodeports of Services when restoring.") // this allows the user to just specify "--preserve-nodeports" as shorthand for "--preserve-nodeports=true" // like a normal bool flag - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the restore.") - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue f = flags.VarPF(&o.AllowPartiallyFailed, "allow-partially-failed", "", "If using --from-schedule, whether to consider PartiallyFailed backups when looking for the most recent one. This flag has no effect if not using --from-schedule.") - f.NoOptDefVal = "true" + f.NoOptDefVal = strTrue flags.BoolVarP(&o.Wait, "wait", "w", o.Wait, "Wait for the operation to complete.") } diff --git a/pkg/cmd/cli/restore/describe.go b/pkg/cmd/cli/restore/describe.go index 76ef3a43b0..e8cbb8d885 100644 --- a/pkg/cmd/cli/restore/describe.go +++ b/pkg/cmd/cli/restore/describe.go @@ -75,7 +75,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { fmt.Fprintf(os.Stderr, "error getting PodVolumeRestores for restore %s: %v\n", restore.Name, err) } - s := output.DescribeRestore(context.Background(), kbClient, &restore, podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) + s := output.DescribeRestore(context.Background(), kbClient, &restore, podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) //nolint if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/schedule/describe.go b/pkg/cmd/cli/schedule/describe.go index ba6972a96c..5c64610436 100644 --- a/pkg/cmd/cli/schedule/describe.go +++ b/pkg/cmd/cli/schedule/describe.go @@ -54,7 +54,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { first := true for _, schedule := range schedules.Items { - s := output.DescribeSchedule(&schedule) + s := output.DescribeSchedule(&schedule) //nolint if first { first = false fmt.Print(s) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 238d155708..e66a6275f1 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -948,7 +948,7 @@ func markInProgressBackupsFailed(ctx context.Context, client ctrlclient.Client, updated.Status.Phase = velerov1api.BackupPhaseFailed updated.Status.FailureReason = fmt.Sprintf("get a backup with status %q during the server starting, mark it as %q", velerov1api.BackupPhaseInProgress, updated.Status.Phase) updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&backup)); err != nil { + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&backup)); err != nil { //nolint log.WithError(errors.WithStack(err)).Errorf("failed to patch backup %q", backup.GetName()) continue } @@ -971,7 +971,7 @@ func markInProgressRestoresFailed(ctx context.Context, client ctrlclient.Client, updated.Status.Phase = velerov1api.RestorePhaseFailed updated.Status.FailureReason = fmt.Sprintf("get a restore with status %q during the server starting, mark it as %q", velerov1api.RestorePhaseInProgress, updated.Status.Phase) updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&restore)); err != nil { + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&restore)); err != nil { //nolint log.WithError(errors.WithStack(err)).Errorf("failed to patch restore %q", restore.GetName()) continue } diff --git a/pkg/cmd/util/downloadrequest/downloadrequest.go b/pkg/cmd/util/downloadrequest/downloadrequest.go index f131b32e0c..902dcd7982 100644 --- a/pkg/cmd/util/downloadrequest/downloadrequest.go +++ b/pkg/cmd/util/downloadrequest/downloadrequest.go @@ -111,7 +111,7 @@ func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name strin httpClient := new(http.Client) httpClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecureSkipTLSVerify, + InsecureSkipVerify: insecureSkipTLSVerify, //nolint:gosec RootCAs: caPool, }, IdleConnTimeout: timeout, diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index a6a2f6f996..b17267444e 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -125,7 +125,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } d.Printf("\tIncluded:\t%s\n", s) if len(spec.ExcludedNamespaces) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(spec.ExcludedNamespaces, ", ") } @@ -140,7 +140,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } d.Printf("\tIncluded:\t%s\n", s) if len(spec.ExcludedResources) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(spec.ExcludedResources, ", ") } @@ -149,7 +149,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { d.Printf("\tCluster-scoped:\t%s\n", BoolPointerString(spec.IncludeClusterResources, "excluded", "included", "auto")) d.Println() - s = "" + s = emptyDisplay if spec.LabelSelector != nil { s = metav1.FormatLabelSelector(spec.LabelSelector) } @@ -169,7 +169,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { d.Println() if len(spec.Hooks.Resources) == 0 { - d.Printf("Hooks:\t\n") + d.Printf("Hooks:\t" + emptyDisplay + "\n") } else { d.Printf("Hooks:\n") d.Printf("\tResources:\n") @@ -184,7 +184,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } d.Printf("\t\t\t\tIncluded:\t%s\n", s) if len(spec.ExcludedNamespaces) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(spec.ExcludedNamespaces, ", ") } @@ -199,14 +199,14 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } d.Printf("\t\t\t\tIncluded:\t%s\n", s) if len(spec.ExcludedResources) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(spec.ExcludedResources, ", ") } d.Printf("\t\t\t\tExcluded:\t%s\n", s) d.Println() - s = "" + s = emptyDisplay if backupResourceHookSpec.LabelSelector != nil { s = metav1.FormatLabelSelector(backupResourceHookSpec.LabelSelector) } diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index bfba88f15e..1a520b0aea 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -34,7 +34,10 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/encode" ) -const downloadRequestTimeout = 30 * time.Second +const ( + downloadRequestTimeout = 30 * time.Second + emptyDisplay = "" +) // BindFlags defines a set of output-specific flags within the provided // FlagSet. diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index f6b5f2b378..631dbb2cb0 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -107,7 +107,7 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel } d.Printf("\tIncluded:\t%s\n", s) if len(restore.Spec.ExcludedNamespaces) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(restore.Spec.ExcludedNamespaces, ", ") } @@ -122,7 +122,7 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel } d.Printf("\tIncluded:\t%s\n", s) if len(restore.Spec.ExcludedResources) == 0 { - s = "" + s = emptyDisplay } else { s = strings.Join(restore.Spec.ExcludedResources, ", ") } @@ -134,7 +134,7 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel d.DescribeMap("Namespace mappings", restore.Spec.NamespaceMapping) d.Println() - s = "" + s = emptyDisplay if restore.Spec.LabelSelector != nil { s = metav1.FormatLabelSelector(restore.Spec.LabelSelector) } @@ -149,7 +149,7 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel } d.Println() - s = "" + s = emptyDisplay if restore.Spec.ExistingResourcePolicy != "" { s = string(restore.Spec.ExistingResourcePolicy) } diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 39444459e4..bf5ddc9325 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -699,7 +699,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } // Delete the VolumeSnapshots created in the backup, when CSI feature is enabled. - c.deleteVolumeSnapshot(volumeSnapshots, volumeSnapshotContents, *backup, backupLog) + c.deleteVolumeSnapshot(volumeSnapshots, volumeSnapshotContents, backupLog) } @@ -751,7 +751,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { return err } - if errs := persistBackup(backup, backupFile, logFile, backupStore, c.logger.WithField(Backup, kubeutil.NamespaceAndName(backup)), volumeSnapshots, volumeSnapshotContents, volumeSnapshotClasses); len(errs) > 0 { + if errs := persistBackup(backup, backupFile, logFile, backupStore, volumeSnapshots, volumeSnapshotContents, volumeSnapshotClasses); len(errs) > 0 { fatalErrs = append(fatalErrs, errs...) } @@ -795,7 +795,6 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac func persistBackup(backup *pkgbackup.Request, backupContents, backupLog *os.File, backupStore persistence.BackupStore, - log logrus.FieldLogger, csiVolumeSnapshots []snapshotv1api.VolumeSnapshot, csiVolumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, csiVolumesnapshotClasses []snapshotv1api.VolumeSnapshotClass, @@ -945,7 +944,7 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo // change DeletionPolicy to Retain before deleting VS, then change DeletionPolicy back to Delete. func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api.VolumeSnapshot, volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, - backup pkgbackup.Request, logger logrus.FieldLogger) { + logger logrus.FieldLogger) { var wg sync.WaitGroup vscMap := make(map[string]snapshotv1api.VolumeSnapshotContent) for _, vsc := range volumeSnapshotContents { diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 371179e56c..7b188d98b3 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -329,7 +329,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque if restore.Spec.BackupName != backup.Name { continue } - restoreLog := log.WithField("restore", kube.NamespaceAndName(&restore)) + restoreLog := log.WithField("restore", kube.NamespaceAndName(&restore)) //nolint restoreLog.Info("Deleting restore log/results from backup storage") if err := backupStore.DeleteRestore(restore.Name); err != nil { @@ -339,8 +339,8 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque } restoreLog.Info("Deleting restore referencing backup") - if err := r.Delete(ctx, &restore); err != nil { - errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(&restore)).Error()) + if err := r.Delete(ctx, &restore); err != nil { //nolint + errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(&restore)).Error()) //nolint } } } @@ -427,7 +427,7 @@ func (r *backupDeletionReconciler) deleteExistingDeletionRequests(ctx context.Co if dbr.Name == req.Name { continue } - if err := r.Delete(ctx, &dbr); err != nil { + if err := r.Delete(ctx, &dbr); err != nil { //nolint errs = append(errs, errors.WithStack(err)) } else { log.Infof("deletion request '%s' removed.", dbr.Name) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 8352e3ccd6..30ebe3b094 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -323,7 +323,7 @@ func (b *backupSyncReconciler) deleteOrphanedBackups(ctx context.Context, locati continue } - if err := b.client.Delete(ctx, &backup, &client.DeleteOptions{}); err != nil { + if err := b.client.Delete(ctx, &backup, &client.DeleteOptions{}); err != nil { //nolint log.WithError(errors.WithStack(err)).Error("Error deleting orphaned backup from cluster") } else { log.Debug("Deleted orphaned backup from cluster") @@ -347,7 +347,7 @@ func (b *backupSyncReconciler) deleteCSISnapshotsByBackup(ctx context.Context, b for _, vs := range vsList.Items { name := kube.NamespaceAndName(vs.GetObjectMeta()) log.Debugf("Deleting volumesnapshot %s", name) - if err := b.client.Delete(context.TODO(), &vs); err != nil { + if err := b.client.Delete(context.TODO(), &vs); err != nil { //nolint log.WithError(err).Warnf("Failed to delete volumesnapshot %s", name) } } @@ -379,11 +379,11 @@ func backupSyncSourceOrderFunc(objList client.ObjectList) client.ObjectList { bslArray = append(bslArray, &inputBSLList.Items[i]) // append everything before the default for _, bsl := range inputBSLList.Items[:i] { - bslArray = append(bslArray, &bsl) + bslArray = append(bslArray, &bsl) //nolint } // append everything after the default for _, bsl := range inputBSLList.Items[i+1:] { - bslArray = append(bslArray, &bsl) + bslArray = append(bslArray, &bsl) //nolint } meta.SetList(resultBSLList, bslArray) diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 9c78171ae9..5bdaf80390 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -302,7 +302,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve // Write a done file with name= into the just-created .velero dir // within the volume. The velero init container on the pod is waiting // for this file to exist in each restored volume before completing. - if err := ioutil.WriteFile(filepath.Join(volumePath, ".velero", string(restoreUID)), nil, 0644); err != nil { + if err := ioutil.WriteFile(filepath.Join(volumePath, ".velero", string(restoreUID)), nil, 0644); err != nil { //nolint:gosec return errors.Wrap(err, "error writing done file") } diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index d14917589b..038e971095 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -463,7 +463,7 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli func (c *restoreController) runValidatedRestore(restore *api.Restore, info backupInfo) error { // instantiate the per-restore logger that will output both to a temp file // (for upload to object storage) and to stdout. - restoreLog, err := newRestoreLogger(restore, c.logger, c.restoreLogLevel, c.logFormat) + restoreLog, err := newRestoreLogger(restore, c.restoreLogLevel, c.logFormat) if err != nil { return err } @@ -577,14 +577,14 @@ func (c *restoreController) runValidatedRestore(restore *api.Restore, info backu "errors": restoreErrors, } - if err := putResults(restore, m, info.backupStore, c.logger); err != nil { + if err := putResults(restore, m, info.backupStore); err != nil { c.logger.WithError(err).Error("Error uploading restore results to backup storage") } return nil } -func putResults(restore *api.Restore, results map[string]pkgrestore.Result, backupStore persistence.BackupStore, log logrus.FieldLogger) error { +func putResults(restore *api.Restore, results map[string]pkgrestore.Result, backupStore persistence.BackupStore) error { buf := new(bytes.Buffer) gzw := gzip.NewWriter(buf) defer gzw.Close() @@ -669,7 +669,7 @@ type restoreLogger struct { w *gzip.Writer } -func newRestoreLogger(restore *api.Restore, baseLogger logrus.FieldLogger, logLevel logrus.Level, logFormat logging.Format) (*restoreLogger, error) { +func newRestoreLogger(restore *api.Restore, logLevel logrus.Level, logFormat logging.Format) (*restoreLogger, error) { file, err := ioutil.TempFile("", "") if err != nil { return nil, errors.Wrap(err, "error creating temp file") diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index cf1d32fbb5..091c20b51b 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -62,7 +62,7 @@ func IsRunningInNode(ctx context.Context, namespace string, nodeName string, pod } for _, pod := range pods.Items { - if kube.IsPodRunning(&pod) != nil { + if kube.IsPodRunning(&pod) != nil { //nolint continue } diff --git a/pkg/plugin/clientmgmt/process/client_builder.go b/pkg/plugin/clientmgmt/process/client_builder.go index 4b6c27731f..aaa56661a3 100644 --- a/pkg/plugin/clientmgmt/process/client_builder.go +++ b/pkg/plugin/clientmgmt/process/client_builder.go @@ -77,7 +77,7 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig { string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(b.clientLogger)), }, Logger: b.pluginLogger, - Cmd: exec.Command(b.commandName, b.commandArgs...), + Cmd: exec.Command(b.commandName, b.commandArgs...), //nolint } } diff --git a/pkg/repository/config/aws.go b/pkg/repository/config/aws.go index 0ff4ca218a..a3c63b949c 100644 --- a/pkg/repository/config/aws.go +++ b/pkg/repository/config/aws.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:gosec package config import ( diff --git a/pkg/repository/config/azure.go b/pkg/repository/config/azure.go index 8c5871c527..c90c4f0ac7 100644 --- a/pkg/repository/config/azure.go +++ b/pkg/repository/config/azure.go @@ -50,18 +50,18 @@ func getSubscriptionID(config map[string]string) string { return os.Getenv(subscriptionIDEnvVar) } -func getStorageAccountKey(config map[string]string) (string, *azure.Environment, error) { +func getStorageAccountKey(config map[string]string) (string, error) { credentialsFile := selectCredentialsFile(config) if err := loadCredentialsIntoEnv(credentialsFile); err != nil { - return "", nil, err + return "", err } // Get Azure cloud from AZURE_CLOUD_NAME, if it exists. If the env var does not // exist, parseAzureEnvironment will return azure.PublicCloud. env, err := parseAzureEnvironment(os.Getenv(cloudNameEnvVar)) if err != nil { - return "", nil, errors.Wrap(err, "unable to parse azure cloud name environment variable") + return "", errors.Wrap(err, "unable to parse azure cloud name environment variable") } // Get storage key from secret using key config[storageAccountKeyEnvVarConfigKey]. If the config does not @@ -69,21 +69,21 @@ func getStorageAccountKey(config map[string]string) (string, *azure.Environment, if secretKeyEnvVar := config[storageAccountKeyEnvVarConfigKey]; secretKeyEnvVar != "" { storageKey := os.Getenv(secretKeyEnvVar) if storageKey == "" { - return "", env, errors.Errorf("no storage key secret with key %s found", secretKeyEnvVar) + return "", errors.Errorf("no storage key secret with key %s found", secretKeyEnvVar) } - return storageKey, env, nil + return storageKey, nil } // get subscription ID from object store config or AZURE_SUBSCRIPTION_ID environment variable subscriptionID := getSubscriptionID(config) if subscriptionID == "" { - return "", nil, errors.New("azure subscription ID not found in object store's config or in environment variable") + return "", errors.New("azure subscription ID not found in object store's config or in environment variable") } // we need config["resourceGroup"], config["storageAccount"] - if _, err := getRequiredValues(mapLookup(config), resourceGroupConfigKey, storageAccountConfigKey); err != nil { - return "", env, errors.Wrap(err, "unable to get all required config values") + if err := getRequiredValues(mapLookup(config), resourceGroupConfigKey, storageAccountConfigKey); err != nil { + return "", errors.Wrap(err, "unable to get all required config values") } // get authorizer from environment in the following order: @@ -93,7 +93,7 @@ func getStorageAccountKey(config map[string]string) (string, *azure.Environment, // 4. MSI (managed service identity) authorizer, err := auth.NewAuthorizerFromEnvironment() if err != nil { - return "", nil, errors.Wrap(err, "error getting authorizer from environment") + return "", errors.Wrap(err, "error getting authorizer from environment") } // get storageAccountsClient @@ -103,10 +103,10 @@ func getStorageAccountKey(config map[string]string) (string, *azure.Environment, // get storage key res, err := storageAccountsClient.ListKeys(context.TODO(), config[resourceGroupConfigKey], config[storageAccountConfigKey], storagemgmt.Kerb) if err != nil { - return "", env, errors.WithStack(err) + return "", errors.WithStack(err) } if res.Keys == nil || len(*res.Keys) == 0 { - return "", env, errors.New("No storage keys found") + return "", errors.New("No storage keys found") } var storageKey string @@ -120,10 +120,10 @@ func getStorageAccountKey(config map[string]string) (string, *azure.Environment, } if storageKey == "" { - return "", env, errors.New("No storage key with Full permissions found") + return "", errors.New("No storage key with Full permissions found") } - return storageKey, env, nil + return storageKey, nil } func mapLookup(data map[string]string) func(string) string { @@ -136,12 +136,12 @@ func mapLookup(data map[string]string) func(string) string { // relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based // on info in the provided object storage location config map. func GetAzureResticEnvVars(config map[string]string) (map[string]string, error) { - storageAccountKey, _, err := getStorageAccountKey(config) + storageAccountKey, err := getStorageAccountKey(config) if err != nil { return nil, err } - if _, err := getRequiredValues(mapLookup(config), storageAccountConfigKey); err != nil { + if err := getRequiredValues(mapLookup(config), storageAccountConfigKey); err != nil { return nil, errors.Wrap(err, "unable to get all required config values") } @@ -191,7 +191,7 @@ func parseAzureEnvironment(cloudName string) (*azure.Environment, error) { return &env, errors.WithStack(err) } -func getRequiredValues(getValue func(string) string, keys ...string) (map[string]string, error) { +func getRequiredValues(getValue func(string) string, keys ...string) error { missing := []string{} results := map[string]string{} @@ -204,10 +204,10 @@ func getRequiredValues(getValue func(string) string, keys ...string) (map[string } if len(missing) > 0 { - return nil, errors.Errorf("the following keys do not have values: %s", strings.Join(missing, ", ")) + return errors.Errorf("the following keys do not have values: %s", strings.Join(missing, ", ")) } - return results, nil + return nil } // GetAzureStorageDomain gets the Azure storage domain required by a Azure blob connection, @@ -221,7 +221,7 @@ func GetAzureStorageDomain(config map[string]string) string { } func GetAzureCredentials(config map[string]string) (string, string, error) { - storageAccountKey, _, err := getStorageAccountKey(config) + storageAccountKey, err := getStorageAccountKey(config) if err != nil { return "", "", err } diff --git a/pkg/repository/config/gcp.go b/pkg/repository/config/gcp.go index ed9e3ec6a8..88b5d5ff35 100644 --- a/pkg/repository/config/gcp.go +++ b/pkg/repository/config/gcp.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:gosec package config import "os" diff --git a/pkg/repository/keys/keys.go b/pkg/repository/keys/keys.go index 2ec8bc0ba3..48d3bd75ca 100644 --- a/pkg/repository/keys/keys.go +++ b/pkg/repository/keys/keys.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:gosec package keys import ( diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 6bb4f38445..30d1c97aaa 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -280,12 +280,12 @@ func (urp *unifiedRepoProvider) DefaultMaintenanceFrequency(ctx context.Context, } func (urp *unifiedRepoProvider) GetPassword(param interface{}) (string, error) { - repoParam, ok := param.(RepoParam) + _, ok := param.(RepoParam) if !ok { return "", errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param) } - repoPassword, err := getRepoPassword(urp.credentialGetter.FromSecret, repoParam) + repoPassword, err := getRepoPassword(urp.credentialGetter.FromSecret) if err != nil { return "", errors.Wrap(err, "error to get repo password") } @@ -330,7 +330,7 @@ func (urp *unifiedRepoProvider) GetStoreOptions(param interface{}) (map[string]s return storeOptions, nil } -func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) { +func getRepoPassword(secretStore credentials.SecretStore) (string, error) { if secretStore == nil { return "", errors.New("invalid credentials interface") } diff --git a/pkg/restic/command.go b/pkg/restic/command.go index d06968018f..a484a53161 100644 --- a/pkg/restic/command.go +++ b/pkg/restic/command.go @@ -77,7 +77,7 @@ func (c *Command) String() string { // Cmd returns an exec.Cmd for the command. func (c *Command) Cmd() *exec.Cmd { parts := c.StringSlice() - cmd := exec.Command(parts[0], parts[1:]...) + cmd := exec.Command(parts[0], parts[1:]...) //nolint cmd.Dir = c.Dir if len(c.Env) > 0 { diff --git a/pkg/restore/admissionwebhook_config_action.go b/pkg/restore/admissionwebhook_config_action.go index 8fd5c1693e..7a617b6f82 100644 --- a/pkg/restore/admissionwebhook_config_action.go +++ b/pkg/restore/admissionwebhook_config_action.go @@ -72,13 +72,13 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemA newWebhooks := make([]interface{}, 0) for i, entry := range webhooks { logger2 := logger.WithField("index", i) - obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&entry) + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&entry) //nolint if err != nil { logger2.Errorf("failed to convert the webhook entry, error: %v, it will be dropped", err) continue } s, _, _ := unstructured.NestedString(obj, "sideEffects") - if s != "None" && s != "NoneOnDryRun" { + if s != "None" && s != "NoneOnDryRun" { //nolint logger2.Infof("reset the invalid sideEffects value '%s' to 'None'", s) obj["sideEffects"] = "None" } diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 2779553cea..ef6578a03a 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -1523,7 +1523,7 @@ func shouldRenamePV(ctx *restoreContext, obj *unstructured.Unstructured, client // remapClaimRefNS remaps a PersistentVolume's claimRef.Namespace based on a // restore's NamespaceMappings, if necessary. Returns true if the namespace was // remapped, false if it was not required. -func remapClaimRefNS(ctx *restoreContext, obj *unstructured.Unstructured) (bool, error) { +func remapClaimRefNS(ctx *restoreContext, obj *unstructured.Unstructured) (bool, error) { //nolint:unparam if len(ctx.restore.Spec.NamespaceMapping) == 0 { ctx.log.Debug("Persistent volume does not need to have the claimRef.namespace remapped because restore is not remapping any namespaces") return false, nil diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index bf7ac0011c..42f0a35419 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -125,7 +125,7 @@ func GetVolumeDirectory(ctx context.Context, log logrus.FieldLogger, pod *corev1 for _, item := range pod.Spec.Volumes { if item.Name == volumeName { - volume = &item + volume = &item //nolint break } } From c92f06ef172a658302ee401e835ac716867a6deb Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 18 Oct 2022 22:26:22 +0800 Subject: [PATCH 338/366] fix lint loop iterator problem Signed-off-by: Lyndon-Li --- changelogs/unreleased/5459-lyndon | 1 + golangci.yaml | 2 +- pkg/cmd/cli/backup/create.go | 10 +++------- pkg/cmd/cli/backup/describe.go | 4 ++-- pkg/cmd/cli/backuplocation/create.go | 4 ++-- pkg/cmd/cli/backuplocation/delete.go | 12 ++++++------ pkg/cmd/cli/backuplocation/set.go | 4 ++-- pkg/cmd/cli/nodeagent/server.go | 8 ++++---- pkg/cmd/cli/restore/create.go | 12 ++++-------- pkg/cmd/cli/restore/describe.go | 4 ++-- pkg/cmd/cli/schedule/describe.go | 4 ++-- pkg/cmd/server/server.go | 8 ++++---- pkg/controller/backup_deletion_controller.go | 12 ++++++------ pkg/controller/backup_sync_controller.go | 14 ++++++++------ pkg/nodeagent/node_agent.go | 6 +++--- pkg/repository/provider/unified_repo_test.go | 2 +- pkg/restore/admissionwebhook_config_action.go | 6 +++--- pkg/util/kube/utils.go | 6 +++--- 18 files changed, 57 insertions(+), 62 deletions(-) create mode 100644 changelogs/unreleased/5459-lyndon diff --git a/changelogs/unreleased/5459-lyndon b/changelogs/unreleased/5459-lyndon new file mode 100644 index 0000000000..ba04f2082a --- /dev/null +++ b/changelogs/unreleased/5459-lyndon @@ -0,0 +1 @@ +Upgrade velero docker image to use go 1.18 and upgrade golangci-lint to 1.45.0 \ No newline at end of file diff --git a/golangci.yaml b/golangci.yaml index 8c6d87b63e..e958170d02 100644 --- a/golangci.yaml +++ b/golangci.yaml @@ -117,7 +117,7 @@ linters-settings: # minimal length of string constant, 3 by default min-len: 3 # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 + min-occurrences: 5 gocritic: # Which checks should be enabled; can't be combined with 'disabled-checks'; # See https://go-critic.github.io/overview#checks-overview diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 27c0be3729..52aeab3d52 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -112,10 +112,6 @@ func NewCreateOptions() *CreateOptions { } } -const ( - strTrue = "true" -) - func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.DurationVar(&o.TTL, "ttl", o.TTL, "How long before the backup can be garbage collected.") flags.Var(&o.IncludeNamespaces, "include-namespaces", "Namespaces to include in the backup (use '*' for all namespaces).") @@ -131,13 +127,13 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f := flags.VarPF(&o.SnapshotVolumes, "snapshot-volumes", "", "Take snapshots of PersistentVolumes as part of the backup. If the parameter is not set, it is treated as setting to 'true'.") // this allows the user to just specify "--snapshot-volumes" as shorthand for "--snapshot-volumes=true" // like a normal bool flag - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the backup") - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" f = flags.VarPF(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", "", "Use pod volume file system backup by default for volumes") - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" } // BindWait binds the wait flag separately so it is not called by other create diff --git a/pkg/cmd/cli/backup/describe.go b/pkg/cmd/cli/backup/describe.go index 3e5ebbded3..1284e29c93 100644 --- a/pkg/cmd/cli/backup/describe.go +++ b/pkg/cmd/cli/backup/describe.go @@ -73,7 +73,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { } first := true - for _, backup := range backups.Items { + for i, backup := range backups.Items { deleteRequestListOptions := pkgbackup.NewDeleteBackupRequestListOptions(backup.Name, string(backup.UID)) deleteRequestList, err := veleroClient.VeleroV1().DeleteBackupRequests(f.Namespace()).List(context.TODO(), deleteRequestListOptions) if err != nil { @@ -102,7 +102,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { } } - s := output.DescribeBackup(context.Background(), kbClient, &backup, deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) //nolint + s := output.DescribeBackup(context.Background(), kbClient, &backups.Items[i], deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index ec19b6e56d..e08e0c6ae2 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -209,10 +209,10 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { if err := kbClient.List(context.Background(), locations, &kbclient.ListOptions{Namespace: f.Namespace()}); err != nil { return errors.WithStack(err) } - for _, location := range locations.Items { + for i, location := range locations.Items { if location.Spec.Default { location.Spec.Default = false - if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { //nolint + if err := kbClient.Update(context.Background(), &locations.Items[i], &kbclient.UpdateOptions{}); err != nil { return errors.WithStack(err) } break diff --git a/pkg/cmd/cli/backuplocation/delete.go b/pkg/cmd/cli/backuplocation/delete.go index d67ed54369..becb92afb3 100644 --- a/pkg/cmd/cli/backuplocation/delete.go +++ b/pkg/cmd/cli/backuplocation/delete.go @@ -115,8 +115,8 @@ func Run(f client.Factory, o *cli.DeleteOptions) error { } // Create a backup-location deletion request for each - for _, location := range locations.Items { - if err := kbClient.Delete(context.Background(), &location, &kbclient.DeleteOptions{}); err != nil { //nolint + for i, location := range locations.Items { + if err := kbClient.Delete(context.Background(), &locations.Items[i], &kbclient.DeleteOptions{}); err != nil { errs = append(errs, errors.WithStack(err)) continue } @@ -162,8 +162,8 @@ func findAssociatedBackupRepos(client kbclient.Client, bslName, ns string) (vele func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []error { var errs []error - for _, backup := range backups.Items { - if err := client.Delete(context.Background(), &backup, &kbclient.DeleteOptions{}); err != nil { //nolint + for i, backup := range backups.Items { + if err := client.Delete(context.Background(), &backups.Items[i], &kbclient.DeleteOptions{}); err != nil { errs = append(errs, errors.WithStack(fmt.Errorf("delete backup %q associated with deleted BSL: %w", backup.Name, err))) continue } @@ -174,8 +174,8 @@ func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []err func deleteBackupRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { var errs []error - for _, repo := range repos.Items { - if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { //nolint + for i, repo := range repos.Items { + if err := client.Delete(context.Background(), &repos.Items[i], &kbclient.DeleteOptions{}); err != nil { errs = append(errs, errors.WithStack(fmt.Errorf("delete backup repository %q associated with deleted BSL: %w", repo.Name, err))) continue } diff --git a/pkg/cmd/cli/backuplocation/set.go b/pkg/cmd/cli/backuplocation/set.go index 44730d659d..bdb36b867c 100644 --- a/pkg/cmd/cli/backuplocation/set.go +++ b/pkg/cmd/cli/backuplocation/set.go @@ -120,7 +120,7 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error { if err := kbClient.List(context.Background(), locations, &kbclient.ListOptions{Namespace: f.Namespace()}); err != nil { return errors.WithStack(err) } - for _, location := range locations.Items { + for i, location := range locations.Items { if !location.Spec.Default { continue } @@ -129,7 +129,7 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error { break } location.Spec.Default = false - if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil { //nolint + if err := kbClient.Update(context.Background(), &locations.Items[i], &kbclient.UpdateOptions{}); err != nil { return errors.WithStack(err) } break diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 7fb3dfc827..b154f9bf81 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -294,7 +294,7 @@ func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumebackups") return } - for _, pvb := range pvbs.Items { + for i, pvb := range pvbs.Items { if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseInProgress { s.logger.Debugf("the status of podvolumebackup %q is %q, skip", pvb.GetName(), pvb.Status.Phase) continue @@ -307,7 +307,7 @@ func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvb.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed pvb.Status.Message = fmt.Sprintf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, pvb.Status.Phase) pvb.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(s.ctx, &pvb, ctrlclient.MergeFrom(original)); err != nil { //nolint + if err := client.Patch(s.ctx, &pvbs.Items[i], ctrlclient.MergeFrom(original)); err != nil { s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) continue } @@ -321,7 +321,7 @@ func (s *nodeAgentServer) markInProgressPVRsFailed(client ctrlclient.Client) { s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") return } - for _, pvr := range pvrs.Items { + for i, pvr := range pvrs.Items { if pvr.Status.Phase != velerov1api.PodVolumeRestorePhaseInProgress { s.logger.Debugf("the status of podvolumerestore %q is %q, skip", pvr.GetName(), pvr.Status.Phase) continue @@ -345,7 +345,7 @@ func (s *nodeAgentServer) markInProgressPVRsFailed(client ctrlclient.Client) { pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed pvr.Status.Message = fmt.Sprintf("get a podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, pvr.Status.Phase) pvr.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(s.ctx, &pvr, ctrlclient.MergeFrom(original)); err != nil { //nolint + if err := client.Patch(s.ctx, &pvrs.Items[i], ctrlclient.MergeFrom(original)); err != nil { s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) continue } diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index 2ac45e8ee8..7625c1f762 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -107,10 +107,6 @@ func NewCreateOptions() *CreateOptions { } } -const ( - strTrue = "true" -) - func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.BackupName, "from-backup", "", "Backup to restore from") flags.StringVar(&o.ScheduleName, "from-schedule", "", "Schedule to restore from") @@ -127,18 +123,18 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f := flags.VarPF(&o.RestoreVolumes, "restore-volumes", "", "Whether to restore volumes from snapshots.") // this allows the user to just specify "--restore-volumes" as shorthand for "--restore-volumes=true" // like a normal bool flag - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" f = flags.VarPF(&o.PreserveNodePorts, "preserve-nodeports", "", "Whether to preserve nodeports of Services when restoring.") // this allows the user to just specify "--preserve-nodeports" as shorthand for "--preserve-nodeports=true" // like a normal bool flag - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the restore.") - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" f = flags.VarPF(&o.AllowPartiallyFailed, "allow-partially-failed", "", "If using --from-schedule, whether to consider PartiallyFailed backups when looking for the most recent one. This flag has no effect if not using --from-schedule.") - f.NoOptDefVal = strTrue + f.NoOptDefVal = "true" flags.BoolVarP(&o.Wait, "wait", "w", o.Wait, "Wait for the operation to complete.") } diff --git a/pkg/cmd/cli/restore/describe.go b/pkg/cmd/cli/restore/describe.go index e8cbb8d885..88514e8651 100644 --- a/pkg/cmd/cli/restore/describe.go +++ b/pkg/cmd/cli/restore/describe.go @@ -68,14 +68,14 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { } first := true - for _, restore := range restores.Items { + for i, restore := range restores.Items { opts := newPodVolumeRestoreListOptions(restore.Name) podvolumeRestoreList, err := veleroClient.VeleroV1().PodVolumeRestores(f.Namespace()).List(context.TODO(), opts) if err != nil { fmt.Fprintf(os.Stderr, "error getting PodVolumeRestores for restore %s: %v\n", restore.Name, err) } - s := output.DescribeRestore(context.Background(), kbClient, &restore, podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) //nolint + s := output.DescribeRestore(context.Background(), kbClient, &restores.Items[i], podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/schedule/describe.go b/pkg/cmd/cli/schedule/describe.go index 5c64610436..b43cad45fc 100644 --- a/pkg/cmd/cli/schedule/describe.go +++ b/pkg/cmd/cli/schedule/describe.go @@ -53,8 +53,8 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { } first := true - for _, schedule := range schedules.Items { - s := output.DescribeSchedule(&schedule) //nolint + for i := range schedules.Items { + s := output.DescribeSchedule(&schedules.Items[i]) if first { first = false fmt.Print(s) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index e66a6275f1..cc1bb8367a 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -939,7 +939,7 @@ func markInProgressBackupsFailed(ctx context.Context, client ctrlclient.Client, return } - for _, backup := range backups.Items { + for i, backup := range backups.Items { if backup.Status.Phase != velerov1api.BackupPhaseInProgress { log.Debugf("the status of backup %q is %q, skip", backup.GetName(), backup.Status.Phase) continue @@ -948,7 +948,7 @@ func markInProgressBackupsFailed(ctx context.Context, client ctrlclient.Client, updated.Status.Phase = velerov1api.BackupPhaseFailed updated.Status.FailureReason = fmt.Sprintf("get a backup with status %q during the server starting, mark it as %q", velerov1api.BackupPhaseInProgress, updated.Status.Phase) updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&backup)); err != nil { //nolint + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&backups.Items[i])); err != nil { log.WithError(errors.WithStack(err)).Errorf("failed to patch backup %q", backup.GetName()) continue } @@ -962,7 +962,7 @@ func markInProgressRestoresFailed(ctx context.Context, client ctrlclient.Client, log.WithError(errors.WithStack(err)).Error("failed to list restores") return } - for _, restore := range restores.Items { + for i, restore := range restores.Items { if restore.Status.Phase != velerov1api.RestorePhaseInProgress { log.Debugf("the status of restore %q is %q, skip", restore.GetName(), restore.Status.Phase) continue @@ -971,7 +971,7 @@ func markInProgressRestoresFailed(ctx context.Context, client ctrlclient.Client, updated.Status.Phase = velerov1api.RestorePhaseFailed updated.Status.FailureReason = fmt.Sprintf("get a restore with status %q during the server starting, mark it as %q", velerov1api.RestorePhaseInProgress, updated.Status.Phase) updated.Status.CompletionTimestamp = &metav1.Time{Time: time.Now()} - if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&restore)); err != nil { //nolint + if err := client.Patch(ctx, updated, ctrlclient.MergeFrom(&restores.Items[i])); err != nil { log.WithError(errors.WithStack(err)).Errorf("failed to patch restore %q", restore.GetName()) continue } diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 7b188d98b3..76c39c8c84 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -325,11 +325,11 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque }); err != nil { log.WithError(errors.WithStack(err)).Error("Error listing restore API objects") } else { - for _, restore := range restoreList.Items { + for i, restore := range restoreList.Items { if restore.Spec.BackupName != backup.Name { continue } - restoreLog := log.WithField("restore", kube.NamespaceAndName(&restore)) //nolint + restoreLog := log.WithField("restore", kube.NamespaceAndName(&restoreList.Items[i])) restoreLog.Info("Deleting restore log/results from backup storage") if err := backupStore.DeleteRestore(restore.Name); err != nil { @@ -339,8 +339,8 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque } restoreLog.Info("Deleting restore referencing backup") - if err := r.Delete(ctx, &restore); err != nil { //nolint - errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(&restore)).Error()) //nolint + if err := r.Delete(ctx, &restoreList.Items[i]); err != nil { + errs = append(errs, errors.Wrapf(err, "error deleting restore %s", kube.NamespaceAndName(&restoreList.Items[i])).Error()) } } } @@ -423,11 +423,11 @@ func (r *backupDeletionReconciler) deleteExistingDeletionRequests(ctx context.Co return []error{errors.Wrap(err, "error listing existing DeleteBackupRequests for backup")} } var errs []error - for _, dbr := range dbrList.Items { + for i, dbr := range dbrList.Items { if dbr.Name == req.Name { continue } - if err := r.Delete(ctx, &dbr); err != nil { //nolint + if err := r.Delete(ctx, &dbrList.Items[i]); err != nil { errs = append(errs, errors.WithStack(err)) } else { log.Infof("deletion request '%s' removed.", dbr.Name) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 30ebe3b094..d29a586416 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -317,13 +317,13 @@ func (b *backupSyncReconciler) deleteOrphanedBackups(ctx context.Context, locati return } - for _, backup := range backupList.Items { + for i, backup := range backupList.Items { log = log.WithField("backup", backup.Name) if backup.Status.Phase != velerov1api.BackupPhaseCompleted || backupStoreBackups.Has(backup.Name) { continue } - if err := b.client.Delete(ctx, &backup, &client.DeleteOptions{}); err != nil { //nolint + if err := b.client.Delete(ctx, &backupList.Items[i], &client.DeleteOptions{}); err != nil { log.WithError(errors.WithStack(err)).Error("Error deleting orphaned backup from cluster") } else { log.Debug("Deleted orphaned backup from cluster") @@ -344,10 +344,10 @@ func (b *backupSyncReconciler) deleteCSISnapshotsByBackup(ctx context.Context, b if err := b.client.List(ctx, &vsList, listOptions); err != nil { log.WithError(err).Warnf("Failed to list volumesnapshots for backup: %s, the deletion will be skipped", backupName) } else { - for _, vs := range vsList.Items { + for i, vs := range vsList.Items { name := kube.NamespaceAndName(vs.GetObjectMeta()) log.Debugf("Deleting volumesnapshot %s", name) - if err := b.client.Delete(context.TODO(), &vs); err != nil { //nolint + if err := b.client.Delete(context.TODO(), &vsList.Items[i]); err != nil { log.WithError(err).Warnf("Failed to delete volumesnapshot %s", name) } } @@ -379,11 +379,13 @@ func backupSyncSourceOrderFunc(objList client.ObjectList) client.ObjectList { bslArray = append(bslArray, &inputBSLList.Items[i]) // append everything before the default for _, bsl := range inputBSLList.Items[:i] { - bslArray = append(bslArray, &bsl) //nolint + cpBsl := bsl + bslArray = append(bslArray, &cpBsl) } // append everything after the default for _, bsl := range inputBSLList.Items[i+1:] { - bslArray = append(bslArray, &bsl) //nolint + cpBsl := bsl + bslArray = append(bslArray, &cpBsl) } meta.SetList(resultBSLList, bslArray) diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index 091c20b51b..cae3e88e57 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -61,12 +61,12 @@ func IsRunningInNode(ctx context.Context, namespace string, nodeName string, pod return errors.Wrap(err, "failed to list daemonset pods") } - for _, pod := range pods.Items { - if kube.IsPodRunning(&pod) != nil { //nolint + for i := range pods.Items { + if kube.IsPodRunning(&pods.Items[i]) != nil { continue } - if pod.Spec.NodeName == nodeName { + if pods.Items[i].Spec.NodeName == nodeName { return nil } } diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index e6f891cce7..c8121058c6 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -506,7 +506,7 @@ func TestGetRepoPassword(t *testing.T) { }, } - password, err := getRepoPassword(urp.credentialGetter.FromSecret, RepoParam{}) + password, err := getRepoPassword(urp.credentialGetter.FromSecret) require.Equal(t, tc.expected, password) diff --git a/pkg/restore/admissionwebhook_config_action.go b/pkg/restore/admissionwebhook_config_action.go index 7a617b6f82..74fd1b3ff9 100644 --- a/pkg/restore/admissionwebhook_config_action.go +++ b/pkg/restore/admissionwebhook_config_action.go @@ -70,15 +70,15 @@ func (a *AdmissionWebhookConfigurationAction) Execute(input *velero.RestoreItemA return velero.NewRestoreItemActionExecuteOutput(input.Item), nil } newWebhooks := make([]interface{}, 0) - for i, entry := range webhooks { + for i := range webhooks { logger2 := logger.WithField("index", i) - obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&entry) //nolint + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&webhooks[i]) if err != nil { logger2.Errorf("failed to convert the webhook entry, error: %v, it will be dropped", err) continue } s, _, _ := unstructured.NestedString(obj, "sideEffects") - if s != "None" && s != "NoneOnDryRun" { //nolint + if s != "None" && s != "NoneOnDryRun" { logger2.Infof("reset the invalid sideEffects value '%s' to 'None'", s) obj["sideEffects"] = "None" } diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 42f0a35419..73603b059f 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -123,9 +123,9 @@ func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client core func GetVolumeDirectory(ctx context.Context, log logrus.FieldLogger, pod *corev1api.Pod, volumeName string, cli client.Client) (string, error) { var volume *corev1api.Volume - for _, item := range pod.Spec.Volumes { - if item.Name == volumeName { - volume = &item //nolint + for i := range pod.Spec.Volumes { + if pod.Spec.Volumes[i].Name == volumeName { + volume = &pod.Spec.Volumes[i] break } } From 34cca775336c81feda0c277402f8c98c258e817c Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Mon, 26 Sep 2022 17:50:13 -0400 Subject: [PATCH 339/366] Add nil check before execution of csi snapshot delete Signed-off-by: Shubham Pampattiwar add changelog Signed-off-by: Shubham Pampattiwar --- changelogs/unreleased/5401-shubham-pampattiwar | 1 + pkg/controller/backup_controller.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5401-shubham-pampattiwar diff --git a/changelogs/unreleased/5401-shubham-pampattiwar b/changelogs/unreleased/5401-shubham-pampattiwar new file mode 100644 index 0000000000..e42b5260c5 --- /dev/null +++ b/changelogs/unreleased/5401-shubham-pampattiwar @@ -0,0 +1 @@ +Add nil check before execution of csi snapshot delete \ No newline at end of file diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index c874201fd3..900345b5ef 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -707,7 +707,9 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } // Delete the VolumeSnapshots created in the backup, when CSI feature is enabled. - c.deleteVolumeSnapshot(volumeSnapshots, volumeSnapshotContents, *backup, backupLog) + if len(volumeSnapshots) > 0 && len(volumeSnapshotContents) > 0 { + c.deleteVolumeSnapshot(volumeSnapshots, volumeSnapshotContents, *backup, backupLog) + } } From c5339227fe73c0dabcd610cb209b2203102b6d41 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Wed, 19 Oct 2022 04:26:45 -0400 Subject: [PATCH 340/366] increase ensure restic repository timeout (#5335) Signed-off-by: Shubham Pampattiwar --- changelogs/unreleased/5335-shubham-pampattiwar | 1 + pkg/repository/ensurer.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5335-shubham-pampattiwar diff --git a/changelogs/unreleased/5335-shubham-pampattiwar b/changelogs/unreleased/5335-shubham-pampattiwar new file mode 100644 index 0000000000..803f0f3cb5 --- /dev/null +++ b/changelogs/unreleased/5335-shubham-pampattiwar @@ -0,0 +1 @@ +Increase ensure restic repository timeout to 5m \ No newline at end of file diff --git a/pkg/repository/ensurer.go b/pkg/repository/ensurer.go index 5527ac7422..7d7bd3ffbc 100644 --- a/pkg/repository/ensurer.go +++ b/pkg/repository/ensurer.go @@ -124,7 +124,7 @@ func (r *RepositoryEnsurer) createBackupRepositoryAndWait(ctx context.Context, n } } - err := wait.PollWithContext(ctx, time.Millisecond*500, time.Minute, checkFunc) + err := wait.PollWithContext(ctx, time.Millisecond*500, time.Minute*5, checkFunc) if err != nil { return nil, errors.Wrap(err, "failed to wait BackupRepository") } else { From 11a7c796eb1b4416b2656a9394d7379412d2992d Mon Sep 17 00:00:00 2001 From: danfengliu Date: Thu, 20 Oct 2022 17:49:51 +0800 Subject: [PATCH 341/366] Fix label naming issue for restore helper (#5469) Signed-off-by: danfengl --- go.sum | 2 -- test/e2e/util/velero/install.go | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go.sum b/go.sum index 2f90eb7cce..7c5ef1829d 100644 --- a/go.sum +++ b/go.sum @@ -759,7 +759,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -855,7 +854,6 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 7f50d780cc..14c6e622b0 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -352,8 +352,8 @@ func patchResources(ctx context.Context, resources *unstructured.UnstructuredLis Name: "restic-restore-action-config", Namespace: namespace, Labels: map[string]string{ - "velero.io/plugin-config": "", - "velero.io/restic": "RestoreItemAction", + "velero.io/plugin-config": "", + "velero.io/pod-volume-restore": "RestoreItemAction", }, }, Data: map[string]string{ From b146a880c62811ead62e36f46aa9c54598f822f2 Mon Sep 17 00:00:00 2001 From: Kira Boyle Date: Wed, 15 Jun 2022 14:38:57 -0700 Subject: [PATCH 342/366] update k8s.io dependencies to 0.24.0 * This also required an update to use github.com/bombsimon/logrusr/v3 * 'WithClusterName' removed as per the k8s doc reasoning: * https://github.com/kubernetes/apimachinery/blob/release-1.24/pkg/apis/meta/v1/types.go\#L257-L259 * ('ClusterName was a legacy field that was always cleared by the system and never used') * Test was updated accordingly Signed-off-by: Kira Boyle --- changelogs/unreleased/5012-kcboyle | 6 + go.mod | 66 ++++++----- go.sum | 178 ++++++++++++++++++++--------- pkg/builder/object_meta.go | 8 -- pkg/cmd/server/server.go | 8 +- pkg/restore/restore_test.go | 1 - 6 files changed, 174 insertions(+), 93 deletions(-) create mode 100644 changelogs/unreleased/5012-kcboyle diff --git a/changelogs/unreleased/5012-kcboyle b/changelogs/unreleased/5012-kcboyle new file mode 100644 index 0000000000..341cef245f --- /dev/null +++ b/changelogs/unreleased/5012-kcboyle @@ -0,0 +1,6 @@ +Update the k8s.io dependencies to 0.24.0. +This also required an update to github.com/bombsimon/logrusr/v3. +Removed the `WithClusterName` method +as it is a "legacy field that was +always cleared by the system and never used" as per upstream k8s +https://github.com/kubernetes/apimachinery/blob/release-1.24/pkg/apis/meta/v1/types.go#L257-L259 \ No newline at end of file diff --git a/go.mod b/go.mod index 145babd7d2..e91150f265 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.3.0 github.com/aws/aws-sdk-go v1.43.31 - github.com/bombsimon/logrusr v1.1.0 - github.com/evanphx/json-patch v4.11.0+incompatible + github.com/bombsimon/logrusr/v3 v3.0.0 + github.com/evanphx/json-patch v5.6.0+incompatible github.com/fatih/color v1.13.0 github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.7 + github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.3.0 github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-plugin v1.4.3 @@ -25,31 +25,31 @@ require ( github.com/kopia/kopia v0.10.7 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.16.0 + github.com/onsi/gomega v1.18.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.12.1 + github.com/prometheus/client_golang v1.12.2 github.com/robfig/cron v1.1.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/afero v1.6.0 - github.com/spf13/cobra v1.2.1 + github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.1 github.com/vmware-tanzu/crash-diagnostics v0.3.7 - golang.org/x/mod v0.5.1 - golang.org/x/net v0.0.0-20220325170049-de3da57026de - golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 + golang.org/x/net v0.0.0-20220615171555-694bf12d69de + golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/api v0.74.0 google.golang.org/grpc v1.45.0 google.golang.org/protobuf v1.28.0 - k8s.io/api v0.22.2 - k8s.io/apiextensions-apiserver v0.22.2 - k8s.io/apimachinery v0.22.2 - k8s.io/cli-runtime v0.22.2 - k8s.io/client-go v0.22.2 - k8s.io/klog/v2 v2.9.0 + k8s.io/api v0.24.1 + k8s.io/apiextensions-apiserver v0.24.1 + k8s.io/apimachinery v0.24.1 + k8s.io/cli-runtime v0.24.0 + k8s.io/client-go v0.24.1 + k8s.io/klog/v2 v2.60.1 k8s.io/kube-aggregator v0.19.12 - sigs.k8s.io/controller-runtime v0.10.2 + sigs.k8s.io/controller-runtime v0.12.1 sigs.k8s.io/yaml v1.3.0 ) @@ -73,27 +73,33 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v0.4.0 // indirect - github.com/go-logr/zapr v0.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/zapr v1.2.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/googleapis/gax-go/v2 v2.2.0 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.1 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -106,13 +112,14 @@ require ( github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rs/xid v1.3.0 // indirect github.com/stretchr/objx v0.2.0 // indirect @@ -125,10 +132,10 @@ require ( go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee // indirect - golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 // indirect + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -138,10 +145,11 @@ require ( gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.22.2 // indirect - k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect + k8s.io/component-base v0.24.1 // indirect + k8s.io/kube-openapi v0.0.0-20220614142933-1062c7ade5f8 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 diff --git a/go.sum b/go.sum index 7c5ef1829d..7f200bfc43 100644 --- a/go.sum +++ b/go.sum @@ -71,7 +71,6 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -128,10 +127,13 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= @@ -146,9 +148,10 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bombsimon/logrusr v1.1.0 h1:Y03FI4Z/Shyrc9jF26vuaUbnPxC5NMJnTtJA/3Lihq8= -github.com/bombsimon/logrusr v1.1.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZA82TQ= +github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -186,6 +189,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -209,6 +213,8 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7fo github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -220,19 +226,24 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -243,29 +254,38 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= -github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -281,6 +301,7 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -319,6 +340,11 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -331,8 +357,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -354,6 +381,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -373,9 +401,9 @@ github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTK github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -420,8 +448,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -435,6 +463,7 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -459,7 +488,6 @@ github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kopia/kopia v0.10.7 h1:6s0ZIZW3Ge2ozzefddASy7CIUadp/5tF9yCDKQfAKKI= github.com/kopia/kopia v0.10.7/go.mod h1:0d9THPD+jwomPcXvPbCdmLyX6phQVP7AqcCcDEajfNA= @@ -488,6 +516,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -528,7 +558,7 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -539,6 +569,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -561,13 +592,15 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -589,8 +622,9 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -601,8 +635,9 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= +github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -621,6 +656,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -645,8 +681,9 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -680,6 +717,9 @@ github.com/vladimirvivien/gexe v0.1.1 h1:2A0SBaOSKH+cwLVdt6H+KkHZotZWRNLlWygANGw github.com/vladimirvivien/gexe v0.1.1/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= github.com/vmware-tanzu/crash-diagnostics v0.3.7 h1:6gbv/3o1FzyRLS7Dz/+yVg1Lk1oRBQLyI3d1YTtlTT8= github.com/vmware-tanzu/crash-diagnostics v0.3.7/go.mod h1:gO8670rd+qdjnJVol674snT/A46GQ27u085kKhZznlM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -688,6 +728,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= @@ -700,9 +741,12 @@ go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= @@ -735,8 +779,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= @@ -759,6 +803,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -798,8 +844,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -854,10 +900,14 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de h1:ogOG2+P6LjO2j55AkRScrkB2BFpd+Z8TY2wcM0Z3MGo= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -876,8 +926,9 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb h1:8tDJ3aechhddbdPAxpycgXHJRMLpk/Ab+aa4OgdN5/g= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -923,7 +974,6 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -940,7 +990,6 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -966,8 +1015,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -978,13 +1027,16 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA= +golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1001,8 +1053,10 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1065,6 +1119,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1154,6 +1209,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1184,6 +1240,7 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= @@ -1278,6 +1335,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -1292,58 +1350,76 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.19.12/go.mod h1:EK+KvSq2urA6+CjVdZyAHEphXoLq2K2eW6lxOzTKSaY= -k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= -k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= -k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= +k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= +k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= +k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= +k8s.io/apiextensions-apiserver v0.24.1 h1:5yBh9+ueTq/kfnHQZa0MAo6uNcPrtxPMpNQgorBaKS0= +k8s.io/apiextensions-apiserver v0.24.1/go.mod h1:A6MHfaLDGfjOc/We2nM7uewD5Oa/FnEbZ6cD7g2ca4Q= k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.19.12/go.mod h1:9eb44nUQSsz9QZiilFRuMj3ZbTmoWolU8S2gnXoRMjo= -k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= +k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apiserver v0.19.12/go.mod h1:ldZAZTNIKfMMv/UUEhk6UyTXC0/34iRdNFHo+MJOPc4= -k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= -k8s.io/cli-runtime v0.22.2 h1:fsd9rFk9FSaVq4SUq1fM27c8CFGsYZUJ/3BkgmjYWuY= +k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0= k8s.io/cli-runtime v0.22.2/go.mod h1:tkm2YeORFpbgQHEK/igqttvPTRIHFRz5kATlw53zlMI= +k8s.io/cli-runtime v0.24.0 h1:ot3Qf49T852uEyNApABO1UHHpFIckKK/NqpheZYN2gM= +k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A= k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= k8s.io/client-go v0.19.12/go.mod h1:BAGKQraZ6fDmXhT46pGXWZQQqN7P4E0BJux0+9O6Gt0= -k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= +k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= +k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= +k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/code-generator v0.19.12/go.mod h1:ADrDvaUQWGn4a8lX0ONtzb7uFmDRQOMSYIMk1qWIAx8= -k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= +k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= k8s.io/component-base v0.19.12/go.mod h1:tpwExE0sY3A7CwtlxGL7SnQOdQfUlnFybT6GmAD+z/s= -k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= -k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= +k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts= +k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-aggregator v0.19.12 h1:OwyNUe/7/gxzEnaLd3sC9Yrpx0fZAERzvFslX5Qq5g8= k8s.io/kube-aggregator v0.19.12/go.mod h1:K76wPd03pSHEmS1FgJOcpryac5C3va4cbCvSu+4EmE0= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220614142933-1062c7ade5f8 h1:IyQ1DifCBk589JD4Cm2CT2poIdO3lfPzz3WwVh1Ugf8= +k8s.io/kube-openapi v0.0.0-20220614142933-1062c7ade5f8/go.mod h1:guXtiQW/y/AWAfPSOaI/1eY0TGBAmL5OygiIyUOKDRc= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= -sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= +sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI= +sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 h1:2sgAQQcY0dEW2SsQwTXhQV4vO6+rSslYx8K3XmM5hqQ= +sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= +sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= +sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/pkg/builder/object_meta.go b/pkg/builder/object_meta.go index 92cfb29ef9..6c01019b47 100644 --- a/pkg/builder/object_meta.go +++ b/pkg/builder/object_meta.go @@ -117,14 +117,6 @@ func setMapEntries(m map[string]string, vals ...string) map[string]string { return m } -// WithClusterName is a functional option that applies the specified -// cluster name to an object. -func WithClusterName(val string) func(obj metav1.Object) { - return func(obj metav1.Object) { - obj.SetClusterName(val) - } -} - // WithFinalizers is a functional option that applies the specified // finalizers to an object. func WithFinalizers(vals ...string) func(obj metav1.Object) { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index acb6eb8d2f..0a794a706f 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -27,9 +27,7 @@ import ( "strings" "time" - "github.com/vmware-tanzu/velero/pkg/uploader" - - "github.com/bombsimon/logrusr" + logrusr "github.com/bombsimon/logrusr/v3" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" @@ -47,6 +45,8 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "github.com/vmware-tanzu/velero/pkg/uploader" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" @@ -326,7 +326,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s corev1api.AddToScheme(scheme) snapshotv1api.AddToScheme(scheme) - ctrl.SetLogger(logrusr.NewLogger(logger)) + ctrl.SetLogger(logrusr.New(logger)) mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ Scheme: scheme, diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index c7fedf7c6b..f97849921f 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -873,7 +873,6 @@ func TestRestoreItems(t *testing.T) { ObjectMeta( builder.WithLabels("key-1", "val-1"), builder.WithAnnotations("key-1", "val-1"), - builder.WithClusterName("cluster-1"), builder.WithFinalizers("finalizer-1"), ). Result(), From 7d5e17fe79167d0782a6fe8fb23cbe4df54b56e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 21 Oct 2022 11:27:18 +0800 Subject: [PATCH 343/366] Change name of changelog file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/{5012-kcboyle => 5471-kcboyle} | 0 design/Implemented/backup-resources-order.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename changelogs/unreleased/{5012-kcboyle => 5471-kcboyle} (100%) diff --git a/changelogs/unreleased/5012-kcboyle b/changelogs/unreleased/5471-kcboyle similarity index 100% rename from changelogs/unreleased/5012-kcboyle rename to changelogs/unreleased/5471-kcboyle diff --git a/design/Implemented/backup-resources-order.md b/design/Implemented/backup-resources-order.md index d888ad2371..92d6a6bebb 100644 --- a/design/Implemented/backup-resources-order.md +++ b/design/Implemented/backup-resources-order.md @@ -2,7 +2,7 @@ This document proposes a solution that allows user to specify a backup order for resources of specific resource type. ## Background -During backup process, user may need to back up resources of specific type in some specific order to ensure the resources were backup properly because these resources are related and ordering might be required to preserve the consistency for the apps to recover itself �from the backup image +During backup process, user may need to back up resources of specific type in some specific order to ensure the resources were backup properly because these resources are related and ordering might be required to preserve the consistency for the apps to recover itself from the backup image (Ex: primary-secondary database pods in a cluster). ## Goals From ecee846ed5e19cd5c8f4525a642ded281f4ec03e Mon Sep 17 00:00:00 2001 From: Dave Pedu Date: Fri, 21 Oct 2022 08:59:08 -0700 Subject: [PATCH 344/366] Trivial correction to 1.7 upgrade instructions The container name for the aws plugin is `velero-plugin-for-aws`. There was an extra `velero-` prefix in the doc. Signed-off-by: Dave Pedu --- site/content/docs/v1.7/upgrade-to-1.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/v1.7/upgrade-to-1.7.md b/site/content/docs/v1.7/upgrade-to-1.7.md index c46b881c1d..6cf7748c6c 100644 --- a/site/content/docs/v1.7/upgrade-to-1.7.md +++ b/site/content/docs/v1.7/upgrade-to-1.7.md @@ -53,7 +53,7 @@ Before upgrading, check the [Velero compatibility matrix](https://github.com/vmw # if you are using other plugin kubectl set image deployment/velero \ velero=velero/velero:v1.7.0 \ - velero-velero-plugin-for-aws=velero/velero-plugin-for-aws:v1.3.0 \ + velero-plugin-for-aws=velero/velero-plugin-for-aws:v1.3.0 \ --namespace velero # optional, if using the restic daemon set From 5027aae19442be5ef026f2831649e4ee21098236 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Mon, 24 Oct 2022 10:42:08 +0800 Subject: [PATCH 345/366] Add more nil pointer check for CSI related code in backup controller. (#5388) Add some corner cases checking for CSI snapshot in backup controller. Signed-off-by: Xun Jiang --- changelogs/unreleased/5388-blackpiglet | 1 + pkg/builder/volume_snapshot_builder.go | 69 ++++++++++++++ .../volume_snapshot_content_builder.go | 70 +++++++++++++++ pkg/controller/backup_controller.go | 55 +++++++++--- pkg/controller/backup_controller_test.go | 90 +++++++++++++++++++ pkg/test/fake_controller_runtime_client.go | 5 ++ 6 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/5388-blackpiglet create mode 100644 pkg/builder/volume_snapshot_builder.go create mode 100644 pkg/builder/volume_snapshot_content_builder.go diff --git a/changelogs/unreleased/5388-blackpiglet b/changelogs/unreleased/5388-blackpiglet new file mode 100644 index 0000000000..2bd9f73752 --- /dev/null +++ b/changelogs/unreleased/5388-blackpiglet @@ -0,0 +1 @@ +Add some corner cases checking for CSI snapshot in backup controller. \ No newline at end of file diff --git a/pkg/builder/volume_snapshot_builder.go b/pkg/builder/volume_snapshot_builder.go new file mode 100644 index 0000000000..19815c0f05 --- /dev/null +++ b/pkg/builder/volume_snapshot_builder.go @@ -0,0 +1,69 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package builder + +import ( + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// VolumeSnapshotBuilder builds VolumeSnapshot objects. +type VolumeSnapshotBuilder struct { + object *snapshotv1api.VolumeSnapshot +} + +// ForVolumeSnapshot is the constructor for VolumeSnapshotBuilder. +func ForVolumeSnapshot(ns, name string) *VolumeSnapshotBuilder { + return &VolumeSnapshotBuilder{ + object: &snapshotv1api.VolumeSnapshot{ + TypeMeta: metav1.TypeMeta{ + APIVersion: snapshotv1api.SchemeGroupVersion.String(), + Kind: "VolumeSnapshot", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + }, + } +} + +// ObjectMeta applies functional options to the VolumeSnapshot's ObjectMeta. +func (v *VolumeSnapshotBuilder) ObjectMeta(opts ...ObjectMetaOpt) *VolumeSnapshotBuilder { + for _, opt := range opts { + opt(v.object) + } + + return v +} + +// Result return the built VolumeSnapshot. +func (v *VolumeSnapshotBuilder) Result() *snapshotv1api.VolumeSnapshot { + return v.object +} + +// Status init the built VolumeSnapshot's status. +func (v *VolumeSnapshotBuilder) Status() *VolumeSnapshotBuilder { + v.object.Status = &snapshotv1api.VolumeSnapshotStatus{} + return v +} + +// BoundVolumeSnapshotContentName set built VolumeSnapshot's status BoundVolumeSnapshotContentName field. +func (v *VolumeSnapshotBuilder) BoundVolumeSnapshotContentName(vscName string) *VolumeSnapshotBuilder { + v.object.Status.BoundVolumeSnapshotContentName = &vscName + return v +} diff --git a/pkg/builder/volume_snapshot_content_builder.go b/pkg/builder/volume_snapshot_content_builder.go new file mode 100644 index 0000000000..936eb74c53 --- /dev/null +++ b/pkg/builder/volume_snapshot_content_builder.go @@ -0,0 +1,70 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package builder + +import ( + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// VolumeSnapshotContentBuilder builds VolumeSnapshotContent object. +type VolumeSnapshotContentBuilder struct { + object *snapshotv1api.VolumeSnapshotContent +} + +// ForVolumeSnapshotContent is the constructor of VolumeSnapshotContentBuilder. +func ForVolumeSnapshotContent(name string) *VolumeSnapshotContentBuilder { + return &VolumeSnapshotContentBuilder{ + object: &snapshotv1api.VolumeSnapshotContent{ + TypeMeta: metav1.TypeMeta{ + APIVersion: snapshotv1api.SchemeGroupVersion.String(), + Kind: "VolumeSnapshotContent", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, + } +} + +// Result returns the built VolumeSnapshotContent. +func (v *VolumeSnapshotContentBuilder) Result() *snapshotv1api.VolumeSnapshotContent { + return v.object +} + +// Status initiates VolumeSnapshotContent's status. +func (v *VolumeSnapshotContentBuilder) Status() *VolumeSnapshotContentBuilder { + v.object.Status = &snapshotv1api.VolumeSnapshotContentStatus{} + return v +} + +// DeletionPolicy sets built VolumeSnapshotContent's spec.DeletionPolicy value. +func (v *VolumeSnapshotContentBuilder) DeletionPolicy(policy snapshotv1api.DeletionPolicy) *VolumeSnapshotContentBuilder { + v.object.Spec.DeletionPolicy = policy + return v +} + +func (v *VolumeSnapshotContentBuilder) VolumeSnapshotRef(namespace, name string) *VolumeSnapshotContentBuilder { + v.object.Spec.VolumeSnapshotRef = v1.ObjectReference{ + APIVersion: "snapshot.storage.k8s.io/v1", + Kind: "VolumeSnapshot", + Namespace: namespace, + Name: name, + } + return v +} diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 5b3235e8ac..177b4f975e 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -95,7 +95,7 @@ type backupController struct { backupStoreGetter persistence.ObjectBackupStoreGetter formatFlag logging.Format volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister - volumeSnapshotClient *snapshotterClientSet.Clientset + volumeSnapshotClient snapshotterClientSet.Interface credentialFileStore credentials.FileStore } @@ -119,7 +119,7 @@ func NewBackupController( backupStoreGetter persistence.ObjectBackupStoreGetter, formatFlag logging.Format, volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister, - volumeSnapshotClient *snapshotterClientSet.Clientset, + volumeSnapshotClient snapshotterClientSet.Interface, credentialStore credentials.FileStore, ) Interface { c := &backupController{ @@ -674,10 +674,11 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } - err = c.checkVolumeSnapshotReadyToUse(context.Background(), volumeSnapshots, backup.Spec.CSISnapshotTimeout.Duration) + volumeSnapshots, err = c.waitVolumeSnapshotReadyToUse(context.Background(), backup.Spec.CSISnapshotTimeout.Duration, backup.Name) if err != nil { backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) } + backup.CSISnapshots = volumeSnapshots err = c.kbClient.List(context.Background(), vscList, &kbclient.ListOptions{LabelSelector: selector}) @@ -911,18 +912,35 @@ func encodeToJSONGzip(data interface{}, desc string) (*bytes.Buffer, []error) { return buf, nil } -// Waiting for VolumeSnapshot ReadyTosue to true is time consuming. Try to make the process parallel by +// waitVolumeSnapshotReadyToUse is used to wait VolumeSnapshot turned to ReadyToUse. +// Waiting for VolumeSnapshot ReadyToUse to true is time consuming. Try to make the process parallel by // using goroutine here instead of waiting in CSI plugin, because it's not easy to make BackupItemAction // parallel by now. After BackupItemAction parallel is implemented, this logic should be moved to CSI plugin // as https://github.com/vmware-tanzu/velero-plugin-for-csi/pull/100 -func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, volumesnapshots []snapshotv1api.VolumeSnapshot, - csiSnapshotTimeout time.Duration) error { +func (c *backupController) waitVolumeSnapshotReadyToUse(ctx context.Context, + csiSnapshotTimeout time.Duration, backupName string) ([]snapshotv1api.VolumeSnapshot, error) { eg, _ := errgroup.WithContext(ctx) timeout := csiSnapshotTimeout interval := 5 * time.Second + volumeSnapshots := make([]snapshotv1api.VolumeSnapshot, 0) + + if c.volumeSnapshotLister != nil { + tmpVSs, err := c.volumeSnapshotLister.List(label.NewSelectorForBackup(backupName)) + if err != nil { + c.logger.Error(err) + return volumeSnapshots, err + } + + for _, vs := range tmpVSs { + volumeSnapshots = append(volumeSnapshots, *vs) + } + } - for _, vs := range volumesnapshots { - volumeSnapshot := vs + vsChannel := make(chan snapshotv1api.VolumeSnapshot, len(volumeSnapshots)) + defer close(vsChannel) + + for index := range volumeSnapshots { + volumeSnapshot := volumeSnapshots[index] eg.Go(func() error { err := wait.PollImmediate(interval, timeout, func() (bool, error) { tmpVS, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(volumeSnapshot.Namespace).Get(ctx, volumeSnapshot.Name, metav1.GetOptions{}) @@ -934,6 +952,9 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo return false, nil } + c.logger.Debugf("VolumeSnapshot %s/%s turned into ReadyToUse.", volumeSnapshot.Namespace, volumeSnapshot.Name) + // Put the ReadyToUse VolumeSnapshot element in the result channel. + vsChannel <- *tmpVS return true, nil }) if err == wait.ErrWaitTimeout { @@ -942,7 +963,16 @@ func (c *backupController) checkVolumeSnapshotReadyToUse(ctx context.Context, vo return err }) } - return eg.Wait() + + err := eg.Wait() + + result := make([]snapshotv1api.VolumeSnapshot, 0) + length := len(vsChannel) + for index := 0; index < length; index++ { + result = append(result, <-vsChannel) + } + + return result, err } // deleteVolumeSnapshot delete VolumeSnapshot created during backup. @@ -965,7 +995,8 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api. defer wg.Done() var vsc snapshotv1api.VolumeSnapshotContent modifyVSCFlag := false - if vs.Status.BoundVolumeSnapshotContentName != nil && + if vs.Status != nil && + vs.Status.BoundVolumeSnapshotContentName != nil && len(*vs.Status.BoundVolumeSnapshotContentName) > 0 { var found bool if vsc, found = vscMap[*vs.Status.BoundVolumeSnapshotContentName]; !found { @@ -976,6 +1007,8 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api. if vsc.Spec.DeletionPolicy == snapshotv1api.VolumeSnapshotContentDelete { modifyVSCFlag = true } + } else { + logger.Errorf("VolumeSnapshot %s/%s is not ready. This is not expected.", vs.Namespace, vs.Name) } // Change VolumeSnapshotContent's DeletionPolicy to Retain before deleting VolumeSnapshot, @@ -1001,7 +1034,7 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api. } // Delete VolumeSnapshot from cluster - logger.Debugf("Deleting VolumeSnapshotContent %s", vsc.Name) + logger.Debugf("Deleting VolumeSnapshot %s/%s", vs.Namespace, vs.Name) err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots(vs.Namespace).Delete(context.TODO(), vs.Name, metav1.DeleteOptions{}) if err != nil { logger.Errorf("fail to delete VolumeSnapshot %s/%s: %s", vs.Namespace, vs.Name, err.Error()) diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 4617975681..696397b729 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -27,6 +27,9 @@ import ( "testing" "time" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapshotfake "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/fake" + snapshotinformers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -1393,3 +1396,90 @@ func Test_getLastSuccessBySchedule(t *testing.T) { }) } } + +func TestDeleteVolumeSnapshot(t *testing.T) { + tests := []struct { + name string + vsArray []snapshotv1api.VolumeSnapshot + vscArray []snapshotv1api.VolumeSnapshotContent + expectedVSArray []snapshotv1api.VolumeSnapshot + expectedVSCArray []snapshotv1api.VolumeSnapshotContent + }{ + { + name: "VS is ReadyToUse, and VS has corresponding VSC. VS should be deleted.", + vsArray: []snapshotv1api.VolumeSnapshot{ + *builder.ForVolumeSnapshot("velero", "vs1").ObjectMeta(builder.WithLabels("testing-vs", "vs1")).Status().BoundVolumeSnapshotContentName("vsc1").Result(), + }, + vscArray: []snapshotv1api.VolumeSnapshotContent{ + *builder.ForVolumeSnapshotContent("vsc1").DeletionPolicy(snapshotv1api.VolumeSnapshotContentDelete).Status().Result(), + }, + expectedVSArray: []snapshotv1api.VolumeSnapshot{}, + expectedVSCArray: []snapshotv1api.VolumeSnapshotContent{ + *builder.ForVolumeSnapshotContent("vsc1").DeletionPolicy(snapshotv1api.VolumeSnapshotContentRetain).VolumeSnapshotRef("ns-", "name-").Status().Result(), + }, + }, + { + name: "Corresponding VSC not found for VS. VS is not deleted.", + vsArray: []snapshotv1api.VolumeSnapshot{ + *builder.ForVolumeSnapshot("velero", "vs1").ObjectMeta(builder.WithLabels("testing-vs", "vs1")).Status().BoundVolumeSnapshotContentName("vsc1").Result(), + }, + vscArray: []snapshotv1api.VolumeSnapshotContent{}, + expectedVSArray: []snapshotv1api.VolumeSnapshot{ + *builder.ForVolumeSnapshot("velero", "vs1").Status().BoundVolumeSnapshotContentName("vsc1").Result(), + }, + expectedVSCArray: []snapshotv1api.VolumeSnapshotContent{}, + }, + { + name: "VS status is nil. VSC should not be modified.", + vsArray: []snapshotv1api.VolumeSnapshot{ + *builder.ForVolumeSnapshot("velero", "vs1").ObjectMeta(builder.WithLabels("testing-vs", "vs1")).Result(), + }, + vscArray: []snapshotv1api.VolumeSnapshotContent{ + *builder.ForVolumeSnapshotContent("vsc1").DeletionPolicy(snapshotv1api.VolumeSnapshotContentDelete).Status().Result(), + }, + expectedVSArray: []snapshotv1api.VolumeSnapshot{}, + expectedVSCArray: []snapshotv1api.VolumeSnapshotContent{ + *builder.ForVolumeSnapshotContent("vsc1").DeletionPolicy(snapshotv1api.VolumeSnapshotContentDelete).Status().Result(), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + fakeClient := velerotest.NewFakeControllerRuntimeClientBuilder(t).WithLists( + &snapshotv1api.VolumeSnapshotContentList{Items: tc.vscArray}, + ).Build() + + vsClient := snapshotfake.NewSimpleClientset(&tc.vsArray[0]) + sharedInformers := snapshotinformers.NewSharedInformerFactory(vsClient, 0) + + for _, vs := range tc.vsArray { + sharedInformers.Snapshot().V1().VolumeSnapshots().Informer().GetStore().Add(vs) + } + + logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatText) + c := &backupController{ + kbClient: fakeClient, + volumeSnapshotClient: vsClient, + volumeSnapshotLister: sharedInformers.Snapshot().V1().VolumeSnapshots().Lister(), + } + + c.deleteVolumeSnapshot(tc.vsArray, tc.vscArray, logger) + + vsList, err := c.volumeSnapshotClient.SnapshotV1().VolumeSnapshots("velero").List(context.TODO(), metav1.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, len(tc.expectedVSArray), len(vsList.Items)) + for index := range tc.expectedVSArray { + assert.Equal(t, tc.expectedVSArray[index].Status, vsList.Items[index].Status) + assert.Equal(t, tc.expectedVSArray[index].Spec, vsList.Items[index].Spec) + } + + vscList := &snapshotv1api.VolumeSnapshotContentList{} + require.NoError(t, c.kbClient.List(context.Background(), vscList)) + assert.Equal(t, len(tc.expectedVSCArray), len(vscList.Items)) + for index := range tc.expectedVSCArray { + assert.Equal(t, tc.expectedVSCArray[index].Spec, vscList.Items[index].Spec) + } + }) + } +} diff --git a/pkg/test/fake_controller_runtime_client.go b/pkg/test/fake_controller_runtime_client.go index d1c1b6106f..0be391bd9f 100644 --- a/pkg/test/fake_controller_runtime_client.go +++ b/pkg/test/fake_controller_runtime_client.go @@ -19,6 +19,7 @@ package test import ( "testing" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/stretchr/testify/require" corev1api "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -34,6 +35,8 @@ func NewFakeControllerRuntimeClientBuilder(t *testing.T) *k8sfake.ClientBuilder require.NoError(t, err) err = corev1api.AddToScheme(scheme) require.NoError(t, err) + err = snapshotv1api.AddToScheme(scheme) + require.NoError(t, err) return k8sfake.NewClientBuilder().WithScheme(scheme) } @@ -43,5 +46,7 @@ func NewFakeControllerRuntimeClient(t *testing.T, initObjs ...runtime.Object) cl require.NoError(t, err) err = corev1api.AddToScheme(scheme) require.NoError(t, err) + err = snapshotv1api.AddToScheme(scheme) + require.NoError(t, err) return k8sfake.NewFakeClientWithScheme(scheme, initObjs...) } From 9695340c129c2eb92262bfa4d7fa893be8702754 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 21 Oct 2022 14:11:09 +0800 Subject: [PATCH 346/366] repo config for s3 compatible store Signed-off-by: Lyndon-Li --- changelogs/unreleased/5478-lyndon | 1 + pkg/repository/config/config.go | 18 ++++++++++++++---- pkg/repository/config/config_test.go | 21 ++++++++++++++++++++- pkg/repository/provider/unified_repo.go | 6 +++--- pkg/restic/common.go | 2 +- 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/5478-lyndon diff --git a/changelogs/unreleased/5478-lyndon b/changelogs/unreleased/5478-lyndon new file mode 100644 index 0000000000..d068694491 --- /dev/null +++ b/changelogs/unreleased/5478-lyndon @@ -0,0 +1 @@ +Issue fix 5477: create the common way to support S3 compatible object storages that work for both Restic and Kopia; Keep the resticRepoPrefix parameter for compatibility \ No newline at end of file diff --git a/pkg/repository/config/config.go b/pkg/repository/config/config.go index d7ed99b69e..c1ef8b906c 100644 --- a/pkg/repository/config/config.go +++ b/pkg/repository/config/config.go @@ -56,7 +56,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) prefix = layout.GetResticDir() } - backendType := GetBackendType(location.Spec.Provider) + backendType := GetBackendType(location.Spec.Provider, location.Spec.Config) if repoPrefix := location.Spec.Config["resticRepoPrefix"]; repoPrefix != "" { return repoPrefix, nil @@ -87,15 +87,25 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) return fmt.Sprintf("gs:%s:/%s", bucket, prefix), nil } - return "", errors.New("restic repository prefix (resticRepoPrefix) not specified in backup storage location's config") + return "", errors.Errorf("invalid backend type %s, provider %s", backendType, location.Spec.Provider) } -func GetBackendType(provider string) BackendType { +// GetBackendType returns a backend type that is known by Velero. +// If the provider doesn't indicate a known backend type, but the endpoint is +// specified, Velero regards it as a S3 compatible object store and return AWSBackend as the type. +func GetBackendType(provider string, config map[string]string) BackendType { if !strings.Contains(provider, "/") { provider = "velero.io/" + provider } - return BackendType(provider) + bt := BackendType(provider) + if IsBackendTypeValid(bt) { + return bt + } else if config != nil && config["s3Url"] != "" { + return AWSBackend + } else { + return bt + } } func IsBackendTypeValid(backendType BackendType) bool { diff --git a/pkg/repository/config/config_test.go b/pkg/repository/config/config_test.go index 2fa26a1936..4f18d6faea 100644 --- a/pkg/repository/config/config_test.go +++ b/pkg/repository/config/config_test.go @@ -48,7 +48,7 @@ func TestGetRepoIdentifier(t *testing.T) { }, }, repoName: "repo-1", - expectedErr: "restic repository prefix (resticRepoPrefix) not specified in backup storage location's config", + expectedErr: "invalid backend type velero.io/unsupported-provider, provider unsupported-provider", }, { name: "resticRepoPrefix in BSL config is used if set", @@ -69,6 +69,25 @@ func TestGetRepoIdentifier(t *testing.T) { repoName: "repo-1", expected: "custom:prefix:/restic/repo-1", }, + { + name: "s3Url in BSL config is used", + bsl: &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "custom-repo-identifier", + Config: map[string]string{ + "s3Url": "s3Url", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "bucket", + Prefix: "prefix", + }, + }, + }, + }, + repoName: "repo-1", + expected: "s3:s3Url/bucket/prefix/restic/repo-1", + }, { name: "s3.amazonaws.com URL format is used if region cannot be determined for AWS BSL", bsl: &velerov1api.BackupStorageLocation{ diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 30d1c97aaa..991ffc6980 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -344,7 +344,7 @@ func getRepoPassword(secretStore credentials.SecretStore) (string, error) { } func getStorageType(backupLocation *velerov1api.BackupStorageLocation) string { - backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider, backupLocation.Spec.Config) switch backendType { case repoconfig.AWSBackend: @@ -368,7 +368,7 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr return map[string]string{}, errors.New("invalid credentials interface") } - backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider, backupLocation.Spec.Config) if !repoconfig.IsBackendTypeValid(backendType) { return map[string]string{}, errors.New("invalid storage provider") } @@ -414,7 +414,7 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoBackend string, repoName string) (map[string]string, error) { result := make(map[string]string) - backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider, backupLocation.Spec.Config) if !repoconfig.IsBackendTypeValid(backendType) { return map[string]string{}, errors.New("invalid storage provider") } diff --git a/pkg/restic/common.go b/pkg/restic/common.go index f1ecb9a718..26eb2ef27a 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -92,7 +92,7 @@ func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileSto config[repoconfig.CredentialsFileKey] = credsFile } - backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider, backupLocation.Spec.Config) switch backendType { case repoconfig.AWSBackend: From 9d01432007675b660c2f7ffe2ded3675a6cf156a Mon Sep 17 00:00:00 2001 From: Faizan Ahmad Date: Tue, 25 Oct 2022 07:43:43 +0530 Subject: [PATCH 347/366] Fix pointer deference to string in error message Signed-off-by: Faizan Ahmad --- pkg/controller/backup_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 177b4f975e..67290db249 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -1000,7 +1000,7 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []snapshotv1api. len(*vs.Status.BoundVolumeSnapshotContentName) > 0 { var found bool if vsc, found = vscMap[*vs.Status.BoundVolumeSnapshotContentName]; !found { - logger.Errorf("Not find %s from the vscMap", vs.Status.BoundVolumeSnapshotContentName) + logger.Errorf("Not find %s from the vscMap", *vs.Status.BoundVolumeSnapshotContentName) return } From 150570feecb9a78832621157ebad825d5d6c5423 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 27 Oct 2022 14:20:26 +0800 Subject: [PATCH 348/366] Remove redundancy code block left by #5388. (#5483) Signed-off-by: Xun Jiang --- changelogs/unreleased/5483-blackpiglet | 1 + pkg/controller/backup_controller.go | 11 ----------- 2 files changed, 1 insertion(+), 11 deletions(-) create mode 100644 changelogs/unreleased/5483-blackpiglet diff --git a/changelogs/unreleased/5483-blackpiglet b/changelogs/unreleased/5483-blackpiglet new file mode 100644 index 0000000000..59d6accf3b --- /dev/null +++ b/changelogs/unreleased/5483-blackpiglet @@ -0,0 +1 @@ +Remove redundancy code block left by #5388. \ No newline at end of file diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 177b4f975e..50afedf5df 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -663,17 +663,6 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { selector := label.NewSelectorForBackup(backup.Name) vscList := &snapshotv1api.VolumeSnapshotContentList{} - if c.volumeSnapshotLister != nil { - tmpVSs, err := c.volumeSnapshotLister.List(selector) - if err != nil { - backupLog.Error(err) - } - - for _, vs := range tmpVSs { - volumeSnapshots = append(volumeSnapshots, *vs) - } - } - volumeSnapshots, err = c.waitVolumeSnapshotReadyToUse(context.Background(), backup.Spec.CSISnapshotTimeout.Duration, backup.Name) if err != nil { backupLog.Errorf("fail to wait VolumeSnapshot change to Ready: %s", err.Error()) From 3efa5357aa132878e6f82cce17134c489c00af85 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 21 Oct 2022 10:22:50 +0800 Subject: [PATCH 349/366] refactor pvbr doc Signed-off-by: Lyndon-Li --- changelogs/unreleased/5484-lyndon | 1 + site/content/docs/main/file-system-backup.md | 597 +++++++++++++++++++ site/content/docs/main/restic.md | 549 ----------------- site/data/docs/main-toc.yml | 8 +- site/layouts/index.redirects | 2 +- 5 files changed, 603 insertions(+), 554 deletions(-) create mode 100644 changelogs/unreleased/5484-lyndon create mode 100644 site/content/docs/main/file-system-backup.md delete mode 100644 site/content/docs/main/restic.md diff --git a/changelogs/unreleased/5484-lyndon b/changelogs/unreleased/5484-lyndon new file mode 100644 index 0000000000..6bd2dced1f --- /dev/null +++ b/changelogs/unreleased/5484-lyndon @@ -0,0 +1 @@ +Refactor Pod Volume Backup/Restore doc to match the new behavior diff --git a/site/content/docs/main/file-system-backup.md b/site/content/docs/main/file-system-backup.md new file mode 100644 index 0000000000..5b1043c63b --- /dev/null +++ b/site/content/docs/main/file-system-backup.md @@ -0,0 +1,597 @@ +--- +title: "File System Backup" +layout: docs +--- + +Velero supports backing up and restoring Kubernetes volumes attached to pods from the file system of the volumes, called +File System Backup (FSB shortly) or Pod Volume Backup. The data movement is fulfilled by using modules from free open-source +backup tools [restic][1] and [kopia][2]. This support is considered beta quality. Please see the list of [limitations](#limitations) +to understand if it fits your use case. + +Velero allows you to take snapshots of persistent volumes as part of your backups if you’re using one of +the supported cloud providers’ block storage offerings (Amazon EBS Volumes, Azure Managed Disks, Google Persistent Disks). +It also provides a plugin model that enables anyone to implement additional object and block storage backends, outside the +main Velero repository. + +Velero's File System Backup is an addition to the aforementioned snapshot approaches. Its pros and cons are listed below: +Pros: +- It is capable of backing up and restoring almost any type of Kubernetes volume. Therefore, if you need a volume snapshot +plugin for your storage platform, or if you're using EFS, AzureFile, NFS, emptyDir, local, or any other volume type that doesn't +have a native snapshot concept, FSB might be for you. +- It is not tied to a specific storage platform, so you could save the backup data to a different storage platform from +the one backing Kubernetes volumes, for example, a durable storage. + +Cons: +- It backs up data from the live file system, so the backup data is less consistent than the snapshot approaches. +- It access the file system from the mounted hostpath directory, so the pods need to run as root user and even under +privileged mode in some environments. + +**NOTE:** hostPath volumes are not supported, but the [local volume type][5] is supported. + +## Setup File System Backup + +### Prerequisites + +- Understand how Velero performs [file system backup](#how-backup-and-restore-work). +- [Download][4] the latest Velero release. +- Kubernetes v1.16.0 or later are required. Velero's File System Backup requires the Kubernetes [MountPropagation feature][6]. + +### Install Velero Node Agent + +Velero Node Agent is a Kubernetes daemonset that hosts FSB modules, i.e., restic, kopia uploader & repository. +To install Node Agent, use the `--use-node-agent` flag in the `velero install` command. See the [install overview][3] for more +details on other flags for the install command. + +``` +velero install --use-node-agent +``` + +When using FSB on a storage that doesn't have Velero support for snapshots, the `--use-volume-snapshots=false` flag prevents an +unused `VolumeSnapshotLocation` from being created on installation. + +At present, Velero FSB supports object storage as the backup storage only. Velero gets the parameters from the +[BackupStorageLocation `config`](api-types/backupstoragelocation.md) to compose the URL to the backup storage. Velero's known object +storage providers are include here [supported providers](supported-providers.md), for which, Velero pre-defines the endpoints; if you +want to use a different backup storage, make sure it is S3 compatible and you provide the correct bucket name and endpoint in +BackupStorageLocation. Alternatively, for Restic, you could set the `resticRepoPrefix` value in BackupStorageLocation. For example, +on AWS, `resticRepoPrefix` is something like `s3:s3-us-west-2.amazonaws.com/bucket` (note that `resticRepoPrefix` doesn't work for Kopia). +Velero handles the creation of the backup repo prefix in the backup storage, so make sure it is specified in BackupStorageLocation correctly. + +Velero creates one backup repo per namespace. For example, if backing up 2 namespaces, namespace1 and namespace2, using kopia +repository on AWS S3, the full backup repo path for namespace1 would be `https://s3-us-west-2.amazonaws.com/bucket/kopia/ns1` and +for namespace2 would be `https://s3-us-west-2.amazonaws.com/bucket/kopia/ns2`. + +There may be additional installation steps depending on the cloud provider plugin you are using. You should refer to the +[plugin specific documentation](supported-providers.md) for the must up to date information. + +### Configure Node Agent DaemonSet spec + +After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the node-agent DaemonSet spec. +The steps in this section are only needed if you are installing on RancherOS, OpenShift, VMware Tanzu Kubernetes Grid +Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. + + +**RancherOS** + + +Update the host path for volumes in the nonde-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to +`/opt/rke/var/lib/kubelet/pods`. + +```yaml +hostPath: + path: /var/lib/kubelet/pods +``` + +to + +```yaml +hostPath: + path: /opt/rke/var/lib/kubelet/pods +``` + + +**OpenShift** + + +To mount the correct hostpath to pods volumes, run the node-agent pod in `privileged` mode. + +1. Add the `velero` ServiceAccount to the `privileged` SCC: + + ``` + $ oc adm policy add-scc-to-user privileged -z velero -n velero + ``` + +2. Modify the DaemonSet yaml to request a privileged mode: + + ```diff + @@ -67,3 +67,5 @@ spec: + value: /credentials/cloud + - name: VELERO_SCRATCH_DIR + value: /scratch + + securityContext: + + privileged: true + ``` + + or + + ```shell + oc patch ds/node-agent \ + --namespace velero \ + --type json \ + -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value": { "privileged": true}}]' + ``` + + +If node-agent is not running in a privileged mode, it will not be able to access pods volumes within the mounted +hostpath directory because of the default enforced SELinux mode configured in the host system level. You can +[create a custom SCC](https://docs.openshift.com/container-platform/3.11/admin_guide/manage_scc.html) to relax the +security in your cluster so that node-agent pods are allowed to use the hostPath volume plug-in without granting +them access to the `privileged` SCC. + +By default a userland openshift namespace will not schedule pods on all nodes in the cluster. + +To schedule on all nodes the namespace needs an annotation: + +``` +oc annotate namespace openshift.io/node-selector="" +``` + +This should be done before velero installation. + +Or the ds needs to be deleted and recreated: + +``` +oc get ds node-agent -o yaml -n > ds.yaml +oc annotate namespace openshift.io/node-selector="" +oc create -n -f ds.yaml +``` + +**VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** + +You need to enable the `Allow Privileged` option in your plan configuration so that Velero is able to mount the hostpath. + +The hostPath should be changed from `/var/lib/kubelet/pods` to `/var/vcap/data/kubelet/pods` + +```yaml +hostPath: + path: /var/vcap/data/kubelet/pods +``` + + +**Microsoft Azure** + +If you are using [Azure Files][8], you need to add `nouser_xattr` to your storage class's `mountOptions`. +See [this restic issue][9] for more details. + +You can use the following command to patch the storage class: + +```bash +kubectl patch storageclass/ \ + --type json \ + --patch '[{"op":"add","path":"/mountOptions/-","value":"nouser_xattr"}]' +``` + +## To back up + +Velero supports two approaches of discovering pod volumes that need to be backed up using FSB: + +- Opt-in approach: Where every pod containing a volume to be backed up using FSB must be annotated +with the volume's name. +- Opt-out approach: Where all pod volumes are backed up using FSB, with the ability to opt-out any +volumes that should not be backed up. + +The following sections provide more details on the two approaches. + +### Using the opt-out approach + +In this approach, Velero will back up all pod volumes using FSB with the exception of: + +- Volumes mounting the default service account token, Kubernetes secrets, and config maps +- Hostpath volumes + +It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` +annotation on the pod. + +Instructions to back up using this approach are as follows: + +1. Run the following command on each pod that contains volumes that should **not** be backed up using FSB + + ```bash + kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes-excludes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... + ``` + where the volume names are the names of the volumes in the pod spec. + + For example, in the following pod: + + ```yaml + apiVersion: v1 + kind: Pod + metadata: + name: app1 + namespace: sample + spec: + containers: + - image: k8s.gcr.io/test-webserver + name: test-webserver + volumeMounts: + - name: pvc1-vm + mountPath: /volume-1 + - name: pvc2-vm + mountPath: /volume-2 + volumes: + - name: pvc1-vm + persistentVolumeClaim: + claimName: pvc1 + - name: pvc2-vm + claimName: pvc2 + ``` + to exclude FSB of volume `pvc1-vm`, you would run: + + ```bash + kubectl -n sample annotate pod/app1 backup.velero.io/backup-volumes-excludes=pvc1-vm + ``` + +2. Take a Velero backup: + + ```bash + velero backup create BACKUP_NAME --default-volumes-to-fs-backup OTHER_OPTIONS + ``` + + The above steps uses the opt-out approach on a per backup basis. + + Alternatively, this behavior may be enabled on all velero backups running the `velero install` command with + the `--default-volumes-to-fs-backup` flag. Refer [install overview][10] for details. + +3. When the backup completes, view information about the backups: + + ```bash + velero backup describe YOUR_BACKUP_NAME + ``` + ```bash + kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml + ``` + +### Using opt-in pod volume backup + +Velero, by default, uses this approach to discover pod volumes that need to be backed up using FSB. Every pod +containing a volume to be backed up using FSB must be annotated with the volume's name using the +`backup.velero.io/backup-volumes` annotation. + +Instructions to back up using this approach are as follows: + +1. Run the following for each pod that contains a volume to back up: + + ```bash + kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... + ``` + + where the volume names are the names of the volumes in the pod spec. + + For example, for the following pod: + + ```yaml + apiVersion: v1 + kind: Pod + metadata: + name: sample + namespace: foo + spec: + containers: + - image: k8s.gcr.io/test-webserver + name: test-webserver + volumeMounts: + - name: pvc-volume + mountPath: /volume-1 + - name: emptydir-volume + mountPath: /volume-2 + volumes: + - name: pvc-volume + persistentVolumeClaim: + claimName: test-volume-claim + - name: emptydir-volume + emptyDir: {} + ``` + + You'd run: + + ```bash + kubectl -n foo annotate pod/sample backup.velero.io/backup-volumes=pvc-volume,emptydir-volume + ``` + + This annotation can also be provided in a pod template spec if you use a controller to manage your pods. + +1. Take a Velero backup: + + ```bash + velero backup create NAME OPTIONS... + ``` + +1. When the backup completes, view information about the backups: + + ```bash + velero backup describe YOUR_BACKUP_NAME + ``` + ```bash + kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml + ``` + +## To restore + +Regardless of how volumes are discovered for backup using FSB, the process of restoring remains the same. + +1. Restore from your Velero backup: + + ```bash + velero restore create --from-backup BACKUP_NAME OPTIONS... + ``` + +1. When the restore completes, view information about your pod volume restores: + + ```bash + velero restore describe YOUR_RESTORE_NAME + ``` + ```bash + kubectl -n velero get podvolumerestores -l velero.io/restore-name=YOUR_RESTORE_NAME -o yaml + ``` + +## Limitations + +- `hostPath` volumes are not supported. [Local persistent volumes][5] are supported. +- At present, Velero uses a static, common encryption key for all backup repositories it creates. **This means +that anyone who has access to your backup storage can decrypt your backup data**. Make sure that you limit access +to the backup storage appropriately. +- An incremental backup chain will be maintained across pod reschedules for PVCs. However, for pod volumes that +are *not* PVCs, such as `emptyDir` volumes, when a pod is deleted/recreated (for example, by a ReplicaSet/Deployment), +the next backup of those volumes will be full rather than incremental, because the pod volume's lifecycle is assumed +to be defined by its pod. +- Even though the backup data could be incrementally preserved, for a single file data, FSB leverages on deduplication +to find the difference to be saved. This means that large files (such as ones storing a database) will take a long time +to scan for data deduplication, even if the actual difference is small. +- You may need to [customize the resource limits](/docs/main/customize-installation/#customize-resource-requests-and-limits) +to make sure backups complete successfully for massive small files or large backup size cases, for more details refer to +[Velero File System Backup Performance Guide](https://empty-to-be-created). +- Velero's File System Backup reads/writes data from volumes by accessing the node's filesystem, on which the pod is running. +For this reason, FSB can only backup volumes that are mounted by a pod and not directly from the PVC. For orphan PVC/PV pairs +(without running pods), some Velero users overcame this limitation running a staging pod (i.e. a busybox or alpine container +with an infinite sleep) to mount these PVC/PV pairs prior taking a Velero backup. + +## Customize Restore Helper Container + +Velero uses a helper init container when performing a FSB restore. By default, the image for this container is +`velero/velero-restore-helper:`, where `VERSION` matches the version/tag of the main Velero image. +You can customize the image that is used for this helper by creating a ConfigMap in the Velero namespace with the alternate image. + +In addition, you can customize the resource requirements for the init container, should you need. + +The ConfigMap must look like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + # any name can be used; Velero uses the labels (below) + # to identify it rather than the name + name: fs-restore-action-config + # must be in the velero namespace + namespace: velero + # the below labels should be used verbatim in your + # ConfigMap. + labels: + # this value-less label identifies the ConfigMap as + # config for a plugin (i.e. the built-in restore + # item action plugin) + velero.io/plugin-config: "" + # this label identifies the name and kind of plugin + # that this ConfigMap is for. + velero.io/pod-volume-restore: RestoreItemAction +data: + # The value for "image" can either include a tag or not; + # if the tag is *not* included, the tag from the main Velero + # image will automatically be used. + image: myregistry.io/my-custom-helper-image[:OPTIONAL_TAG] + + # "cpuRequest" sets the request.cpu value on the restore init containers during restore. + # If not set, it will default to "100m". A value of "0" is treated as unbounded. + cpuRequest: 200m + + # "memRequest" sets the request.memory value on the restore init containers during restore. + # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. + memRequest: 128Mi + + # "cpuLimit" sets the request.cpu value on the restore init containers during restore. + # If not set, it will default to "100m". A value of "0" is treated as unbounded. + cpuLimit: 200m + + # "memLimit" sets the request.memory value on the restore init containers during restore. + # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. + memLimit: 128Mi + + # "secCtxRunAsUser" sets the securityContext.runAsUser value on the restore init containers during restore. + secCtxRunAsUser: 1001 + + # "secCtxRunAsGroup" sets the securityContext.runAsGroup value on the restore init containers during restore. + secCtxRunAsGroup: 999 + + # "secCtxAllowPrivilegeEscalation" sets the securityContext.allowPrivilegeEscalation value on the restore init containers during restore. + secCtxAllowPrivilegeEscalation: false + + # "secCtx" sets the securityContext object value on the restore init containers during restore. + # This key override `secCtxRunAsUser`, `secCtxRunAsGroup`, `secCtxAllowPrivilegeEscalation` if `secCtx.runAsUser`, `secCtx.runAsGroup` or `secCtx.allowPrivilegeEscalation` are set. + secCtx: | + capabilities: + drop: + - ALL + add: [] + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsUser: 1001 + runAsGroup: 999 + +``` + +## Troubleshooting + +Run the following checks: + +Are your Velero server and daemonset pods running? + +```bash +kubectl get pods -n velero +``` + +Does your backup repository exist, and is it ready? + +```bash +velero repo get + +velero repo get REPO_NAME -o yaml +``` + +Are there any errors in your Velero backup/restore? + +```bash +velero backup describe BACKUP_NAME +velero backup logs BACKUP_NAME + +velero restore describe RESTORE_NAME +velero restore logs RESTORE_NAME +``` + +What is the status of your pod volume backups/restores? + +```bash +kubectl -n velero get podvolumebackups -l velero.io/backup-name=BACKUP_NAME -o yaml + +kubectl -n velero get podvolumerestores -l velero.io/restore-name=RESTORE_NAME -o yaml +``` + +Is there any useful information in the Velero server or daemon pod logs? + +```bash +kubectl -n velero logs deploy/velero +kubectl -n velero logs DAEMON_POD_NAME +``` + +**NOTE**: You can increase the verbosity of the pod logs by adding `--log-level=debug` as an argument +to the container command in the deployment/daemonset pod template spec. + +## How backup and restore work + +### How Velero integrates with Restic +Velero integrate Restic binary directly, so the operations are done by calling Restic commands: +- Run `restic init` command to initialize the [restic repository](https://restic.readthedocs.io/en/latest/100_references.html#terminology) +- Run `restic prune` command periodically to prune restic repository +- Run `restic backup` commands to backup pod volume data +- Run `restic restore` commands to restore pod volume data + +### How Velero integrates with Kopia +Velero integrate Kopia modules into Velero's code, primarily two modules: +- Kopia Uploader: Velero makes some wrap and isolation around it to create a generic file system uploader, +which is used to backup pod volume data +- Kopia Repository: Velero integrates it with Velero's Unified Repository Interface, it is used to preserve the backup data and manage +the backup storage + +For more details, refer to [kopia architecture](https://kopia.io/docs/advanced/architecture/) and +Velero's [Unified Repository design](https://github.com/vmware-tanzu/velero/pull/4926) + +### Custom resource and controllers +Velero has three custom resource definitions and associated controllers: + +- `BackupRepository` - represents/manages the lifecycle of Velero's backup repositories. Velero creates +a backup repository per namespace when the first FSB backup/restore for a namespace is requested. The backup +repository is backed by restic or kopia, the `BackupRepository` controller invokes restic or kopia internally, +refer to [restic integration](#how-velero-integrates-with-restic) and [kopia integration](#how-velero-integrates-with-kopia) +for details. + + You can see information about your Velero's backup repositories by running `velero repo get`. + +- `PodVolumeBackup` - represents a FSB backup of a volume in a pod. The main Velero backup process creates +one or more of these when it finds an annotated pod. Each node in the cluster runs a controller for this +resource (in a daemonset) that handles the `PodVolumeBackups` for pods on that node. `PodVolumeBackup` is backed by +restic or kopia, the controller invokes restic or kopia internally, refer to [restic integration](#how-velero-integrates-with-restic) +and [kopia integration](#how-velero-integrates-with-kopia) for details. + +- `PodVolumeRestore` - represents a FSB restore of a pod volume. The main Velero restore process creates one +or more of these when it encounters a pod that has associated FSB backups. Each node in the cluster runs a +controller for this resource (in the same daemonset as above) that handles the `PodVolumeRestores` for pods +on that node. `PodVolumeRestore` is backed by restic or kopia, the controller invokes restic or kopia internally, +refer to [restic integration](#how-velero-integrates-with-restic) and [kopia integration](#how-velero-integrates-with-kopia) for details. + +### Path selection +Velero's FSB supports two data movement paths, the restic path and the kopia path. Velero allows users to select +between the two paths: +- For backup, the path is specified at the installation time through the `uploader-type` flag, the valid value is +either `restic` or `kopia`, or default to `restic` if the value is not specified. The selection is not allowed to be +changed after the installation. +- For restore, the path is decided by the path used to back up the data, it is automatically selected. For example, +if you've created a backup with restic path, then you reinstall Velero with `uploader-type=kopia`, when you create +a restore from the backup, the restore still goes with restic path. + +### Backup + +1. Based on configuration, the main Velero backup process uses the opt-in or opt-out approach to check each pod +that it's backing up for the volumes to be backed up using FSB. +2. When found, Velero first ensures a backup repository exists for the pod's namespace, by: + - checking if a `BackupRepository` custom resource already exists + - if not, creating a new one, and waiting for the `BackupRepository` controller to init/connect it +3. Velero then creates a `PodVolumeBackup` custom resource per volume listed in the pod annotation +4. The main Velero process now waits for the `PodVolumeBackup` resources to complete or fail +5. Meanwhile, each `PodVolumeBackup` is handled by the controller on the appropriate node, which: + - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data + - finds the pod volume's subdirectory within the above volume + - based on the path selection, Velero inokes restic or kopia for backup + - updates the status of the custom resource to `Completed` or `Failed` +6. As each `PodVolumeBackup` finishes, the main Velero process adds it to the Velero backup in a file named +`-podvolumebackups.json.gz`. This file gets uploaded to object storage alongside the backup tarball. +It will be used for restores, as seen in the next section. + +### Restore + +1. The main Velero restore process checks each existing `PodVolumeBackup` custom resource in the cluster to backup from. +2. For each `PodVolumeBackup` found, Velero first ensures a backup repository exists for the pod's namespace, by: + - checking if a `BackupRepository` custom resource already exists + - if not, creating a new one, and waiting for the `BackupRepository` controller to connect it (note that + in this case, the actual repository should already exist in backup storage, so the Velero controller will simply + check it for integrity and make a location connection) +3. Velero adds an init container to the pod, whose job is to wait for all FSB restores for the pod to complete (more +on this shortly) +4. Velero creates the pod, with the added init container, by submitting it to the Kubernetes API. Then, the Kubernetes +scheduler schedules this pod to a worker node, and the pod must be in a running state. If the pod fails to start for +some reason (i.e. lack of cluster resources), the FSB restore will not be done. +5. Velero creates a `PodVolumeRestore` custom resource for each volume to be restored in the pod +6. The main Velero process now waits for each `PodVolumeRestore` resource to complete or fail +7. Meanwhile, each `PodVolumeRestore` is handled by the controller on the appropriate node, which: + - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data + - waits for the pod to be running the init container + - finds the pod volume's subdirectory within the above volume + - based on the path selection, Velero inokes restic or kopia for restore + - on success, writes a file into the pod volume, in a `.velero` subdirectory, whose name is the UID of the Velero + restore that this pod volume restore is for + - updates the status of the custom resource to `Completed` or `Failed` +8. The init container that was added to the pod is running a process that waits until it finds a file +within each restored volume, under `.velero`, whose name is the UID of the Velero restore being run +9. Once all such files are found, the init container's process terminates successfully and the pod moves +on to running other init containers/the main containers. + +Velero won't restore a resource if a that resource is scaled to 0 and already exists in the cluster. If Velero restored the +requested pods in this scenario, the Kubernetes reconciliation loops that manage resources would delete the running pods +because its scaled to be 0. Velero will be able to restore once the resources is scaled up, and the pods are created and remain running. + +## 3rd party controllers + +### Monitor backup annotation + +Velero does not provide a mechanism to detect persistent volume claims that are missing the File System Backup annotation. + +To solve this, a controller was written by Thomann Bits&Beats: [velero-pvc-watcher][7] + +[1]: https://github.com/restic/restic +[2]: https://github.com/kopia/kopia +[3]: customize-installation.md#enable-restic-integration +[4]: https://github.com/vmware-tanzu/velero/releases/ +[5]: https://kubernetes.io/docs/concepts/storage/volumes/#local +[6]: https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation +[7]: https://github.com/bitsbeats/velero-pvc-watcher +[8]: https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv +[9]: https://github.com/restic/restic/issues/1800 +[10]: customize-installation.md#default-pod-volume-backup-to-file-system-backup diff --git a/site/content/docs/main/restic.md b/site/content/docs/main/restic.md deleted file mode 100644 index 492738af15..0000000000 --- a/site/content/docs/main/restic.md +++ /dev/null @@ -1,549 +0,0 @@ ---- -title: "Restic Integration" -layout: docs ---- - -Velero supports backing up and restoring Kubernetes volumes using a free open-source backup tool called [restic][1]. This support is considered beta quality. Please see the list of [limitations](#limitations) to understand if it fits your use case. - -Velero allows you to take snapshots of persistent volumes as part of your backups if you’re using one of -the supported cloud providers’ block storage offerings (Amazon EBS Volumes, Azure Managed Disks, Google Persistent Disks). -It also provides a plugin model that enables anyone to implement additional object and block storage backends, outside the -main Velero repository. - -Velero's Restic integration was added to give you an out-of-the-box solution for backing up and restoring almost any type of Kubernetes volume. This integration is an addition to Velero's capabilities, not a replacement for existing functionality. If you're running on AWS, and taking EBS snapshots as part of your regular Velero backups, there's no need to switch to using Restic. However, if you need a volume snapshot plugin for your storage platform, or if you're using EFS, AzureFile, NFS, emptyDir, -local, or any other volume type that doesn't have a native snapshot concept, Restic might be for you. - -Restic is not tied to a specific storage platform, which means that this integration also paves the way for future work to enable -cross-volume-type data migrations. - -**NOTE:** hostPath volumes are not supported, but the [local volume type][4] is supported. - -## Setup Restic - -### Prerequisites - -- Understand how Velero performs [backups with the Restic integration](#how-backup-and-restore-work-with-restic). -- [Download][3] the latest Velero release. -- Kubernetes v1.16.0 and later. Velero's Restic integration requires the Kubernetes [MountPropagation feature][6]. - -### Install Restic - -To install Restic, use the `--use-restic` flag in the `velero install` command. See the [install overview][2] for more details on other flags for the install command. - -``` -velero install --use-restic -``` - -When using Restic on a storage provider that doesn't have Velero support for snapshots, the `--use-volume-snapshots=false` flag prevents an unused `VolumeSnapshotLocation` from being created on installation. - -Velero handles the creation of the restic repo prefix for Amazon, Azure, and GCP plugins, if you are using a different [provider plugin](supported-providers.md), then you will need to make sure the `resticRepoPrefix` is set in the [BackupStorageLocation `config`](api-types/backupstoragelocation.md). The value for `resticRepoPrefix` should be the cloud storage URL where all namespace restic repos will be created. Velero creates one restic repo per namespace. For example, if backing up 2 namespaces, namespace1 and namespace2, using restic on AWS, the `resticRepoPrefix` would be something like `s3:s3-us-west-2.amazonaws.com/bucket/restic` and the full restic repo path for namespace1 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns1` and for namespace2 would be `s3:s3-us-west-2.amazonaws.com/bucket/restic/ns2`. - -There may be additional installation steps depending on the cloud provider plugin you are using. You should refer to the [plugin specific documentation](supported-providers.md) for the must up to date information. - -### Configure Restic DaemonSet spec - -After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the Restic DaemonSet spec. The steps in this section are only needed if you are installing on RancherOS, OpenShift, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. - - -**RancherOS** - - -Update the host path for volumes in the Restic DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to `/opt/rke/var/lib/kubelet/pods`. - -```yaml -hostPath: - path: /var/lib/kubelet/pods -``` - -to - -```yaml -hostPath: - path: /opt/rke/var/lib/kubelet/pods -``` - - -**OpenShift** - - -To mount the correct hostpath to pods volumes, run the Restic pod in `privileged` mode. - -1. Add the `velero` ServiceAccount to the `privileged` SCC: - - ``` - $ oc adm policy add-scc-to-user privileged -z velero -n velero - ``` - -2. For OpenShift version >= `4.1`, modify the DaemonSet yaml to request a privileged mode: - - ```diff - @@ -67,3 +67,5 @@ spec: - value: /credentials/cloud - - name: VELERO_SCRATCH_DIR - value: /scratch - + securityContext: - + privileged: true - ``` - - or - - ```shell - oc patch ds/restic \ - --namespace velero \ - --type json \ - -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value": { "privileged": true}}]' - ``` - -3. For OpenShift version < `4.1`, modify the DaemonSet yaml to request a privileged mode and mount the correct hostpath to pods volumes. - - ```diff - @@ -35,7 +35,7 @@ spec: - secretName: cloud-credentials - - name: host-pods - hostPath: - - path: /var/lib/kubelet/pods - + path: /var/lib/origin/openshift.local.volumes/pods - - name: scratch - emptyDir: {} - containers: - @@ -67,3 +67,5 @@ spec: - value: /credentials/cloud - - name: VELERO_SCRATCH_DIR - value: /scratch - + securityContext: - + privileged: true - ``` - - or - - ```shell - oc patch ds/restic \ - --namespace velero \ - --type json \ - -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value": { "privileged": true}}]' - - oc patch ds/restic \ - --namespace velero \ - --type json \ - -p '[{"op":"replace","path":"/spec/template/spec/volumes/0/hostPath","value": { "path": "/var/lib/origin/openshift.local.volumes/pods"}}]' - ``` - - -If Restic is not running in a privileged mode, it will not be able to access pods volumes within the mounted hostpath directory because of the default enforced SELinux mode configured in the host system level. You can [create a custom SCC](https://docs.openshift.com/container-platform/3.11/admin_guide/manage_scc.html) to relax the security in your cluster so that Restic pods are allowed to use the hostPath volume plug-in without granting them access to the `privileged` SCC. - -By default a userland openshift namespace will not schedule pods on all nodes in the cluster. - -To schedule on all nodes the namespace needs an annotation: - -``` -oc annotate namespace openshift.io/node-selector="" -``` - -This should be done before velero installation. - -Or the ds needs to be deleted and recreated: - -``` -oc get ds restic -o yaml -n > ds.yaml -oc annotate namespace openshift.io/node-selector="" -oc create -n -f ds.yaml -``` - -**VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** - -You need to enable the `Allow Privileged` option in your plan configuration so that Restic is able to mount the hostpath. - -The hostPath should be changed from `/var/lib/kubelet/pods` to `/var/vcap/data/kubelet/pods` - -```yaml -hostPath: - path: /var/vcap/data/kubelet/pods -``` - - -**Microsoft Azure** - -If you are using [Azure Files][8], you need to add `nouser_xattr` to your storage class's `mountOptions`. See [this restic issue][9] for more details. - -You can use the following command to patch the storage class: - -```bash -kubectl patch storageclass/ \ - --type json \ - --patch '[{"op":"add","path":"/mountOptions/-","value":"nouser_xattr"}]' -``` - -## To back up - -Velero supports two approaches of discovering pod volumes that need to be backed up using Restic: - -- Opt-in approach: Where every pod containing a volume to be backed up using Restic must be annotated with the volume's name. -- Opt-out approach: Where all pod volumes are backed up using Restic, with the ability to opt-out any volumes that should not be backed up. - -The following sections provide more details on the two approaches. - -### Using the opt-out approach - -In this approach, Velero will back up all pod volumes using Restic with the exception of: - -- Volumes mounting the default service account token, Kubernetes secrets, and config maps -- Hostpath volumes - -It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` annotation on the pod. - -Instructions to back up using this approach are as follows: - -1. Run the following command on each pod that contains volumes that should **not** be backed up using Restic - - ```bash - kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes-excludes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... - ``` - where the volume names are the names of the volumes in the pod spec. - - For example, in the following pod: - - ```yaml - apiVersion: v1 - kind: Pod - metadata: - name: app1 - namespace: sample - spec: - containers: - - image: k8s.gcr.io/test-webserver - name: test-webserver - volumeMounts: - - name: pvc1-vm - mountPath: /volume-1 - - name: pvc2-vm - mountPath: /volume-2 - volumes: - - name: pvc1-vm - persistentVolumeClaim: - claimName: pvc1 - - name: pvc2-vm - claimName: pvc2 - ``` - to exclude Restic backup of volume `pvc1-vm`, you would run: - - ```bash - kubectl -n sample annotate pod/app1 backup.velero.io/backup-volumes-excludes=pvc1-vm - ``` - -2. Take a Velero backup: - - ```bash - velero backup create BACKUP_NAME --default-volumes-to-restic OTHER_OPTIONS - ``` - - The above steps uses the opt-out approach on a per backup basis. - - Alternatively, this behavior may be enabled on all velero backups running the `velero install` command with the `--default-volumes-to-restic` flag. Refer [install overview][11] for details. - -3. When the backup completes, view information about the backups: - - ```bash - velero backup describe YOUR_BACKUP_NAME - ``` - ```bash - kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml - ``` - -### Using opt-in pod volume backup - -Velero, by default, uses this approach to discover pod volumes that need to be backed up using Restic. Every pod containing a volume to be backed up using Restic must be annotated with the volume's name using the `backup.velero.io/backup-volumes` annotation. - -Instructions to back up using this approach are as follows: - -1. Run the following for each pod that contains a volume to back up: - - ```bash - kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... - ``` - - where the volume names are the names of the volumes in the pod spec. - - For example, for the following pod: - - ```yaml - apiVersion: v1 - kind: Pod - metadata: - name: sample - namespace: foo - spec: - containers: - - image: k8s.gcr.io/test-webserver - name: test-webserver - volumeMounts: - - name: pvc-volume - mountPath: /volume-1 - - name: emptydir-volume - mountPath: /volume-2 - volumes: - - name: pvc-volume - persistentVolumeClaim: - claimName: test-volume-claim - - name: emptydir-volume - emptyDir: {} - ``` - - You'd run: - - ```bash - kubectl -n foo annotate pod/sample backup.velero.io/backup-volumes=pvc-volume,emptydir-volume - ``` - - This annotation can also be provided in a pod template spec if you use a controller to manage your pods. - -1. Take a Velero backup: - - ```bash - velero backup create NAME OPTIONS... - ``` - -1. When the backup completes, view information about the backups: - - ```bash - velero backup describe YOUR_BACKUP_NAME - ``` - ```bash - kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml - ``` - -## To restore - -Regardless of how volumes are discovered for backup using Restic, the process of restoring remains the same. - -1. Restore from your Velero backup: - - ```bash - velero restore create --from-backup BACKUP_NAME OPTIONS... - ``` - -1. When the restore completes, view information about your pod volume restores: - - ```bash - velero restore describe YOUR_RESTORE_NAME - ``` - ```bash - kubectl -n velero get podvolumerestores -l velero.io/restore-name=YOUR_RESTORE_NAME -o yaml - ``` - -## Limitations - -- `hostPath` volumes are not supported. [Local persistent volumes][4] are supported. -- Those of you familiar with [restic][1] may know that it encrypts all of its data. Velero uses a static, -common encryption key for all Restic repositories it creates. **This means that anyone who has access to your -bucket can decrypt your Restic backup data**. Make sure that you limit access to the Restic bucket -appropriately. -- An incremental backup chain will be maintained across pod reschedules for PVCs. However, for pod volumes that are *not* -PVCs, such as `emptyDir` volumes, when a pod is deleted/recreated (for example, by a ReplicaSet/Deployment), the next backup of those -volumes will be full rather than incremental, because the pod volume's lifecycle is assumed to be defined by its pod. -- Restic scans each file in a single thread. This means that large files (such as ones storing a database) will take a long time to scan for data deduplication, even if the actual -difference is small. -- If you plan to use Velero's Restic integration to backup 100GB of data or more, you may need to [customize the resource limits](/docs/main/customize-installation/#customize-resource-requests-and-limits) to make sure backups complete successfully. -- Velero's Restic integration backs up data from volumes by accessing the node's filesystem, on which the pod is running. For this reason, Velero's Restic integration can only backup volumes that are mounted by a pod and not directly from the PVC. For orphan PVC/PV pairs (without running pods), some Velero users overcame this limitation running a staging pod (i.e. a busybox or alpine container with an infinite sleep) to mount these PVC/PV pairs prior taking a Velero backup. - -## Customize Restore Helper Container - -Velero uses a helper init container when performing a Restic restore. By default, the image for this container is `velero/velero-restic-restore-helper:`, -where `VERSION` matches the version/tag of the main Velero image. You can customize the image that is used for this helper by creating a ConfigMap in the Velero namespace with -the alternate image. - -In addition, you can customize the resource requirements for the init container, should you need. - -The ConfigMap must look like the following: - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - # any name can be used; Velero uses the labels (below) - # to identify it rather than the name - name: restic-restore-action-config - # must be in the velero namespace - namespace: velero - # the below labels should be used verbatim in your - # ConfigMap. - labels: - # this value-less label identifies the ConfigMap as - # config for a plugin (i.e. the built-in restic restore - # item action plugin) - velero.io/plugin-config: "" - # this label identifies the name and kind of plugin - # that this ConfigMap is for. - velero.io/restic: RestoreItemAction -data: - # The value for "image" can either include a tag or not; - # if the tag is *not* included, the tag from the main Velero - # image will automatically be used. - image: myregistry.io/my-custom-helper-image[:OPTIONAL_TAG] - - # "cpuRequest" sets the request.cpu value on the restic init containers during restore. - # If not set, it will default to "100m". A value of "0" is treated as unbounded. - cpuRequest: 200m - - # "memRequest" sets the request.memory value on the restic init containers during restore. - # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. - memRequest: 128Mi - - # "cpuLimit" sets the request.cpu value on the restic init containers during restore. - # If not set, it will default to "100m". A value of "0" is treated as unbounded. - cpuLimit: 200m - - # "memLimit" sets the request.memory value on the restic init containers during restore. - # If not set, it will default to "128Mi". A value of "0" is treated as unbounded. - memLimit: 128Mi - - # "secCtxRunAsUser" sets the securityContext.runAsUser value on the restic init containers during restore. - secCtxRunAsUser: 1001 - - # "secCtxRunAsGroup" sets the securityContext.runAsGroup value on the restic init containers during restore. - secCtxRunAsGroup: 999 - - # "secCtxAllowPrivilegeEscalation" sets the securityContext.allowPrivilegeEscalation value on the restic init containers during restore. - secCtxAllowPrivilegeEscalation: false - - # "secCtx" sets the securityContext object value on the restic init containers during restore. - # This key override `secCtxRunAsUser`, `secCtxRunAsGroup`, `secCtxAllowPrivilegeEscalation` if `secCtx.runAsUser`, `secCtx.runAsGroup` or `secCtx.allowPrivilegeEscalation` are set. - secCtx: | - capabilities: - drop: - - ALL - add: [] - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1001 - runAsGroup: 999 - -``` - -## Troubleshooting - -Run the following checks: - -Are your Velero server and daemonset pods running? - -```bash -kubectl get pods -n velero -``` - -Does your Restic repository exist, and is it ready? - -```bash -velero restic repo get - -velero restic repo get REPO_NAME -o yaml -``` - -Are there any errors in your Velero backup/restore? - -```bash -velero backup describe BACKUP_NAME -velero backup logs BACKUP_NAME - -velero restore describe RESTORE_NAME -velero restore logs RESTORE_NAME -``` - -What is the status of your pod volume backups/restores? - -```bash -kubectl -n velero get podvolumebackups -l velero.io/backup-name=BACKUP_NAME -o yaml - -kubectl -n velero get podvolumerestores -l velero.io/restore-name=RESTORE_NAME -o yaml -``` - -Is there any useful information in the Velero server or daemon pod logs? - -```bash -kubectl -n velero logs deploy/velero -kubectl -n velero logs DAEMON_POD_NAME -``` - -**NOTE**: You can increase the verbosity of the pod logs by adding `--log-level=debug` as an argument -to the container command in the deployment/daemonset pod template spec. - -## How backup and restore work with Restic - -Velero has three custom resource definitions and associated controllers: - -- `ResticRepository` - represents/manages the lifecycle of Velero's [restic repositories][5]. Velero creates -a Restic repository per namespace when the first Restic backup for a namespace is requested. The controller -for this custom resource executes Restic repository lifecycle commands -- `restic init`, `restic check`, -and `restic prune`. - - You can see information about your Velero's Restic repositories by running `velero restic repo get`. - -- `PodVolumeBackup` - represents a Restic backup of a volume in a pod. The main Velero backup process creates -one or more of these when it finds an annotated pod. Each node in the cluster runs a controller for this -resource (in a daemonset) that handles the `PodVolumeBackups` for pods on that node. The controller executes -`restic backup` commands to backup pod volume data. - -- `PodVolumeRestore` - represents a Restic restore of a pod volume. The main Velero restore process creates one -or more of these when it encounters a pod that has associated Restic backups. Each node in the cluster runs a -controller for this resource (in the same daemonset as above) that handles the `PodVolumeRestores` for pods -on that node. The controller executes `restic restore` commands to restore pod volume data. - -### Backup - -1. Based on configuration, the main Velero backup process uses the opt-in or opt-out approach to check each pod that it's backing up for the volumes to be backed up using Restic. -1. When found, Velero first ensures a Restic repository exists for the pod's namespace, by: - - checking if a `ResticRepository` custom resource already exists - - if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it -1. Velero then creates a `PodVolumeBackup` custom resource per volume listed in the pod annotation -1. The main Velero process now waits for the `PodVolumeBackup` resources to complete or fail -1. Meanwhile, each `PodVolumeBackup` is handled by the controller on the appropriate node, which: - - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data - - finds the pod volume's subdirectory within the above volume - - runs `restic backup` - - updates the status of the custom resource to `Completed` or `Failed` -1. As each `PodVolumeBackup` finishes, the main Velero process adds it to the Velero backup in a file named `-podvolumebackups.json.gz`. This file gets uploaded to object storage alongside the backup tarball. It will be used for restores, as seen in the next section. - -### Restore - -1. The main Velero restore process checks each existing `PodVolumeBackup` custom resource in the cluster to backup from. -1. For each `PodVolumeBackup` found, Velero first ensures a Restic repository exists for the pod's namespace, by: - - checking if a `ResticRepository` custom resource already exists - - if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it (note that - in this case, the actual repository should already exist in object storage, so the Velero controller will simply - check it for integrity) -1. Velero adds an init container to the pod, whose job is to wait for all Restic restores for the pod to complete (more -on this shortly) -1. Velero creates the pod, with the added init container, by submitting it to the Kubernetes API. Then, the Kubernetes scheduler schedules this pod to a worker node, and the pod must be in a running state. If the pod fails to start for some reason (i.e. lack of cluster resources), the Restic restore will not be done. -1. Velero creates a `PodVolumeRestore` custom resource for each volume to be restored in the pod -1. The main Velero process now waits for each `PodVolumeRestore` resource to complete or fail -1. Meanwhile, each `PodVolumeRestore` is handled by the controller on the appropriate node, which: - - has a hostPath volume mount of `/var/lib/kubelet/pods` to access the pod volume data - - waits for the pod to be running the init container - - finds the pod volume's subdirectory within the above volume - - runs `restic restore` - - on success, writes a file into the pod volume, in a `.velero` subdirectory, whose name is the UID of the Velero restore - that this pod volume restore is for - - updates the status of the custom resource to `Completed` or `Failed` -1. The init container that was added to the pod is running a process that waits until it finds a file -within each restored volume, under `.velero`, whose name is the UID of the Velero restore being run -1. Once all such files are found, the init container's process terminates successfully and the pod moves -on to running other init containers/the main containers. - -Velero won't restore a resource if a that resource is scaled to 0 and already exists in the cluster. If Velero restored the requested pods in this scenario, the Kubernetes reconciliation loops that manage resources would delete the running pods because its scaled to be 0. Velero will be able to restore once the resources is scaled up, and the pods are created and remain running. - -## 3rd party controllers - -### Monitor backup annotation - -Velero does not provide a mechanism to detect persistent volume claims that are missing the Restic backup annotation. - -To solve this, a controller was written by Thomann Bits&Beats: [velero-pvc-watcher][7] - -[1]: https://github.com/restic/restic -[2]: customize-installation.md#enable-restic-integration -[3]: https://github.com/vmware-tanzu/velero/releases/ -[4]: https://kubernetes.io/docs/concepts/storage/volumes/#local -[5]: http://restic.readthedocs.io/en/latest/100_references.html#terminology -[6]: https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation -[7]: https://github.com/bitsbeats/velero-pvc-watcher -[8]: https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv -[9]: https://github.com/restic/restic/issues/1800 -[11]: customize-installation.md#default-pod-volume-backup-to-restic diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index 0402cc29cd..5442c62cc7 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -19,8 +19,8 @@ toc: url: /supported-providers - page: Evaluation install url: /contributions/minio - - page: Restic integration - url: /restic + - page: File system backup + url: /file-system-backup - page: Examples url: /examples - page: Uninstalling @@ -65,8 +65,8 @@ toc: url: /debugging-install - page: Troubleshoot a restore url: /debugging-restores - - page: Troubleshoot Restic - url: /restic#troubleshooting + - page: Troubleshoot file system backup + url: /file-system-backup#troubleshooting - title: Contribute subfolderitems: - page: Start Contributing diff --git a/site/layouts/index.redirects b/site/layouts/index.redirects index b336ccfb1d..bd30d5d67d 100644 --- a/site/layouts/index.redirects +++ b/site/layouts/index.redirects @@ -11,4 +11,4 @@ /docs/customize-installation /docs/{{ $latest }}/customize-installation /docs/faq /docs/{{ $latest }}/faq /docs/csi /docs/{{ $latest }}/csi -/docs/restic /docs/{{ $latest }}/restic +/docs/file-system-backup /docs/{{ $latest }}/file-system-backup From 818953815d5174890362f83a108630a5e4f48239 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 350/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index b243ce1d91..6e5ae931a6 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -14,9 +14,11 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Follow us on Twitter at [@projectvelero](https://twitter.com/projectvelero) * Join our Kubernetes Slack channel and talk to over 800 other community members: [#velero](https://kubernetes.slack.com/messages/velero) * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. -* Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09): - * 1st and 3rd Tuesday at 12PM ET / 9AM PT ([Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=12pm)) - * 2nd and 4th Wednesday at 8am China Standard Time / Tuesday 7pm EST (8pm EDT) / Tuesday 4pm PST (5pm PDT) ([Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) +* Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) +Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone +> * Beijing/US friendly - we start at 8am Beijing Time / 8pm ET / 5pm PT / 2am CEST - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) +> * US/Europe friendly - we start at 4pm CEST / 10 am ET / 7 am PT / 10pm Beijing Time - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) + * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 4054043c94b7fe65abf8d4f0e8a94ea0465169ee Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 351/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index 6e5ae931a6..589fc1e347 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -16,9 +16,8 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone -> * Beijing/US friendly - we start at 8am Beijing Time / 8pm ET / 5pm PT / 2am CEST - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) -> * US/Europe friendly - we start at 4pm CEST / 10 am ET / 7 am PT / 10pm Beijing Time - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) - - * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) - * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) - * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) + * Beijing/US friendly - we start at 8am Beijing Time / 8pm ET / 5pm PT / 2am CEST - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) + * US/Europe friendly - we start at 4pm CEST / 10 am ET / 7 am PT / 10pm Beijing Time - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) +* Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) +* See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) +* Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 8c6228adb8a23c52f117141940fd9cc01996fdd6 Mon Sep 17 00:00:00 2001 From: Pratik Raj Date: Sat, 29 Oct 2022 01:33:19 +0530 Subject: [PATCH 352/366] feat: dependabot workflow automation for updating dependency Signed-off-by: Pratik Raj --- .github/dependabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d608a82442 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + # Dependencies listed in go.mod + - package-ecosystem: "gomod" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + + # Dependencies listed in .github/workflows/*.yml + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 3571339fd64553dd3bad2eeede0d0d747ee80fc1 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 28 Oct 2022 16:36:27 +0800 Subject: [PATCH 353/366] issue fix 5505 Signed-off-by: Lyndon-Li --- changelogs/unreleased/5512-lyndon | 1 + pkg/repository/config/azure.go | 24 ++++++--- pkg/repository/config/azure_test.go | 46 +++++++++++++++++ pkg/repository/provider/unified_repo.go | 7 ++- pkg/repository/provider/unified_repo_test.go | 54 ++++++++++++++------ 5 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/5512-lyndon diff --git a/changelogs/unreleased/5512-lyndon b/changelogs/unreleased/5512-lyndon new file mode 100644 index 0000000000..2712304b38 --- /dev/null +++ b/changelogs/unreleased/5512-lyndon @@ -0,0 +1 @@ +Fix issue 5505: the pod volume backups/restores except the first one fail under the kopia path if "AZURE_CLOUD_NAME" is specified diff --git a/pkg/repository/config/azure.go b/pkg/repository/config/azure.go index c90c4f0ac7..7716c73c7e 100644 --- a/pkg/repository/config/azure.go +++ b/pkg/repository/config/azure.go @@ -18,6 +18,7 @@ package config import ( "context" + "fmt" "os" "strings" @@ -211,13 +212,15 @@ func getRequiredValues(getValue func(string) string, keys ...string) error { } // GetAzureStorageDomain gets the Azure storage domain required by a Azure blob connection, -// if the provided config doean't have the value, get it from system's environment variables -func GetAzureStorageDomain(config map[string]string) string { - if domain, exist := config[storageDomainConfigKey]; exist { - return domain - } else { - return os.Getenv(cloudNameEnvVar) +// if the provided credential file doesn't have the value, get it from system's environment variables +func GetAzureStorageDomain(config map[string]string) (string, error) { + credentialsFile := selectCredentialsFile(config) + + if err := loadCredentialsIntoEnv(credentialsFile); err != nil { + return "", err } + + return getStorageDomainFromCloudName(os.Getenv(cloudNameEnvVar)) } func GetAzureCredentials(config map[string]string) (string, string, error) { @@ -228,3 +231,12 @@ func GetAzureCredentials(config map[string]string) (string, string, error) { return config[storageAccountConfigKey], storageAccountKey, nil } + +func getStorageDomainFromCloudName(cloudName string) (string, error) { + env, err := parseAzureEnvironment(cloudName) + if err != nil { + return "", errors.Wrapf(err, "unable to parse azure env from cloud name %s", cloudName) + } + + return fmt.Sprintf("blob.%s", env.StorageEndpointSuffix), nil +} diff --git a/pkg/repository/config/azure_test.go b/pkg/repository/config/azure_test.go index d20ac2e28b..f32f87ce01 100644 --- a/pkg/repository/config/azure_test.go +++ b/pkg/repository/config/azure_test.go @@ -20,6 +20,7 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -86,3 +87,48 @@ func TestSelectCredentialsFile(t *testing.T) { }) } } + +func TestGetStorageDomainFromCloudName(t *testing.T) { + testCases := []struct { + name string + cloudName string + expected string + expectedErr string + }{ + { + name: "get azure env fail", + cloudName: "fake-cloud", + expectedErr: "unable to parse azure env from cloud name fake-cloud: autorest/azure: There is no cloud environment matching the name \"FAKE-CLOUD\"", + }, + { + name: "cloud name is empty", + cloudName: "", + expected: "blob.core.windows.net", + }, + { + name: "azure public cloud", + cloudName: "AzurePublicCloud", + expected: "blob.core.windows.net", + }, + { + + name: "azure China cloud", + cloudName: "AzureChinaCloud", + expected: "blob.core.chinacloudapi.cn", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + domain, err := getStorageDomainFromCloudName(tc.cloudName) + + require.Equal(t, tc.expected, domain) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + assert.Empty(t, domain) + } + }) + } +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 30d1c97aaa..47d8feee54 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -466,7 +466,12 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo result[udmrepo.StoreOptionS3DisableTlsVerify] = config["insecureSkipTLSVerify"] result[udmrepo.StoreOptionS3DisableTls] = strconv.FormatBool(disableTls) } else if backendType == repoconfig.AzureBackend { - result[udmrepo.StoreOptionAzureDomain] = getAzureStorageDomain(config) + domain, err := getAzureStorageDomain(config) + if err != nil { + return map[string]string{}, errors.Wrapf(err, "error to get azure storage domain") + } + + result[udmrepo.StoreOptionAzureDomain] = domain } result[udmrepo.StoreOptionOssBucket] = bucket diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index c8121058c6..e33059c2b7 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -235,7 +235,7 @@ func TestGetStorageVariables(t *testing.T) { repoName string repoBackend string getS3BucketRegion func(string) (string, error) - getAzureStorageDomain func(map[string]string) string + getAzureStorageDomain func(map[string]string) (string, error) expected map[string]string expectedErr string }{ @@ -366,17 +366,42 @@ func TestGetStorageVariables(t *testing.T) { "skipTLSVerify": "false", }, }, + { + name: "azure, getAzureStorageDomain fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "fspath": "", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + }, + }, + }, + }, + getAzureStorageDomain: func(config map[string]string) (string, error) { + return "", errors.New("fake error") + }, + repoBackend: "fake-repo-type", + expected: map[string]string{}, + expectedErr: "error to get azure storage domain: fake error", + }, { name: "azure, ObjectStorage section exists in BSL", backupLocation: velerov1api.BackupStorageLocation{ Spec: velerov1api.BackupStorageLocationSpec{ Provider: "velero.io/azure", Config: map[string]string{ - "bucket": "fake-bucket-config", - "prefix": "fake-prefix-config", - "region": "fake-region", - "fspath": "", - "storageDomain": "fake-domain", + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "fspath": "", }, StorageType: velerov1api.StorageType{ ObjectStorage: &velerov1api.ObjectStorageLocation{ @@ -386,8 +411,8 @@ func TestGetStorageVariables(t *testing.T) { }, }, }, - getAzureStorageDomain: func(config map[string]string) string { - return config["storageDomain"] + getAzureStorageDomain: func(config map[string]string) (string, error) { + return "fake-domain", nil }, repoBackend: "fake-repo-type", expected: map[string]string{ @@ -404,18 +429,17 @@ func TestGetStorageVariables(t *testing.T) { Spec: velerov1api.BackupStorageLocationSpec{ Provider: "velero.io/azure", Config: map[string]string{ - "bucket": "fake-bucket", - "prefix": "fake-prefix", - "region": "fake-region", - "fspath": "", - "storageDomain": "fake-domain", + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region", + "fspath": "", }, }, }, repoName: "//fake-name//", repoBackend: "fake-repo-type", - getAzureStorageDomain: func(config map[string]string) string { - return config["storageDomain"] + getAzureStorageDomain: func(config map[string]string) (string, error) { + return "fake-domain", nil }, expected: map[string]string{ "bucket": "fake-bucket", From 41fc6412981a9e0984bb5c91f0aa85fdbc9cd7b2 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 31 Oct 2022 16:25:50 +0800 Subject: [PATCH 354/366] Add credential file store in the backup deletion controller. Signed-off-by: Xun Jiang --- changelogs/unreleased/5521-blackpiglet | 1 + go.mod | 2 +- pkg/cmd/server/server.go | 1 + pkg/controller/backup_deletion_controller.go | 21 ++++++++++++++----- .../backup_deletion_controller_test.go | 1 + 5 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/5521-blackpiglet diff --git a/changelogs/unreleased/5521-blackpiglet b/changelogs/unreleased/5521-blackpiglet new file mode 100644 index 0000000000..93c0b59412 --- /dev/null +++ b/changelogs/unreleased/5521-blackpiglet @@ -0,0 +1 @@ +Add credential store in backup deletion controller to support VSL credential. \ No newline at end of file diff --git a/go.mod b/go.mod index e91150f265..c69d4c2edd 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( k8s.io/client-go v0.24.1 k8s.io/klog/v2 v2.60.1 k8s.io/kube-aggregator v0.19.12 + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 sigs.k8s.io/controller-runtime v0.12.1 sigs.k8s.io/yaml v1.3.0 ) @@ -147,7 +148,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/component-base v0.24.1 // indirect k8s.io/kube-openapi v0.0.0-20220614142933-1062c7ade5f8 // indirect - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 0a794a706f..8e24affcad 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -805,6 +805,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.discoveryHelper, newPluginManager, backupStoreGetter, + s.credentialFileStore, ).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.BackupDeletion) } diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 76c39c8c84..1c75dad6e7 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -28,10 +28,11 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/clock" kubeerrs "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/utils/clock" ctrl "sigs.k8s.io/controller-runtime" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/delete" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/discovery" @@ -43,6 +44,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" + "github.com/vmware-tanzu/velero/pkg/volume" "sigs.k8s.io/controller-runtime/pkg/client" @@ -64,6 +66,7 @@ type backupDeletionReconciler struct { discoveryHelper discovery.Helper newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupStoreGetter persistence.ObjectBackupStoreGetter + credentialStore credentials.FileStore } // NewBackupDeletionReconciler creates a new backup deletion reconciler. @@ -76,6 +79,7 @@ func NewBackupDeletionReconciler( helper discovery.Helper, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupStoreGetter persistence.ObjectBackupStoreGetter, + credentialStore credentials.FileStore, ) *backupDeletionReconciler { return &backupDeletionReconciler{ Client: client, @@ -87,6 +91,7 @@ func NewBackupDeletionReconciler( discoveryHelper: helper, newPluginManager: newPluginManager, backupStoreGetter: backupStoreGetter, + credentialStore: credentialStore, } } @@ -289,7 +294,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque volumeSnapshotter, ok := volumeSnapshotters[snapshot.Spec.Location] if !ok { - if volumeSnapshotter, err = volumeSnapshottersForVSL(ctx, backup.Namespace, snapshot.Spec.Location, r.Client, pluginManager); err != nil { + if volumeSnapshotter, err = r.volumeSnapshottersForVSL(ctx, backup.Namespace, snapshot.Spec.Location, pluginManager); err != nil { errs = append(errs, err.Error()) continue } @@ -387,19 +392,25 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } -func volumeSnapshottersForVSL( +func (r *backupDeletionReconciler) volumeSnapshottersForVSL( ctx context.Context, namespace, vslName string, - client client.Client, pluginManager clientmgmt.Manager, ) (vsv1.VolumeSnapshotter, error) { vsl := &velerov1api.VolumeSnapshotLocation{} - if err := client.Get(ctx, types.NamespacedName{ + if err := r.Client.Get(ctx, types.NamespacedName{ Namespace: namespace, Name: vslName, }, vsl); err != nil { return nil, errors.Wrapf(err, "error getting volume snapshot location %s", vslName) } + + // add credential to config + err := volume.UpdateVolumeSnapshotLocationWithCredentialConfig(vsl, r.credentialStore, r.logger) + if err != nil { + return nil, errors.WithStack(err) + } + volumeSnapshotter, err := pluginManager.GetVolumeSnapshotter(vsl.Spec.Provider) if err != nil { return nil, errors.Wrapf(err, "error getting volume snapshotter for provider %s", vsl.Spec.Provider) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 62be34b0d2..c193f7819f 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -96,6 +96,7 @@ func setupBackupDeletionControllerTest(t *testing.T, req *velerov1api.DeleteBack nil, // discovery helper func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, NewFakeSingleObjectBackupStoreGetter(backupStore), + velerotest.NewFakeCredentialsFileStore("", nil), ), req: ctrl.Request{NamespacedName: types.NamespacedName{Namespace: req.Namespace, Name: req.Name}}, } From 734d6ca336b8cb91b50c2b6a9b13590adc478827 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 355/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index 589fc1e347..e7e0d0396d 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -16,8 +16,8 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone - * Beijing/US friendly - we start at 8am Beijing Time / 8pm ET / 5pm PT / 2am CEST - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) - * US/Europe friendly - we start at 4pm CEST / 10 am ET / 7 am PT / 10pm Beijing Time - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) + * Beijing/US friendly - we start at 8am Beijing Time(CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 1am CET(2am CEST) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) + * US/Europe friendly - we start at 10pm Beijing Time (CST) / 10am EDT(9am EST) / 7am PDT(6am PST) / 3pm CET(4pm CEST) - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 058c44fe10d7e05c2728adc326001c0597124832 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 356/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index e7e0d0396d..e0ec1032da 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -16,8 +16,13 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone +<<<<<<< HEAD * Beijing/US friendly - we start at 8am Beijing Time(CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 1am CET(2am CEST) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) * US/Europe friendly - we start at 10pm Beijing Time (CST) / 10am EDT(9am EST) / 7am PDT(6am PST) / 3pm CET(4pm CEST) - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) +======= + * Beijing/US friendly - we start at 8am Beijing Time(bind to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) + * US/Europe friendly - we start at 10am ET(bind to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) +>>>>>>> Update Community meetings times * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 52a49d19452be5c7e351d22a00597f59ac39340d Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 1 Nov 2022 07:02:06 +0000 Subject: [PATCH 357/366] fix backup failure with self-signed certification Signed-off-by: Ming --- changelogs/unreleased/5526-qiuming-best | 1 + pkg/uploader/provider/restic.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5526-qiuming-best diff --git a/changelogs/unreleased/5526-qiuming-best b/changelogs/unreleased/5526-qiuming-best new file mode 100644 index 0000000000..772f6d4d90 --- /dev/null +++ b/changelogs/unreleased/5526-qiuming-best @@ -0,0 +1 @@ +fix restic backup failure with self-signed certification backend storage diff --git a/pkg/uploader/provider/restic.go b/pkg/uploader/provider/restic.go index 2715b9e9d2..00fe420571 100644 --- a/pkg/uploader/provider/restic.go +++ b/pkg/uploader/provider/restic.go @@ -147,7 +147,9 @@ func (rp *resticProvider) RunBackup( snapshotIdCmd := restic.GetSnapshotCommand(rp.repoIdentifier, rp.credentialsFile, tags) snapshotIdCmd.Env = rp.cmdEnv snapshotIdCmd.CACertFile = rp.caCertFile - + if len(rp.extraFlags) != 0 { + snapshotIdCmd.ExtraFlags = append(snapshotIdCmd.ExtraFlags, rp.extraFlags...) + } snapshotID, err := restic.GetSnapshotID(snapshotIdCmd) if err != nil { return "", false, errors.WithStack(fmt.Errorf("error getting snapshot id with error: %v", err)) From 63788aaf8f570b5e59aa122e8d06a9c15fbeb996 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 358/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index e0ec1032da..e917535f31 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -16,13 +16,8 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone -<<<<<<< HEAD - * Beijing/US friendly - we start at 8am Beijing Time(CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 1am CET(2am CEST) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) - * US/Europe friendly - we start at 10pm Beijing Time (CST) / 10am EDT(9am EST) / 7am PDT(6am PST) / 3pm CET(4pm CEST) - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) -======= - * Beijing/US friendly - we start at 8am Beijing Time(bind to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) - * US/Europe friendly - we start at 10am ET(bind to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) ->>>>>>> Update Community meetings times + * Beijing/US friendly - we start at 8am Beijing Time(bound to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) + * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From e6ba77484194d9c66e63daea144ca2d5d1d29947 Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Tue, 1 Nov 2022 18:11:21 +0800 Subject: [PATCH 359/366] remove restic in docs (#5499) Signed-off-by: Lyndon-Li --- changelogs/unreleased/5499-lyndon | 1 + site/content/docs/main/api-types/backup.md | 4 +- site/content/docs/main/api-types/schedule.md | 4 +- site/content/docs/main/basic-install.md | 2 +- site/content/docs/main/build-from-source.md | 2 +- site/content/docs/main/code-standards.md | 4 +- .../docs/main/contributions/ibm-config.md | 6 +- site/content/docs/main/contributions/minio.md | 6 +- .../docs/main/contributions/tencent-config.md | 12 ++-- .../docs/main/customize-installation.md | 56 +++++++++---------- site/content/docs/main/locations.md | 8 +-- site/content/docs/main/manual-testing.md | 8 +-- site/content/docs/main/migration-case.md | 4 +- site/content/docs/main/on-premises.md | 14 ++--- site/content/docs/main/restore-hooks.md | 2 +- site/content/docs/main/restore-reference.md | 12 ++-- .../docs/main/self-signed-certificates.md | 6 +- site/content/docs/main/supported-providers.md | 4 +- site/content/docs/main/tilt.md | 10 ++-- site/content/docs/main/troubleshooting.md | 6 +- site/content/docs/main/velero-install.md | 14 ++--- site/content/resources/_index.md | 8 +-- 22 files changed, 94 insertions(+), 99 deletions(-) create mode 100644 changelogs/unreleased/5499-lyndon diff --git a/changelogs/unreleased/5499-lyndon b/changelogs/unreleased/5499-lyndon new file mode 100644 index 0000000000..613f6aaeea --- /dev/null +++ b/changelogs/unreleased/5499-lyndon @@ -0,0 +1 @@ +After Pod Volume Backup/Restore refactor, remove all the unreasonable appearance of "restic" word from documents diff --git a/site/content/docs/main/api-types/backup.md b/site/content/docs/main/api-types/backup.md index 23805b21d7..1c1d0b9aad 100644 --- a/site/content/docs/main/api-types/backup.md +++ b/site/content/docs/main/api-types/backup.md @@ -84,8 +84,8 @@ spec: # a default value of 30 days will be used. The default can be configured on the velero server # by passing the flag --default-backup-ttl. ttl: 24h0m0s - # Whether restic should be used to take a backup of all pod volumes by default. - defaultVolumesToRestic: true + # whether pod volume file system backup should be used for all volumes by default. + defaultVolumesToFsBackup: true # Actions to perform at different times during a backup. The only hook supported is # executing a command in a container in a pod using the pod exec API. Optional. hooks: diff --git a/site/content/docs/main/api-types/schedule.md b/site/content/docs/main/api-types/schedule.md index 31130f43ae..eb3aa271ba 100644 --- a/site/content/docs/main/api-types/schedule.md +++ b/site/content/docs/main/api-types/schedule.md @@ -82,8 +82,8 @@ spec: # a default value of 30 days will be used. The default can be configured on the velero server # by passing the flag --default-backup-ttl. ttl: 24h0m0s - # Whether restic should be used to take a backup of all pod volumes by default. - defaultVolumesToRestic: true + # whether pod volume file system backup should be used for all volumes by default. + defaultVolumesToFsBackup: true # The labels you want on backup objects, created from this schedule (instead of copying the labels you have on schedule object itself). # When this field is set, the labels from the Schedule resource are not copied to the Backup resource. metadata: diff --git a/site/content/docs/main/basic-install.md b/site/content/docs/main/basic-install.md index 080b27b2f0..bb8bd7f383 100644 --- a/site/content/docs/main/basic-install.md +++ b/site/content/docs/main/basic-install.md @@ -17,7 +17,7 @@ Velero supports storage providers for both cloud-provider environments and on-pr ### Velero on Windows -Velero does not officially support Windows. In testing, the Velero team was able to backup stateless Windows applications only. The restic integration and backups of stateful applications or PersistentVolumes were not supported. +Velero does not officially support Windows. In testing, the Velero team was able to backup stateless Windows applications only. The File System Backup and backups of stateful applications or PersistentVolumes were not supported. If you want to perform your own testing of Velero on Windows, you must deploy Velero as a Windows container. Velero does not provide official Windows images, but its possible for you to build your own Velero Windows container image to use. Note that you must build this image on a Windows node. diff --git a/site/content/docs/main/build-from-source.md b/site/content/docs/main/build-from-source.md index df9f738cf2..083cdc202c 100644 --- a/site/content/docs/main/build-from-source.md +++ b/site/content/docs/main/build-from-source.md @@ -96,7 +96,7 @@ Optionally, set the `$VERSION` environment variable to change the image tag or ` ```bash make container ``` -_Note: To build build container images for both `velero` and `velero-restic-restore-helper`, run: `make all-containers`_ +_Note: To build build container images for both `velero` and `velero-restore-helper`, run: `make all-containers`_ ### Publishing container images to a registry diff --git a/site/content/docs/main/code-standards.md b/site/content/docs/main/code-standards.md index a6dfa12c64..c12317981a 100644 --- a/site/content/docs/main/code-standards.md +++ b/site/content/docs/main/code-standards.md @@ -70,13 +70,13 @@ Example: We use a package to generate mocks for our interfaces. -Example: if you want to change this mock: https://github.com/vmware-tanzu/velero/blob/main/pkg/restic/mocks/restorer.go +Example: if you want to change this mock: https://github.com/vmware-tanzu/velero/blob/main/pkg/podvolume/mocks/restorer.go Run: ```bash go get github.com/vektra/mockery/.../ -cd pkg/restic +cd pkg/podvolume mockery -name=Restorer ``` diff --git a/site/content/docs/main/contributions/ibm-config.md b/site/content/docs/main/contributions/ibm-config.md index b551bc1a73..332d0a5701 100644 --- a/site/content/docs/main/contributions/ibm-config.md +++ b/site/content/docs/main/contributions/ibm-config.md @@ -71,9 +71,9 @@ velero install \ Velero does not have a volume snapshot plugin for IBM Cloud, so creating volume snapshots is disabled. -Additionally, you can specify `--use-restic` to enable [restic support][16], and `--wait` to wait for the deployment to be ready. +Additionally, you can specify `--use-node-agent` to enable [File System Backup][16], and `--wait` to wait for the deployment to be ready. -(Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/restic pods. +(Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/node-agent pods. Once the installation is complete, remove the default `VolumeSnapshotLocation` that was created by `velero install`, since it's specific to AWS and won't work for IBM Cloud: @@ -98,4 +98,4 @@ Uncomment `storageClassName: ` and replace with your `S [5]: https://cloud.ibm.com/docs/containers/container_index.html#container_index [14]: http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html [15]: ../customize-installation.md#customize-resource-requests-and-limits -[16]: ../restic.md +[16]: ../file-system-backup.md diff --git a/site/content/docs/main/contributions/minio.md b/site/content/docs/main/contributions/minio.md index 3c683473b6..b04354a1f6 100644 --- a/site/content/docs/main/contributions/minio.md +++ b/site/content/docs/main/contributions/minio.md @@ -16,7 +16,7 @@ If you encounter issues with installing or configuring, see [Debugging Installat ## Prerequisites -* Access to a Kubernetes cluster, version 1.7 or later. **Note:** restic support requires Kubernetes version 1.10 or later, or an earlier version with the mount propagation feature enabled. Restic support is not required for this example, but may be of interest later. See [Restic Integration][17]. +* Access to a Kubernetes cluster, version 1.7 or later. **Note:** File System Backup support requires Kubernetes version 1.10 or later, or an earlier version with the mount propagation feature enabled. File System Backup support is not required for this example, but may be of interest later. See [File System Backup][17]. * A DNS server on the cluster * `kubectl` installed * Sufficient disk space to store backups in Minio. You will need sufficient disk space available to handle any @@ -83,7 +83,7 @@ These instructions start the Velero server and a Minio instance that is accessib This example assumes that it is running within a local cluster without a volume provider capable of snapshots, so no `VolumeSnapshotLocation` is created (`--use-volume-snapshots=false`). You may need to update AWS plugin version to one that is [compatible](https://github.com/vmware-tanzu/velero-plugin-for-aws#compatibility) with the version of Velero you are installing. - Additionally, you can specify `--use-restic` to enable restic support, and `--wait` to wait for the deployment to be ready. + Additionally, you can specify `--use-node-agent` to enable File System Backup support, and `--wait` to wait for the deployment to be ready. This example also assumes you have named your Minio bucket "velero". @@ -289,7 +289,7 @@ In this case: [1]: #expose-minio-with-service-of-type-nodeport [3]: ../customize-installation.md -[17]: ../restic.md +[17]: ../file-system-backup.md [18]: ../debugging-restores.md [26]: https://github.com/vmware-tanzu/velero/releases [30]: https://godoc.org/github.com/robfig/cron diff --git a/site/content/docs/main/contributions/tencent-config.md b/site/content/docs/main/contributions/tencent-config.md index 11b0762c0a..592808c2dd 100644 --- a/site/content/docs/main/contributions/tencent-config.md +++ b/site/content/docs/main/contributions/tencent-config.md @@ -39,13 +39,13 @@ aws_secret_access_key= You need to install the Velero CLI first, see [Install the CLI](https://velero.io/docs/v1.5/basic-install/#install-the-cli) for how to install. -Follow the Velero installation command below to create velero and restic workloads and other necessary resource objects. +Follow the Velero installation command below to create velero and node-agent workloads and other necessary resource objects. ```bash velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.1.0 --bucket \ --secret-file ./credentials-velero \ ---use-restic \ ---default-volumes-to-restic \ +--use-node-agent \ +--default-volumes-to-fs-backup \ --backup-location-config \ region=ap-guangzhou,s3ForcePathStyle="true",s3Url=https://cos.ap-guangzhou.myqcloud.com ``` @@ -60,9 +60,9 @@ Description of the parameters: - `--secret-file`: Access tencent cloud COS access credential file for the "credentials-velero" credential file created above. -- `--use-restic`: Back up and restore persistent volume data using the open source free backup tool [restic](https://github.com/restic/restic). However, 'hostPath' volumes are not supported, see the [restic limit](https://velero.io/docs/v1.5/restic/#limitations) for details), an integration that complements Velero's backup capabilities and is recommended to be turned on. +- `--use-node-agent`: Enable Velero node-agent daemonset. At present, Velero File System Backup requires this daemonset, so if you are using File System Backup, it needs to be turned on. For the usage and limitation of File System Backup, See [File System Backup](../file-system-backup.md). -- `--default-volumes-to-restic`: Enable the use of Restic to back up all Pod volumes, provided that the `--use-restic`parameter needs to be turned on. +- `--default-volumes-to-fs-backup`: Enable the use of File System Backup to back up all Pod volumes, provided that the `--use-node-agent`parameter needs to be turned on. - `--backup-location-config`: Back up the bucket access-related configuration: @@ -78,7 +78,7 @@ After executing the installation commands above, the installation process looks {{< figure src="/docs/main/contributions/img-for-tencent/9015313121ed7987558c88081b052574.png" width="100%">}} -After the installation command is complete, wait for the velero and restic workloads to be ready to see if the configured storage location is available. +After the installation command is complete, wait for the velero and node-agent workloads to be ready to see if the configured storage location is available. Executing the 'velero backup-location get' command to view the storage location status and display "Available" indicates that access to Tencent Cloud COS is OK, as shown in the following image: diff --git a/site/content/docs/main/customize-installation.md b/site/content/docs/main/customize-installation.md index b90300f826..786d74dd68 100644 --- a/site/content/docs/main/customize-installation.md +++ b/site/content/docs/main/customize-installation.md @@ -17,17 +17,17 @@ By default, `velero install` expects a credentials file for your `velero` IAM ac If you are using an alternate identity mechanism, such as kube2iam/kiam on AWS, Workload Identity on GKE, etc., that does not require a credentials file, you can specify the `--no-secret` flag instead of `--secret-file`. -## Enable restic integration +## Enable file system backup -By default, `velero install` does not install Velero's [restic integration][3]. To enable it, specify the `--use-restic` flag. +By default, `velero install` does not install Velero's [File System Backup][3]. To enable it, specify the `--use-node-agent` flag. -If you've already run `velero install` without the `--use-restic` flag, you can run the same command again, including the `--use-restic` flag, to add the restic integration to your existing install. +If you've already run `velero install` without the `--use-node-agent` flag, you can run the same command again, including the `--use-node-agent` flag, to add the file system backup to your existing install. -## Default Pod Volume backup to restic +## Default Pod Volume backup to file system backup -By default, `velero install` does not enable the use of restic to take backups of all pod volumes. You must apply an [annotation](restic.md/#using-opt-in-pod-volume-backup) to every pod which contains volumes for Velero to use restic for the backup. +By default, `velero install` does not enable the use of File System Backup (FSB) to take backups of all pod volumes. You must apply an [annotation](file-system-backup.md/#using-opt-in-pod-volume-backup) to every pod which contains volumes for Velero to use FSB for the backup. -If you are planning to only use restic for volume backups, you can run the `velero install` command with the `--default-volumes-to-restic` flag. This will default all pod volumes backups to use restic without having to apply annotations to pods. Note that when this flag is set during install, Velero will always try to use restic to perform the backup, even want an individual backup to use volume snapshots, by setting the `--snapshot-volumes` flag in the `backup create` command. Alternatively, you can set the `--default-volumes-to-restic` on an individual backup to to make sure Velero uses Restic for each volume being backed up. +If you are planning to only use FSB for volume backups, you can run the `velero install` command with the `--default-volumes-to-fs-backup` flag. This will default all pod volumes backups to use FSB without having to apply annotations to pods. Note that when this flag is set during install, Velero will always try to use FSB to perform the backup, even want an individual backup to use volume snapshots, by setting the `--snapshot-volumes` flag in the `backup create` command. Alternatively, you can set the `--default-volumes-to-fs-backup` on an individual backup to to make sure Velero uses FSB for each volume being backed up. ## Enable features @@ -43,15 +43,15 @@ velero install --features=EnableCSI Another example is enabling the support of multiple API group versions, as documented at [- -features=EnableAPIGroupVersions](enable-api-group-versions-feature.md). -Feature flags, passed to `velero install` will be passed to the Velero deployment and also to the `restic` daemon set, if `--use-restic` flag is used. +Feature flags, passed to `velero install` will be passed to the Velero deployment and also to the `node-agent` daemon set, if `--use-node-agent` flag is used. Similarly, features may be disabled by removing the corresponding feature flags from the `--features` flag. -Enabling and disabling feature flags will require modifying the Velero deployment and also the restic daemonset. This may be done from the CLI by uninstalling and re-installing Velero, or by editing the `deploy/velero` and `daemonset/restic` resources in-cluster. +Enabling and disabling feature flags will require modifying the Velero deployment and also the node-agent daemonset. This may be done from the CLI by uninstalling and re-installing Velero, or by editing the `deploy/velero` and `daemonset/node-agent` resources in-cluster. ```bash $ kubectl -n velero edit deploy/velero -$ kubectl -n velero edit daemonset/restic +$ kubectl -n velero edit daemonset/node-agent ``` ### Enable client side features @@ -87,10 +87,10 @@ the config file setting. ## Customize resource requests and limits -At installation, Velero sets default resource requests and limits for the Velero pod and the restic pod, if you using the [restic integration](/docs/main/restic/). +At installation, Velero sets default resource requests and limits for the Velero pod and the node-agent pod, if you using the [File System Backup][3]. {{< table caption="Velero Customize resource requests and limits defaults" >}} -|Setting|Velero pod defaults|restic pod defaults| +|Setting|Velero pod defaults|node-agent pod defaults| |--- |--- |--- | |CPU request|500m|500m| |Memory requests|128Mi|512Mi| @@ -98,9 +98,9 @@ At installation, Velero sets default resource requests and limits for the Velero |Memory limit|512Mi|1024Mi| {{< /table >}} -Depending on the cluster resources, especially if you are using Restic, you may need to increase these defaults. Through testing, the Velero maintainers have found these defaults work well when backing up and restoring 1000 or less resources and total size of files is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. +Depending on the cluster resources, you may need to increase these defaults. Through testing, the Velero maintainers have found these defaults work well when backing up and restoring 1000 or less resources and total size of files is 100GB or below. If the resources you are planning to backup or restore exceed this, you will need to increase the CPU or memory resources available to Velero. In general, the Velero maintainer's testing found that backup operations needed more CPU & memory resources but were less time-consuming than restore operations, when comparing backing up and restoring the same amount of data. The exact CPU and memory limits you will need depend on the scale of the files and directories of your resources and your hardware. It's recommended that you perform your own testing to find the best resource limits for your clusters and resources. -Due to a [known Restic issue](https://github.com/restic/restic/issues/2446), the Restic pod will consume large amounts of memory, especially if you are backing up millions of tiny files and directories. If you are planning to use Restic to backup 100GB of data or more, you will need to increase the resource limits to make sure backups complete successfully. +You may need to increase the resource limits if you are using File System Backup, see the details in [File System Backup][3]. ### Install with custom resource requests and limits @@ -112,17 +112,17 @@ velero install \ --velero-pod-mem-request \ --velero-pod-cpu-limit \ --velero-pod-mem-limit \ - [--use-restic] \ - [--default-volumes-to-restic] \ - [--restic-pod-cpu-request ] \ - [--restic-pod-mem-request ] \ - [--restic-pod-cpu-limit ] \ - [--restic-pod-mem-limit ] + [--use-node-agent] \ + [--default-volumes-to-fs-backup] \ + [--node-agent-pod-cpu-request ] \ + [--node-agent-pod-mem-request ] \ + [--node-agent-pod-cpu-limit ] \ + [--node-agent-pod-mem-limit ] ``` ### Update resource requests and limits after install -After installation you can adjust the resource requests and limits in the Velero Deployment spec or restic DeamonSet spec, if you are using the restic integration. +After installation you can adjust the resource requests and limits in the Velero Deployment spec or node-agent DeamonSet spec, if you are using the File System Backup. **Velero pod** @@ -133,16 +133,16 @@ kubectl patch deployment velero -n velero --patch \ '{"spec":{"template":{"spec":{"containers":[{"name": "velero", "resources": {"limits":{"cpu": "1", "memory": "512Mi"}, "requests": {"cpu": "1", "memory": "128Mi"}}}]}}}}' ``` -**restic pod** +**node-agent pod** -Update the `spec.template.spec.containers.resources.limits` and `spec.template.spec.containers.resources.requests` values in the restic DeamonSet spec. +Update the `spec.template.spec.containers.resources.limits` and `spec.template.spec.containers.resources.requests` values in the node-agent DeamonSet spec. ```bash -kubectl patch daemonset restic -n velero --patch \ -'{"spec":{"template":{"spec":{"containers":[{"name": "restic", "resources": {"limits":{"cpu": "1", "memory": "1024Mi"}, "requests": {"cpu": "1", "memory": "512Mi"}}}]}}}}' +kubectl patch daemonset node-agent -n velero --patch \ +'{"spec":{"template":{"spec":{"containers":[{"name": "node-agent", "resources": {"limits":{"cpu": "1", "memory": "1024Mi"}, "requests": {"cpu": "1", "memory": "512Mi"}}}]}}}}' ``` -Additionally, you may want to update the the default Velero restic pod operation timeout (default 240 minutes) to allow larger backups more time to complete. You can adjust this timeout by adding the `- --restic-timeout` argument to the Velero Deployment spec. +Additionally, you may want to update the the default File System Backup operation timeout (default 240 minutes) to allow larger backups more time to complete. You can adjust this timeout by adding the `- --fs-backup-timeout` argument to the Velero Deployment spec. **NOTE:** Changes made to this timeout value will revert back to the default value if you re-run the Velero install command. @@ -152,7 +152,7 @@ Additionally, you may want to update the the default Velero restic pod operation kubectl edit deploy velero -n velero ``` -1. Add `- --restic-timeout` to `spec.template.spec.containers`. +1. Add `- --fs-backup-timeout` to `spec.template.spec.containers`. ```yaml spec: @@ -160,7 +160,7 @@ Additionally, you may want to update the the default Velero restic pod operation spec: containers: - args: - - --restic-timeout=240m + - --fs-backup-timeout=240m ``` ## Configure more than one storage location for backups or volume snapshots @@ -380,7 +380,7 @@ If you get an error like `complete:13: command not found: compdef`, then add the [1]: https://github.com/vmware-tanzu/velero/releases/latest [2]: namespace.md -[3]: restic.md +[3]: file-system-backup.md [4]: on-premises.md [6]: velero-install.md#usage [7]: https://github.com/vmware-tanzu/velero/issues/2077 diff --git a/site/content/docs/main/locations.md b/site/content/docs/main/locations.md index fe74391f9e..1b4cc15d83 100644 --- a/site/content/docs/main/locations.md +++ b/site/content/docs/main/locations.md @@ -37,13 +37,13 @@ This configuration design enables a number of different use cases, including: - Cross-provider snapshots are not supported. If you have a cluster with more than one type of volume, like EBS and Portworx, but you only have a `VolumeSnapshotLocation` configured for EBS, then Velero will **only** snapshot the EBS volumes. -- Restic data is stored under a prefix/subdirectory of the main Velero bucket, and will go into the bucket corresponding to the `BackupStorageLocation` selected by the user at backup creation time. +- File System Backup data is stored under a prefix/subdirectory of the main Velero bucket, and will go into the bucket corresponding to the `BackupStorageLocation` selected by the user at backup creation time. -- Velero's backups are split into 2 pieces - the metadata stored in object storage, and snapshots/backups of the persistent volume data. Right now, Velero *itself* does not encrypt either of them, instead it relies on the native mechanisms in the object and snapshot systems. A special case is restic, which backs up the persistent volume data at the filesystem level and send it to Velero's object storage. +- Velero's backups are split into 2 pieces - the metadata stored in object storage, and snapshots/backups of the persistent volume data. Right now, Velero *itself* does not encrypt either of them, instead it relies on the native mechanisms in the object and snapshot systems. A special case is File System Backup, which backs up the persistent volume data at the filesystem level and send it to Velero's object storage. -- Velero's compression for object metadata is limited, using Golang's tar implementation. In most instances, Kubernetes objects are limited to 1.5MB in size, but many don't approach that, meaning that compression may not be necessary. Note that restic has not yet implemented compression, but does have de-deduplication capabilities. +- Velero's compression for object metadata is limited, using Golang's tar implementation. In most instances, Kubernetes objects are limited to 1.5MB in size, but many don't approach that, meaning that compression may not be necessary. Note that File System Backup has not yet implemented compression, but does have de-deduplication capabilities. -- If you have [multiple](customize-installation.md/#configure-more-than-one-storage-location-for-backups-or-volume-snapshots) `VolumeSnapshotLocations` configured for a provider, you must always specify a valid `VolumeSnapshotLocation` when creating a backup, even if you are using [Restic](restic.md) for volume backups. You can optionally decide to set the [`--default-volume-snapshot-locations`](customize-locations.md#set-default-backup-storage-location-or-volume-snapshot-locations) flag using the `velero server`, which lists the default `VolumeSnapshotLocation` Velero should use if a `VolumeSnapshotLocation` is not specified when creating a backup. If you only have one `VolumeSnapshotLocation` for a provider, Velero will automatically use that location as the default. +- If you have [multiple](customize-installation.md/#configure-more-than-one-storage-location-for-backups-or-volume-snapshots) `VolumeSnapshotLocations` configured for a provider, you must always specify a valid `VolumeSnapshotLocation` when creating a backup, even if you are using [File System Backup](file-system-backup.md) for volume backups. You can optionally decide to set the [`--default-volume-snapshot-locations`](customize-locations.md#set-default-backup-storage-location-or-volume-snapshot-locations) flag using the `velero server`, which lists the default `VolumeSnapshotLocation` Velero should use if a `VolumeSnapshotLocation` is not specified when creating a backup. If you only have one `VolumeSnapshotLocation` for a provider, Velero will automatically use that location as the default. ## Examples diff --git a/site/content/docs/main/manual-testing.md b/site/content/docs/main/manual-testing.md index d7d8968831..136b490bb2 100644 --- a/site/content/docs/main/manual-testing.md +++ b/site/content/docs/main/manual-testing.md @@ -31,7 +31,7 @@ The "Backup and Restore" test cases below describe general backup and restore fu #### Backup and Restore - Verify that a backup and restore using Volume Snapshots can be performed -- Verify that a backup and restore using Restic can be performed +- Verify that a backup and restore using File System Backup can be performed - Verify that a backup of a cluster workload can be restored in a new cluster - Verify that an installation using the latest version can be used to restore from backups created with the last 3 versions. - e.g. Install Velero 1.6 and use it to restore backups from Velero v1.3, v1.4, v1.5. @@ -61,9 +61,9 @@ The following are test cases that are not currently performed as part of a Veler - Verify that backups that exceed their TTL are deleted - Verify that existing backups in object storage are synced to Velero -### Restic repository test cases +### Backup repository test cases -- Verify that restic repository maintenance is performed as the specified interval +- Verify that backup repository maintenance is performed as the specified interval ### Backup Hooks @@ -76,7 +76,7 @@ The following are test cases that are not currently performed as part of a Veler - Verify that an InitContainer restore hook provided via pod annotation is performed during restore - Verify that an InitContainer restore hook provided via Restore spec is performed during restore -- Verify that an InitContainer restore hook provided via Restore spec is performed during restore that includes restoring restic volumes +- Verify that an InitContainer restore hook provided via Restore spec is performed during restore that includes restoring File System Backup volumes - Verify that an Exec restore hook provided via pod annotation is performed during restore - Verify that an Exec restore hook provided via Restore spec is performed during restore diff --git a/site/content/docs/main/migration-case.md b/site/content/docs/main/migration-case.md index afb3a3e2a2..daac425f45 100644 --- a/site/content/docs/main/migration-case.md +++ b/site/content/docs/main/migration-case.md @@ -11,10 +11,10 @@ This page outlines a cluster migration scenario and some common configurations y Before migrating you should consider the following, -* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [restic](restic.md), which will backup volume contents at the filesystem level. +* Velero does not natively support the migration of persistent volumes snapshots across cloud providers. If you would like to migrate volume data between cloud platforms, enable [File System Backup](file-system-backup.md), which will backup volume contents at the filesystem level. * Velero doesn't support restoring into a cluster with a lower Kubernetes version than where the backup was taken. * Migrating workloads across clusters that are not running the same version of Kubernetes might be possible, but some factors need to be considered before migration, including the compatibility of API groups between clusters for each custom resource. If a Kubernetes version upgrade breaks the compatibility of core/native API groups, migrating with Velero will not be possible without first updating the impacted custom resources. For more information about API group versions, please see [EnableAPIGroupVersions](enable-api-group-versions-feature.md). -* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [restic](restic.md). +* The Velero plugin for AWS and Azure does not support migrating data between regions. If you need to do this, you must use [File System Backup](file-system-backup.md). ## Migration Scenario diff --git a/site/content/docs/main/on-premises.md b/site/content/docs/main/on-premises.md index 30ddf98b31..88e5479737 100644 --- a/site/content/docs/main/on-premises.md +++ b/site/content/docs/main/on-premises.md @@ -18,7 +18,7 @@ If you need to back up persistent volume data, you must select a volume backup s For example, if you use [Portworx][4] for persistent storage, you can install their Velero plugin to get native Portworx snapshots as part of your Velero backups. -If there is no native snapshot plugin available for your storage platform, you can use Velero's [restic integration][1], which provides a platform-agnostic file-level backup solution for volume data. +If there is no native snapshot plugin available for your storage platform, you can use Velero's [File System Backup][1], which provides a platform-agnostic file-level backup solution for volume data. ### Air-gapped deployments @@ -54,17 +54,17 @@ docker tag velero/velero-plugin-for-aws:$PLUGIN_VERSION $PRIVATE_REG/velero-plug docker push $PRIVATE_REG/velero-plugin-for-aws:$PLUGIN_VERSION ``` -#### Preparing the restic helper image (optional) +#### Preparing the restore helper image (optional) -If you are using restic, you will also need to upload the restic helper image. +If you are using File System Backup, you will also need to upload the restore helper image. ```bash PRIVATE_REG= VELERO_VERSION= -docker pull velero/velero-restic-restore-helper:$VELERO_VERSION -docker tag velero/velero-restic-restore-helper:$VELERO_VERSION $PRIVATE_REG/velero-restic-restore-helper:$VELERO_VERSION -docker push $PRIVATE_REG/velero-restic-restore-helper:$VELERO_VERSION +docker pull velero/velero-restore-helper:$VELERO_VERSION +docker tag velero/velero-restore-helper:$VELERO_VERSION $PRIVATE_REG/velero-restore-helper:$VELERO_VERSION +docker push $PRIVATE_REG/velero-restore-helper:$VELERO_VERSION ``` #### Pulling specific architecture images (optional) @@ -88,7 +88,7 @@ velero install \ [0]: supported-providers.md -[1]: restic.md +[1]: file-system-backup.md [2]: https://min.io [3]: contributions/minio.md [4]: https://portworx.com diff --git a/site/content/docs/main/restore-hooks.md b/site/content/docs/main/restore-hooks.md index 0ec36c5d7a..0e2e6b5326 100644 --- a/site/content/docs/main/restore-hooks.md +++ b/site/content/docs/main/restore-hooks.md @@ -12,7 +12,7 @@ Velero supports Restore Hooks, custom actions that can be executed during or aft Use an `InitContainer` hook to add init containers into a pod before it's restored. You can use these init containers to run any setup needed for the pod to resume running from its backed-up state. The InitContainer added by the restore hook will be the first init container in the `podSpec` of the restored pod. -In the case where the pod had volumes backed up using restic, then, the restore hook InitContainer will be added after the `restic-wait` InitContainer. +In the case where the pod had volumes backed up using File System Backup, then, the restore hook InitContainer will be added after the `restore-wait` InitContainer. NOTE: This ordering can be altered by any mutating webhooks that may be installed in the cluster. diff --git a/site/content/docs/main/restore-reference.md b/site/content/docs/main/restore-reference.md index a91ef960db..31a3e79602 100644 --- a/site/content/docs/main/restore-reference.md +++ b/site/content/docs/main/restore-reference.md @@ -29,7 +29,7 @@ The following is an overview of Velero's restore process that starts after you r 1. The `RestoreController` notices the new Restore object and performs validation. -1. The `RestoreController` fetches basic information about the backup being restored, like the [BackupStorageLocation](locations.md) (BSL). It also fetches a tarball of the cluster resources in the backup, any volumes that will be restored using Restic, and any volume snapshots to be restored. +1. The `RestoreController` fetches basic information about the backup being restored, like the [BackupStorageLocation](locations.md) (BSL). It also fetches a tarball of the cluster resources in the backup, any volumes that will be restored using File System Backup, and any volume snapshots to be restored. 1. The `RestoreController` then extracts the tarball of backup cluster resources to the /tmp folder and performs some pre-processing on the resources, including: @@ -56,14 +56,14 @@ The following is an overview of Velero's restore process that starts after you r * The `RestoreController` adds a `velero.io/backup-name` label with the backup name and a `velero.io/restore-name` with the restore name to the resource. This can help you easily identify restored resources and which backup they were restored from. -1. The `RestoreController` creates the resource object on the target cluster. If the resource is a PV then the `RestoreController` will restore the PV data from the [durable snapshot](#durable-snapshot-pv-restore), [Restic](#restic-pv-restore), or [CSI snapshot](#csi-pv-restore) depending on how the PV was backed up. +1. The `RestoreController` creates the resource object on the target cluster. If the resource is a PV then the `RestoreController` will restore the PV data from the [durable snapshot](#durable-snapshot-pv-restore), [File System Backup](#file-system-backup-pv-restore), or [CSI snapshot](#csi-pv-restore) depending on how the PV was backed up. If the resource already exists in the target cluster, which is determined by the Kubernetes API during resource creation, the `RestoreController` will skip the resource. The only [exception](#restore-existing-resource-policy) are Service Accounts, which Velero will attempt to merge differences between the backed up ServiceAccount into the ServiceAccount on the target cluster. You can [change the default existing resource restore policy](#restore-existing-resource-policy) to update resources instead of skipping them using the `--existing-resource-policy`. 1. Once the resource is created on the target cluster, Velero may take some additional steps or wait for additional processes to complete before moving onto the next resource to restore. * If the resource is a Pod, the `RestoreController` will execute any [Restore Hooks](restore-hooks.md) and wait for the hook to finish. - * If the resource is a PV restored by Restic, the `RestoreController` waits for Restic’s restore to complete. The `RestoreController` sets a timeout for any resources restored with Restic during a restore. The default timeout is 4 hours, but you can configure this be setting using `--restic-timeout` restore option. + * If the resource is a PV restored by File System Backup, the `RestoreController` waits for File System Backup’s restore to complete. The `RestoreController` sets a timeout for any resources restored with File System Backup during a restore. The default timeout is 4 hours, but you can configure this be setting using `--fs-backup-timeout` restore option. * If the resource is a Custom Resource Definition, the `RestoreController` waits for its availability in the cluster. The timeout is 1 minute. If any failures happen finishing these steps, the `RestoreController` will log an error in the restore result and will continue restoring. @@ -106,16 +106,16 @@ clusterresourcesets.addons.cluster.x-k8s.io Velero has three approaches when restoring a PV, depending on how the backup was taken. 1. When restoring a snapshot, Velero statically creates the PV and then binds it to a restored PVC. Velero's PV rename and remap process is used only in this case because this is the only case where Velero creates the PV resource directly. -1. When restoring with Restic, Velero uses Kubernetes’ [dynamic provision process](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) to provision the PV after creating the PVC. In this case, the PV object is not actually created by Velero. +1. When restoring with File System Backup, Velero uses Kubernetes’ [dynamic provision process](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) to provision the PV after creating the PVC. In this case, the PV object is not actually created by Velero. 1. When restoring with the [CSI plugin](csi.md), the PV is created from a CSI snapshot by the CSI driver. Velero doesn’t create the PV directly. Instead Velero creates a PVC with its DataSource referring to the CSI VolumeSnapshot object. ### Snapshot PV Restore PV data backed up by durable snapshots is restored by VolumeSnapshot plugins. Velero calls the plugins’ interface to create a volume from a snapshot. The plugin returns the volume’s `volumeID`. This ID is created by storage vendors and will be updated in the PV object created by Velero, so that the PV object is connected to the volume restored from a snapshot. -### Restic PV Restore +### File System Backup PV Restore -For more information on Restic restores, see the [Restic integration](restic.md#restore) page. +For more information on File System Backup restores, see the [File System Backup](file-system-backup.md#restore) page. ### CSI PV Restore diff --git a/site/content/docs/main/self-signed-certificates.md b/site/content/docs/main/self-signed-certificates.md index 2aab5c1b97..9724606de4 100644 --- a/site/content/docs/main/self-signed-certificates.md +++ b/site/content/docs/main/self-signed-certificates.md @@ -52,7 +52,7 @@ You will need to change this setting on the server to make it work. **Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premise environment. Using this flag in production is not recommended. -Velero provides a way for you to skip TLS verification on the object store when using the [AWS provider plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws) or [Restic](restic.md) by passing the `--insecure-skip-tls-verify` flag with the following Velero commands, +Velero provides a way for you to skip TLS verification on the object store when using the [AWS provider plugin](https://github.com/vmware-tanzu/velero-plugin-for-aws) or [File System Backup](file-system-backup.md) by passing the `--insecure-skip-tls-verify` flag with the following Velero commands, * velero backup describe * velero backup download @@ -60,6 +60,6 @@ Velero provides a way for you to skip TLS verification on the object store when * velero restore describe * velero restore log -If true, the object store's TLS certificate will not be checked for validity before Velero connects to the object store or Restic repo. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. +If true, the object store's TLS certificate will not be checked for validity before Velero or backup repository connects to the object storage. You can permanently skip TLS verification for an object store by setting `Spec.Config.InsecureSkipTLSVerify` to true in the [BackupStorageLocation](api-types/backupstoragelocation.md) CRD. -Note that Velero's Restic integration uses Restic commands to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve interacting with Restic, Velero will add the Restic global command parameter `--insecure-tls` to Restic commands. +Note that Velero's File System Backup uses Restic or Kopia to do data transfer between object store and Kubernetes cluster disks. This means that when you specify `--insecure-skip-tls-verify` in Velero operations that involve File System Backup, Velero will convey this information to Restic or Kopia. For example, for Restic, Velero will add the Restic global command parameter `--insecure-tls` to Restic commands. diff --git a/site/content/docs/main/supported-providers.md b/site/content/docs/main/supported-providers.md index fe8c03d3a1..0fc4a48aa0 100644 --- a/site/content/docs/main/supported-providers.md +++ b/site/content/docs/main/supported-providers.md @@ -54,7 +54,7 @@ _Some storage providers, like Quobyte, may need a different [signature algorithm ## Non-supported volume snapshots -In the case you want to take volume snapshots but didn't find a plugin for your provider, Velero has support for snapshotting using restic. Please see the [restic integration][30] documentation. +In the case you want to take volume snapshots but didn't find a plugin for your provider, Velero has support for snapshotting using File System Backup. Please see the [File System Backup][30] documentation. [0]: https://github.com/aws/aws-sdk-go/aws [1]: contributions/ibm-config.md @@ -65,6 +65,6 @@ In the case you want to take volume snapshots but didn't find a plugin for your [6]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/backupstoragelocation.md [7]: contributions/tencent-config.md [25]: https://github.com/hpe-storage/velero-plugin -[30]: restic.md +[30]: file-system-backup.md [36]: https://github.com/vmware-tanzu/velero-plugin-for-gcp#setup [38]: https://www.cloudian.com/ diff --git a/site/content/docs/main/tilt.md b/site/content/docs/main/tilt.md index 3f2a8d90eb..19ec6003bc 100644 --- a/site/content/docs/main/tilt.md +++ b/site/content/docs/main/tilt.md @@ -7,7 +7,7 @@ layout: docs This document describes how to use [Tilt](https://tilt.dev) with any cluster for a simplified workflow that offers easy deployments and rapid iterative builds. -This setup allows for continuing deployment of the Velero server and, if specified, any provider plugin or the restic daemonset. +This setup allows for continuing deployment of the Velero server and, if specified, any provider plugin or the node-agent daemonset. It does this work by: 1. Deploying the necessary Kubernetes resources, such as the Velero CRDs and Velero deployment @@ -60,7 +60,7 @@ Here is an example: "allowed_contexts": [ "development" ], - "enable_restic": false, + "use_node_agent": false, "create_backup_locations": true, "setup-minio": true, "enable_debug": false, @@ -82,8 +82,8 @@ Tilt: an existing image and version might be specified in the Velero deployment **allowed_contexts** (Array, default=[]): A list of kubeconfig contexts Tilt is allowed to use. See the Tilt documentation on *[allow_k8s_contexts](https://docs.tilt.dev/api.html#api.allow_k8s_contexts) for more details. Note: Kind is automatically allowed. -**enable_restic** (Bool, default=false): Indicate whether to deploy the restic Daemonset. If set to `true`, Tilt will look for a `velero/tilt-resources/restic.yaml` file -containing the configuration of the Velero restic DaemonSet. +**use_node_agent** (Bool, default=false): Indicate whether to deploy the node-agent Daemonset. If set to `true`, Tilt will look for a `velero/tilt-resources/node-agent.yaml` file +containing the configuration of the Velero node-agent DaemonSet. **create_backup_locations** (Bool, default=false): Indicate whether to create one or more backup storage locations. If set to `true`, Tilt will look for a `velero/tilt-resources/velero_v1_backupstoragelocation.yaml` file containing at least one configuration for a Velero backup storage location. @@ -97,7 +97,7 @@ containing at least one configuration for a Velero backup storage location. ### Create Kubernetes resource files to deploy All needed Kubernetes resource files are provided as ready to use samples in the `velero/tilt-resources/examples` directory. You only have to move them to the `velero/tilt-resources` level. -Because the Velero Kubernetes deployment as well as the restic DaemonSet contain the configuration +Because the Velero Kubernetes deployment as well as the node-agent DaemonSet contain the configuration for any plugin to be used, files for these resources are expected to be provided by the user so you may choose which provider plugin to load as a init container. Currently, the sample files provided are configured with all the plugins supported by Velero, feel free to remove any of them as needed. diff --git a/site/content/docs/main/troubleshooting.md b/site/content/docs/main/troubleshooting.md index dd1a331493..d7588f23a5 100644 --- a/site/content/docs/main/troubleshooting.md +++ b/site/content/docs/main/troubleshooting.md @@ -148,9 +148,9 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre ] ``` - If [restic-integration][3] is enabled, then, confirm that the restic daemonset is also mounting the `cloud-credentials` secret. + If [File System Backup][3] is enabled, then, confirm that the node-agent daemonset is also mounting the `cloud-credentials` secret. ```bash - $ kubectl -n velero get ds restic -ojson |jq .spec.template.spec.containers[0].volumeMounts + $ kubectl -n velero get ds node-agent -ojson |jq .spec.template.spec.containers[0].volumeMounts [ { "mountPath": "/host_pods", @@ -217,7 +217,7 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre [1]: debugging-restores.md [2]: debugging-install.md -[3]: restic.md +[3]: file-system-backup.md [4]: https://github.com/vmware-tanzu/velero/issues [5]: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html [6]: https://github.com/vmware-tanzu/helm-charts/blob/main/charts/velero diff --git a/site/content/docs/main/velero-install.md b/site/content/docs/main/velero-install.md index 8cc6d4d2e7..7fdd0f9361 100644 --- a/site/content/docs/main/velero-install.md +++ b/site/content/docs/main/velero-install.md @@ -21,12 +21,12 @@ velero install \ --velero-pod-mem-request \ --velero-pod-cpu-limit \ --velero-pod-mem-limit \ - [--use-restic] \ - [--default-volumes-to-restic] \ - [--restic-pod-cpu-request ] \ - [--restic-pod-mem-request ] \ - [--restic-pod-cpu-limit ] \ - [--restic-pod-mem-limit ] + [--use-node-agent] \ + [--default-volumes-to-fs-backup] \ + [--node-agent-pod-cpu-request ] \ + [--node-agent-pod-mem-request ] \ + [--node-agent-pod-cpu-limit ] \ + [--node-agent-pod-mem-limit ] ``` The values for the resource requests and limits flags follow the same format as [Kubernetes resource requirements][3] @@ -39,7 +39,7 @@ This section provides examples that serve as a starting point for more customize ```bash velero install --provider gcp --plugins velero/velero-plugin-for-gcp:v1.0.0 --bucket mybucket --secret-file ./gcp-service-account.json -velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --provider aws --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 --use-restic +velero install --provider aws --plugins velero/velero-plugin-for-aws:v1.0.0 --bucket backups --provider aws --secret-file ./aws-iam-creds --backup-location-config region=us-east-2 --snapshot-location-config region=us-east-2 --use-node-agent velero install --provider azure --plugins velero/velero-plugin-for-microsoft-azure:v1.0.0 --bucket $BLOB_CONTAINER --secret-file ./credentials-velero --backup-location-config resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID[,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID] --snapshot-location-config apiTimeout=[,resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID] ``` diff --git a/site/content/resources/_index.md b/site/content/resources/_index.md index 5d9221e00b..5f05a811a5 100644 --- a/site/content/resources/_index.md +++ b/site/content/resources/_index.md @@ -70,10 +70,4 @@ Here you will find external resources about Velero, such as videos, podcasts, an * [Cormac Hogan has written a series of blog posts on Velero](https://cormachogan.com/?s=velero) -* [Backup and Restore MariaDB Galera Deployments on Kubernetes - by Vikram Vaswani](https://docs.bitnami.com/tutorials/backup-restore-data-mariadb-galera-kubernetes/) - - -* Two great blog posts by community member Imran Pochi: - * [Backup and Restore of Kubernetes Applications using Heptio’s Velero with Restic and Rook-Ceph as the storage provider](https://blog.kubernauts.io/backup-and-restore-of-kubernetes-applications-using-heptios-velero-with-restic-and-rook-ceph-as-2e8df15b1487) - * [Backup and Restore PVCs using Velero with restic and OpenEBS from Baremetal cluster to AWS - ](https://blog.kubernauts.io/backup-and-restore-pvcs-using-velero-with-restic-and-openebs-from-baremetal-cluster-to-aws-d3ac54386109) \ No newline at end of file +* [Backup and Restore MariaDB Galera Deployments on Kubernetes - by Vikram Vaswani](https://docs.bitnami.com/tutorials/backup-restore-data-mariadb-galera-kubernetes/) \ No newline at end of file From 70edb5bdfa6846c5663b28d877d9a0730c97e70f Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 360/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index e917535f31..82a0ee1238 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -16,8 +16,8 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone - * Beijing/US friendly - we start at 8am Beijing Time(bound to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) - * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10)) + * Beijing/US friendly - we start at 8am Beijing Time(bound to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am) + * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 32637da16bd51b202cbb274fc972d18efea4fc27 Mon Sep 17 00:00:00 2001 From: Ming Date: Wed, 2 Nov 2022 06:37:41 +0000 Subject: [PATCH 361/366] fix restic backup progress error Signed-off-by: Ming --- changelogs/unreleased/5534-qiuming-best | 1 + pkg/restic/exec_commands.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5534-qiuming-best diff --git a/changelogs/unreleased/5534-qiuming-best b/changelogs/unreleased/5534-qiuming-best new file mode 100644 index 0000000000..09faeac0c2 --- /dev/null +++ b/changelogs/unreleased/5534-qiuming-best @@ -0,0 +1 @@ +fix restic backup progress error diff --git a/pkg/restic/exec_commands.go b/pkg/restic/exec_commands.go index 22c1a96659..4ff9e1a325 100644 --- a/pkg/restic/exec_commands.go +++ b/pkg/restic/exec_commands.go @@ -105,8 +105,8 @@ func RunBackup(backupCmd *Command, log logrus.FieldLogger, updater uploader.Prog // caller with the progress if stat.BytesDone != 0 { updater.UpdateProgress(&uploader.UploaderProgress{ - TotalBytes: stat.TotalBytesProcessed, - BytesDone: stat.TotalBytesProcessed, + TotalBytes: stat.TotalBytes, + BytesDone: stat.BytesDone, }) } } From 7c16103987e959e180590f3e6573e7e7136b9db2 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 362/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index 82a0ee1238..f4c92f7b58 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -17,7 +17,7 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone * Beijing/US friendly - we start at 8am Beijing Time(bound to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am) - * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PDT(7am PST) / 4pm CEST (3pm CET) / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10) + * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PT / 3pm CET / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From 52c8785e7920a82c42c41e11f3e1e689078fc246 Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Fri, 28 Oct 2022 17:57:18 +0300 Subject: [PATCH 363/366] Update Community meetings times https://github.com/vmware-tanzu/velero/discussions/5091 Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index f4c92f7b58..7e21132908 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -17,7 +17,7 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09) Bi-weekly community meeting alternating every week between Beijing Friendly timezone and EST/Europe Friendly Timezone * Beijing/US friendly - we start at 8am Beijing Time(bound to CST) / 8pm EDT(7pm EST) / 5pm PDT(4pm PST) / 2am CEST(1am CET) - [Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am) - * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PT / 3pm CET / 10pm/11pm CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10) + * US/Europe friendly - we start at 10am ET(bound to ET) / 7am PT / 3pm CET / 10pm(11pm) CST - [Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=10) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) * See previous community meetings on our [YouTube Channel](https://www.youtube.com/playlist?list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM) * Have a question to discuss in the community meeting? Please add it to our [Q&A Discussion board](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a) From c186a7d193d1a5bc87aa30ed617364403388b91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Mon, 31 Oct 2022 16:28:06 +0800 Subject: [PATCH 364/366] Enhance the restore priorities list to support specifying the low prioritized resources that need to be restored in the last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the restore priorities list to support specifying the low prioritized resources that need to be r estored in the last Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/5535-ywk253100 | 1 + pkg/cmd/server/server.go | 57 ++++++++------ pkg/restore/priority.go | 92 ++++++++++++++++++++++ pkg/restore/priority_test.go | 110 +++++++++++++++++++++++++++ pkg/restore/restore.go | 40 ++++++---- pkg/restore/restore_test.go | 33 +++++--- 6 files changed, 286 insertions(+), 47 deletions(-) create mode 100644 changelogs/unreleased/5535-ywk253100 create mode 100644 pkg/restore/priority.go create mode 100644 pkg/restore/priority_test.go diff --git a/changelogs/unreleased/5535-ywk253100 b/changelogs/unreleased/5535-ywk253100 new file mode 100644 index 0000000000..186672ea0c --- /dev/null +++ b/changelogs/unreleased/5535-ywk253100 @@ -0,0 +1 @@ +Enhance the restore priorities list to support specifying the low prioritized resources that need to be restored in the last \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 8e24affcad..33f43c3c20 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -121,7 +121,7 @@ type serverConfig struct { pluginDir, metricsAddress, defaultBackupLocation string backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration defaultBackupTTL, storeValidationFrequency, defaultCSISnapshotTimeout time.Duration - restoreResourcePriorities []string + restoreResourcePriorities restore.Priorities defaultVolumeSnapshotLocations map[string]string restoreOnly bool disabledControllers []string @@ -216,7 +216,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.podVolumeOperationTimeout, "fs-backup-timeout", config.podVolumeOperationTimeout, "How long pod volume file system backups/restores should be allowed to run before timing out.") command.Flags().BoolVar(&config.restoreOnly, "restore-only", config.restoreOnly, "Run in a mode where only restores are allowed; backups, schedules, and garbage-collection are all disabled. DEPRECATED: this flag will be removed in v2.0. Use read-only backup storage locations instead.") command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("List of controllers to disable on startup. Valid values are %s", strings.Join(controller.DisableableControllers, ","))) - command.Flags().StringSliceVar(&config.restoreResourcePriorities, "restore-resource-priorities", config.restoreResourcePriorities, "Desired order of resource restores; any resource not in the list will be restored alphabetically after the prioritized resources.") + command.Flags().Var(&config.restoreResourcePriorities, "restore-resource-priorities", "Desired order of resource restores, the priority list contains two parts which are split by \"-\" element. The resources before \"-\" element are restored first as high priorities, the resources after \"-\" element are restored last as low priorities, and any resource not in the list will be restored alphabetically between the high and low priorities.") command.Flags().StringVar(&config.defaultBackupLocation, "default-backup-storage-location", config.defaultBackupLocation, "Name of the default backup storage location. DEPRECATED: this flag will be removed in v2.0. Use \"velero backup-location set --default\" instead.") command.Flags().DurationVar(&config.storeValidationFrequency, "store-validation-frequency", config.storeValidationFrequency, "How often to verify if the storage is valid. Optional. Set this to `0s` to disable sync. Default 1 minute.") command.Flags().Var(&volumeSnapshotLocations, "default-volume-snapshot-locations", "List of unique volume providers and default volume snapshot location (provider1:location-01,provider2:location-02,...)") @@ -488,6 +488,7 @@ func (s *server) veleroResourcesExist() error { return nil } +// High priorities: // - Custom Resource Definitions come before Custom Resource so that they can be // restored with their corresponding CRD. // - Namespaces go second because all namespaced resources depend on them. @@ -510,28 +511,36 @@ func (s *server) veleroResourcesExist() error { // - CAPI Clusters come before ClusterResourceSets because failing to do so means the CAPI controller-manager will panic. // Both Clusters and ClusterResourceSets need to come before ClusterResourceSetBinding in order to properly restore workload clusters. // See https://github.com/kubernetes-sigs/cluster-api/issues/4105 -var defaultRestorePriorities = []string{ - "customresourcedefinitions", - "namespaces", - "storageclasses", - "volumesnapshotclass.snapshot.storage.k8s.io", - "volumesnapshotcontents.snapshot.storage.k8s.io", - "volumesnapshots.snapshot.storage.k8s.io", - "persistentvolumes", - "persistentvolumeclaims", - "secrets", - "configmaps", - "serviceaccounts", - "limitranges", - "pods", - // we fully qualify replicasets.apps because prior to Kubernetes 1.16, replicasets also - // existed in the extensions API group, but we back up replicasets from "apps" so we want - // to ensure that we prioritize restoring from "apps" too, since this is how they're stored - // in the backup. - "replicasets.apps", - "clusterclasses.cluster.x-k8s.io", - "clusters.cluster.x-k8s.io", - "clusterresourcesets.addons.cluster.x-k8s.io", +// +// Low priorities: +// - Tanzu ClusterBootstrap go last as it can reference any other kind of resources +var defaultRestorePriorities = restore.Priorities{ + HighPriorities: []string{ + "customresourcedefinitions", + "namespaces", + "storageclasses", + "volumesnapshotclass.snapshot.storage.k8s.io", + "volumesnapshotcontents.snapshot.storage.k8s.io", + "volumesnapshots.snapshot.storage.k8s.io", + "persistentvolumes", + "persistentvolumeclaims", + "secrets", + "configmaps", + "serviceaccounts", + "limitranges", + "pods", + // we fully qualify replicasets.apps because prior to Kubernetes 1.16, replicasets also + // existed in the extensions API group, but we back up replicasets from "apps" so we want + // to ensure that we prioritize restoring from "apps" too, since this is how they're stored + // in the backup. + "replicasets.apps", + "clusterclasses.cluster.x-k8s.io", + "clusters.cluster.x-k8s.io", + "clusterresourcesets.addons.cluster.x-k8s.io", + }, + LowPriorities: []string{ + "clusterbootstraps.run.tanzu.vmware.com", + }, } func (s *server) checkNodeAgent() { diff --git a/pkg/restore/priority.go b/pkg/restore/priority.go new file mode 100644 index 0000000000..c544532c74 --- /dev/null +++ b/pkg/restore/priority.go @@ -0,0 +1,92 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restore + +import ( + "fmt" + "strings" +) + +const ( + prioritySeparator = "-" +) + +// Priorities defines the desired order of resource operations: +// Resources in the HighPriorities list will be handled first +// Resources in the LowPriorities list will be handled last +// Other resources will be handled alphabetically after the high prioritized resources and before the low prioritized resources +type Priorities struct { + HighPriorities []string + LowPriorities []string +} + +// String returns a string representation of Priority. +func (p *Priorities) String() string { + priorities := p.HighPriorities + if len(p.LowPriorities) > 0 { + priorities = append(priorities, prioritySeparator) + priorities = append(priorities, p.LowPriorities...) + } + return strings.Join(priorities, ",") +} + +// Set parses the provided string to the priority object +func (p *Priorities) Set(s string) error { + if len(s) == 0 { + return nil + } + strs := strings.Split(s, ",") + separatorIndex := -1 + for i, str := range strs { + if str == prioritySeparator { + if separatorIndex > -1 { + return fmt.Errorf("multiple priority separator %q found", prioritySeparator) + } + separatorIndex = i + } + } + // has no separator + if separatorIndex == -1 { + p.HighPriorities = strs + return nil + } + // start with separator + if separatorIndex == 0 { + // contain only separator + if len(strs) == 1 { + return nil + } + p.LowPriorities = strs[1:] + return nil + } + // end with separator + if separatorIndex == len(strs)-1 { + p.HighPriorities = strs[:len(strs)-1] + return nil + } + + // separator in the middle + p.HighPriorities = strs[:separatorIndex] + p.LowPriorities = strs[separatorIndex+1:] + + return nil +} + +// Type specifies the flag type +func (p *Priorities) Type() string { + return "stringArray" +} diff --git a/pkg/restore/priority_test.go b/pkg/restore/priority_test.go new file mode 100644 index 0000000000..4336c6bfac --- /dev/null +++ b/pkg/restore/priority_test.go @@ -0,0 +1,110 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package restore + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStringOfPriorities(t *testing.T) { + priority := Priorities{ + HighPriorities: []string{"high"}, + } + assert.Equal(t, "high", priority.String()) + + priority = Priorities{ + HighPriorities: []string{"high"}, + LowPriorities: []string{"low"}, + } + assert.Equal(t, "high,-,low", priority.String()) +} + +func TestSetOfPriority(t *testing.T) { + cases := []struct { + name string + input string + priorities Priorities + hasErr bool + }{ + { + name: "empty input", + input: "", + priorities: Priorities{}, + hasErr: false, + }, + { + name: "only high priorities", + input: "p0", + priorities: Priorities{ + HighPriorities: []string{"p0"}, + }, + hasErr: false, + }, + { + name: "only low priorities", + input: "-,p9", + priorities: Priorities{ + LowPriorities: []string{"p9"}, + }, + hasErr: false, + }, + { + name: "only separator", + input: "-", + priorities: Priorities{}, + hasErr: false, + }, + { + name: "multiple separators", + input: "-,-", + priorities: Priorities{}, + hasErr: true, + }, + { + name: "contain both high and low priorities", + input: "p0,p1,p2,-,p9", + priorities: Priorities{ + HighPriorities: []string{"p0", "p1", "p2"}, + LowPriorities: []string{"p9"}, + }, + hasErr: false, + }, + { + name: "end with separator", + input: "p0,-", + priorities: Priorities{ + HighPriorities: []string{"p0"}, + }, + hasErr: false, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + p := Priorities{} + err := p.Set(c.input) + if c.hasErr { + require.NotNil(t, err) + } else { + require.Nil(t, err) + } + assert.Equal(t, c.priorities, p) + }) + } +} diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index ef6578a03a..9f1130fc06 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -110,7 +110,7 @@ type kubernetesRestorer struct { podVolumeRestorerFactory podvolume.RestorerFactory podVolumeTimeout time.Duration resourceTerminatingTimeout time.Duration - resourcePriorities []string + resourcePriorities Priorities fileSystem filesystem.Interface pvRenamer func(string) (string, error) logger logrus.FieldLogger @@ -124,7 +124,7 @@ func NewKubernetesRestorer( restoreClient velerov1client.RestoresGetter, discoveryHelper discovery.Helper, dynamicFactory client.DynamicFactory, - resourcePriorities []string, + resourcePriorities Priorities, namespaceClient corev1.NamespaceInterface, podVolumeRestorerFactory podvolume.RestorerFactory, podVolumeTimeout time.Duration, @@ -358,7 +358,7 @@ type restoreContext struct { renamedPVs map[string]string pvRenamer func(string) (string, error) discoveryHelper discovery.Helper - resourcePriorities []string + resourcePriorities Priorities hooksWaitGroup sync.WaitGroup hooksErrs chan error resourceRestoreHooks []hook.ResourceRestoreHook @@ -374,19 +374,31 @@ type resourceClientKey struct { // getOrderedResources returns an ordered list of resource identifiers to restore, // based on the provided resource priorities and backup contents. The returned list -// begins with all of the prioritized resources (in order), and appends to that -// an alphabetized list of all resources in the backup. -func getOrderedResources(resourcePriorities []string, backupResources map[string]*archive.ResourceItems) []string { - // alphabetize resources in the backup - orderedBackupResources := make([]string, 0, len(backupResources)) +// begins with all of the high prioritized resources (in order), ends with all of +// the low prioritized resources(in order), and an alphabetized list of resources +// in the backup(pick out the prioritized resources) is put in the middle. +func getOrderedResources(resourcePriorities Priorities, backupResources map[string]*archive.ResourceItems) []string { + priorities := map[string]struct{}{} + for _, priority := range resourcePriorities.HighPriorities { + priorities[priority] = struct{}{} + } + for _, priority := range resourcePriorities.LowPriorities { + priorities[priority] = struct{}{} + } + + // pick the prioritized resources out + var orderedBackupResources []string for resource := range backupResources { + if _, exist := priorities[resource]; exist { + continue + } orderedBackupResources = append(orderedBackupResources, resource) } + // alphabetize resources in the backup sort.Strings(orderedBackupResources) - // Main list: everything in resource priorities, followed by what's in the - // backup (alphabetized). - return append(resourcePriorities, orderedBackupResources...) + list := append(resourcePriorities.HighPriorities, orderedBackupResources...) + return append(list, resourcePriorities.LowPriorities...) } type progressUpdate struct { @@ -479,7 +491,7 @@ func (ctx *restoreContext) execute() (Result, Result) { backupResources, make([]restoreableResource, 0), sets.NewString(), - []string{"customresourcedefinitions"}, + Priorities{HighPriorities: []string{"customresourcedefinitions"}}, false, ) warnings.Merge(&w) @@ -1796,7 +1808,7 @@ func (ctx *restoreContext) getOrderedResourceCollection( backupResources map[string]*archive.ResourceItems, restoreResourceCollection []restoreableResource, processedResources sets.String, - resourcePriorities []string, + resourcePriorities Priorities, includeAllResources bool, ) ([]restoreableResource, sets.String, Result, Result) { var warnings, errs Result @@ -1818,7 +1830,7 @@ func (ctx *restoreContext) getOrderedResourceCollection( if includeAllResources { resourceList = getOrderedResources(resourcePriorities, backupResources) } else { - resourceList = resourcePriorities + resourceList = resourcePriorities.HighPriorities } for _, resource := range resourceList { // try to resolve the resource via discovery to a complete group/version/resource diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index f97849921f..c8bd37ad50 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -681,7 +681,7 @@ func TestRestoreResourcePriorities(t *testing.T) { backup *velerov1api.Backup apiResources []*test.APIResource tarball io.Reader - resourcePriorities []string + resourcePriorities Priorities }{ { name: "resources are restored according to the specified resource priorities", @@ -715,7 +715,10 @@ func TestRestoreResourcePriorities(t *testing.T) { test.Deployments(), test.ServiceAccounts(), }, - resourcePriorities: []string{"persistentvolumes", "serviceaccounts", "pods", "deployments.apps"}, + resourcePriorities: Priorities{ + HighPriorities: []string{"persistentvolumes", "persistentvolumeclaims", "serviceaccounts"}, + LowPriorities: []string{"deployments.apps"}, + }, }, } @@ -747,7 +750,7 @@ func TestRestoreResourcePriorities(t *testing.T) { ) assertEmptyResults(t, warnings, errs) - assertResourceCreationOrder(t, tc.resourcePriorities, recorder.resources) + assertResourceCreationOrder(t, []string{"persistentvolumes", "persistentvolumeclaims", "serviceaccounts", "pods", "deployments.apps"}, recorder.resources) } } @@ -2624,7 +2627,7 @@ func TestRestorePersistentVolumes(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { h := newHarness(t) - h.restorer.resourcePriorities = []string{"persistentvolumes", "persistentvolumeclaims"} + h.restorer.resourcePriorities = Priorities{HighPriorities: []string{"persistentvolumes", "persistentvolumeclaims"}} h.restorer.pvRenamer = func(oldName string) (string, error) { renamed := "renamed-" + oldName return renamed, nil @@ -2942,19 +2945,19 @@ func TestIsCompleted(t *testing.T) { func Test_getOrderedResources(t *testing.T) { tests := []struct { name string - resourcePriorities []string + resourcePriorities Priorities backupResources map[string]*archive.ResourceItems want []string }{ { name: "when only priorities are specified, they're returned in order", - resourcePriorities: []string{"prio-3", "prio-2", "prio-1"}, + resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}}, backupResources: nil, want: []string{"prio-3", "prio-2", "prio-1"}, }, { name: "when only backup resources are specified, they're returned in alphabetical order", - resourcePriorities: nil, + resourcePriorities: Priorities{}, backupResources: map[string]*archive.ResourceItems{ "backup-resource-3": nil, "backup-resource-2": nil, @@ -2964,14 +2967,26 @@ func Test_getOrderedResources(t *testing.T) { }, { name: "when priorities and backup resources are specified, they're returned in the correct order", - resourcePriorities: []string{"prio-3", "prio-2", "prio-1"}, + resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}}, + backupResources: map[string]*archive.ResourceItems{ + "prio-3": nil, + "backup-resource-3": nil, + "backup-resource-2": nil, + "backup-resource-1": nil, + }, + want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3"}, + }, + { + name: "when priorities and backup resources are specified, they're returned in the correct order", + resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}, LowPriorities: []string{"prio-0"}}, backupResources: map[string]*archive.ResourceItems{ "prio-3": nil, + "prio-0": nil, "backup-resource-3": nil, "backup-resource-2": nil, "backup-resource-1": nil, }, - want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3", "prio-3"}, + want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3", "prio-0"}, }, } From efcb63a20d67d8144a4027afbd0bc85ac57fc519 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 3 Nov 2022 10:55:59 +0800 Subject: [PATCH 365/366] Fix GCP StorageClass used for E2E testing's YAML syntax error. (#5536) Signed-off-by: Xun Jiang Signed-off-by: Xun Jiang Co-authored-by: Xun Jiang --- test/e2e/testdata/storage-class/gcp.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/testdata/storage-class/gcp.yaml b/test/e2e/testdata/storage-class/gcp.yaml index 30ee8fc1fa..397a4b19f3 100644 --- a/test/e2e/testdata/storage-class/gcp.yaml +++ b/test/e2e/testdata/storage-class/gcp.yaml @@ -9,5 +9,5 @@ parameters: type: pd-standard provisioner: kubernetes.io/gce-pd reclaimPolicy: Delete -volumeBindingMode: volumeBindingMode: WaitForFirstConsumer +volumeBindingMode: WaitForFirstConsumer From ad4fc0b1e45f3dfb7cd73fa974b0b5c76cf1a145 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 3 Nov 2022 17:12:05 -0400 Subject: [PATCH 366/366] add VSL credentials documentation. Signed-off-by: Scott Seago --- site/content/docs/main/locations.md | 54 ++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/locations.md b/site/content/docs/main/locations.md index 1b4cc15d83..db347801b0 100644 --- a/site/content/docs/main/locations.md +++ b/site/content/docs/main/locations.md @@ -205,7 +205,7 @@ kubectl create secret generic -n velero credentials --from-file=bsl= \ --credential== ``` +### Create a volume snapshot location that uses unique credentials + +It is possible to create additional `VolumeSnapshotLocations` that use their own credentials. +This may be necessary if you already have default credentials which don't match the account used by the cloud volumes being backed up. + +If you create additional `VolumeSnapshotLocations` without specifying the credentials to use, Velero will use the credentials provided at install time and stored in the `cloud-credentials` secret. + +#### Prerequisites +- This feature requires support from the [volume snapshotter plugin][5] you wish to use. + All plugins maintained by the Velero team support this feature. + If you are using a plugin from another provider, please check their documentation to determine if this is supported. +- The [plugin for the volume snapshotter provider][5] you wish to use must be [installed][6]. +- You must create a file with the object storage credentials. Follow the instructions provided by your object storage provider plugin to create this file. + +Once you have installed the necessary plugin and created the credentials file, create a [Kubernetes Secret][7] in the Velero namespace that contains these credentials: + +```shell +kubectl create secret generic -n velero credentials --from-file=vsl= +``` + +This will create a secret named `credentials` with a single key (`vsl`) which contains the contents of your credentials file. +Next, create a `VolumeSnapshotLocation` that uses this Secret by passing the Secret name and key in the `--credential` flag. +When interacting with this `VolumeSnapshotLocation` in the future, Velero will fetch the data from the key within the Secret you provide. + +For example, a new `VolumeSnapshotLocation` with a Secret would be configured as follows: + +```bash +velero snapshot-location create \ + --provider \ + --config region= \ + --credential== +``` + +To use this new `VolumeSnapshotLocation` when performing a backup, use the flag `--volume-snapshot-locations [, \ + --credential== +``` + ## Additional Use Cases 1. If you're using Azure's AKS, you may want to store your volume snapshots outside of the "infrastructure" resource group that is automatically created when you create your AKS cluster. This is possible using a `VolumeSnapshotLocation`, by specifying a `resourceGroup` under the `config` section of the snapshot location. See the [Azure volume snapshot location documentation][3] for details.

    AjI(EJ}}d3T-=| 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�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

Yy zflqg(w52ZoKEMd%XNTC$vJFJ?r8Np_PC)E*0|>fpc5 z?sIJsjo-Fu=ZTGJ6^(o-RamW$S(BFffF{Q(cwfFu@DUP^1Jk*8UPTjSrBX@2i_18r z0-Y34?}DWqDhhF8(Us^0%TL^ObGfNoC6>f5I`$5PJ8T(|=_D7AjVSMFYCh>a1wyXH zv`-Q@LWj)1myvb}Bz)!kd*m^iyS{AkcOS766pcnJgDb0Tl`X23ijG?*gH1?OP7gs9 z&Aflc;q>PiZ;s8X=I`N7#xe2Y~usFUJg9# z8|}-m1q;Oiqz)~EwezRDtM->GCm>pox6S76H9QF}UuZk3t21Za5R%9qy#OQVv(e;2 zCA1zMCTTmK_*&W-nQVYJ%i-t300HaSz*`z0}F5ZqctR_&Mu%*z{eyh%B+c zdW?mUHeD1IROObjr$*8&)k{&>2p$mS_zy78%9@h^2mRZl&;l50C%;GR_p9O=(|}J? z&TZ)5Na-wI^lS$BiGp{ZZp{&kpCiFQe_hGY78skO`B>L<<#W)>(Hnjsb$zyz7laBC z_)s-FyEE&OUf-JPDA+T!a&OPTs zag>B7vvWhr(8-gloDU@pe+2#|K224F(FON>d%u9jp0v=Z`)hg(=*~WI>P0wj0O3z@QqNg ziv*CF3Ozm)4_umWcD(@D1i)WgJt-y`=xtxQ{8>;J5eht}k9=DREhq->_(m^;c-%rb z)vO;H)r~RS1zDXkV4(k|tH8`!1)0rebG8>>v}Cc#*H2Sg>YhJ>Xc2k%5AwfSdJu2S z(FKsR3-Xn8CM#ph_4tO9mi#^YYURYDs$y|v{UhIQ>MdwhLqvQ_K zKXmo#*s}_wz>fvWFT9 z9=D=1uVTkm#s0V`jQRV4tXKw4d)}`w*4D>*CORC3ixEo~EGWg!c%k^BETfAMQUxPM zA|LQdu{rp$H|1?4DsoU=MmGP_~sQE%k&!+_h=S}mBP5U+l z9Q6p9`~m)tEusifBF$JfX6rS|b8@K)H!Q0^ti5LxWPE70B+O;0^Z1yCrz&`)S=4=? zkn42~7;Bu4qg8RPw+1OJ^_Zcx?YAfceXIv)++12@fnb-L{j}hpkI>DwWbf9Sa62DRbkd)X{FIolmSL!glN^V9Jw|srqsVD;=Hi6VPCt_|AVhx=~o$> zq3Z>riqMKUd-4tN1H7kCcdkCSr)P;+EvD6h=DF(AL+X z=hsP}WX=bLVdNV8f~9>NjjcYIhp~RIXr}{;qWh|lM$3e3L;lfuP{`3{?U&((@edh` zqNEom^mfFt|6w|;dQdbXc+^C4HRnSaxV3|Xh`kp1>7ydFqxfqDvp;h2;bdswTiroc zrwBJeF^Bz0at{4bClZ8fIH!-|G{S6BXi4wGcmE(CzvedJ61B@M#Zy@HecX#)Y=Co2;1FIV*o1ynM~ zBza|PNc#i(cV$W^K@?CnuTg1PJhvN#?mGs&mk9qKk-lP!1ZaM(bl(Ca%)3r*79y5< z8gZMCJEU?XH5%q>et2B$*p`@o$g}0)H>+n7wust%FO{}&NoJ_!cXGkMeJ0ikwQ!O= zAHrxx{AVh=W65f+rm{eatKdP4Y^+XtEL4q)~tP=1$rU zIqRzo?t$6)GTN)R@6HM~;p%WT(ZF2Vr{YtdbHJ-VreVvfKZlUxmB=G zuFuUO(|$}H?i&QDh~CtLbtGJOrlKw-OJoM(@W4N!JP~2cEv$$mKou$q6^%*hIiO|i zG)T=e9)^OEdE7OE5?r@nVqzi} z8_7@tR3&1@|rYI{8aqLlvf^?AB8s#D#|h??Hr^29xO1AF-gQw zAQ+GK0}~$lUODLHK`vr-Etw{XteTLp7LTeV=pPJ{N%>LvR1nh-%lb~Up9(k&H+Q&P zYtzus5}Y;ccu~hQ+S+jL%e424j;fY^HHXF~03C<^f5c83c4qWu8BaCp4cf)qNoYpg zbKyf9-Tt|utoEu!1ZP4q!%Ict?HquS8i)~g^B`I{sH%?c@PoAIAs$he{u0U#D{bxm z-o8RPf7yXjU*Ly#`8l*Lt4e(jW7|943ajdR-a5B-L8EVjZ+I4p@(y)EV0vdqj+ z`v!M`Mmp;E7syFj*#?>1wT_B~R30h@edbHUzh>02!T`wBgad2FU&v2CbJw!P2fLKw zog51kS^AK9rJ^@5ZOh057t;@CqT#qNw;9`$xX!yQagq_gJJml=V@8U9%)OQ0yWc?f zF3IQ})8sqPnYd1Hm9e?b3){vODY9YeUzKWfn5mtTpY71ZhW(q>ga6%Jd4m5@L=_$qaF7j_`vUV@9E)EOMvmY@roW9<0gjHSsO`% z?#jybE{~BXXOrWD$s2kRqE%YcwF!d}^n9dMV4DZrd|Hb>xr01}-L*?q4L9-Xv;?P) zKJCk->~aW+;Tavna1E@7+xJ3MaQ>-uvYWR#aq25^hJv4Jj@kKNz6a)rsBB{matP@2 zrtAg!JaeH($?oP@S;Q!W#X6XZ-N~X)$2{v!-7)=EL~+ zL|K!c`XGrwq;V?((fL+HD159N_vr88Y!D;MOp078(Glo4B`O302z5ZX8Q%W#ZF+$i z;z+50WSn|*byZpCmINie6z{v6s7<4>W7VemTR z^pBz<(0zJ}qoh6_4KiJ6)O-+L9m$ZwRl9yw`MPX5?*+eMX54UjUb}}}~20R`Fi_pljtKkP#6gGXb5}-A0HA2kw+jP z8#uW5^B~OuPy~||-9zx`q*&J8!J->Q#Hi$Bf5b{#06>tDg%mP8*rRWV3EH7|6x036 zdU1agQ6$lZxNbHV7>0H(cbM>r%AV;xiJxs-@sTBoNj|-ks!I_mlN(LGL-`kO=NFMr-P>kqQ z>ol!IKo8$J&+x|W(?i4T%_2BiUA@#FQv;I#gp^qLqfib-x1zqH(w89-S`Gs>CA{Rz z^Xdjg-r)3FD-V~DGlxm@Ys?l)^|QR224Ru2X%-Tf-=E@P;Au4=ZT?+_*f?ib%ufeb zMA0$irxTR98!_vCL9;k-_HxlEo;x7~i9p_Q-S!`Ya0Y%)PD87{wz_d%9P2Qy5##^B zVl3$9nMe0lJ<)FscSRn~H}k~#0aFZ-w<1Qv$44lq5UwLv9ZWQW%~^jLcHzFou98Y8 zlLvxUGUTF{i#1=|j@fV0{bKMqoX~?TYvOT;b@LYs{_P2#oNAl$3yVFB3d(XzBxx4P zEU7#(^OKWNq2?-OMDXE&}j zA(I0!ReVZ40shwwHv#R&)gIO4kKv6M>wB&eIcmUbzP9QO!RSuyC&!gnU(XWk{LcD) zEliIwn?mVvV#Jiv;=a@SNGOxJqxrdRvearEzd?-j{Snzzgy2h6GWM$#8-X!yPF0+U zxF3(hYw#?GuPIZ6+dP$Y46>^k{NWuPzu%94fVh&Q8cqD`xd|;)8^A1e>2Nf-ykv`{ zr0m4^6>srl3{$mA`rNA1r9I%~`td2~Wh?0EdJ8Dvy??xa-dpRGlp-K-g{lEsU*N6(qJ5IyAC-%} z`+E_-K<{khWM15Ep19c?FYv2RIz0HqQ!@t3MfG}FWzpTpfc(D6a*oLFlOifM>y^$+ zm(FS?g2ftQ$2v|>X-9-XB~ZE-Z}z4~;?G72U0FG5eppuau(|+$Mj|zu#y$oo>q^*W z94<)fXRaX$=Uls)JCW}`b!X>ok|>qo{{F^J_$7as13pQLlddPp5AJW4vG9yY7}cG! zEwn>}?gVXg*AgtpTq|~5sgW-Q>quNB<{hMmpOf!ZZGJngiS9V~TxabgLkp45FEozN zd++wvEOIvugdm17cET&0@-x<@+Ua3OG{5@mKQv~KR5Q|ucrm_w0>6w1Sj<~u%1FNW zZtytxoaa<%?9j&*1vZQ&eu`8kKY^k?Ks&{bs?^SCwz95-!Dh8hD*d8H>!6-v zu@>LW{CZXtd&?c@5cpFCSoDcf_(&l}c`LD+D_9Fl~h2TNV<{u39|U zHoK4+EQKo0(i6i*=e&c90%8GwDNqZ7rZR%L=Cxhd(%cSdetcIg;vwM*k0M8JZATK! z_41#h)-sZC-|b{T8%tB)^z#fN;|JHchq;evm?e2OPzB*3GrQ(~`yMIv8rWp=!J0j6 zjY*eIvi|+@MEhmd#!Ai@?e+r22~eg)Oh?vP^8qzdVx(v+&$I^<*hgf*?^f-~Vx*vl z&s>`(U0&x#5Cl&I;;+Gf7{8bf(UhqegLYmOy`hrn+MeZR^xKXvB_k)Npa+AH%q*Ev zTnOU+ZOnc!zkWIu^$ZTFj+L~Llz>6!qdaA-CIGlIc$i3?NKLjK^oNJwo1Z`h1}u~A ztMD(Zr_HmCtZ{g;5Hhw`gl!EK`apu1X1{vSAZPYgZDtf}qowJnIZ`16=b;dDfQ@{$ ze$bbQ#O=g-^unQ%U`+!PbohX4Hf&{6**F4<0y`8k9Mt|^u;H5IQQ26f8J`sTTnCu2 zR3Ql%==PPf2PuujxpbfpXZ*Y_x}*G3h=0F<9c*N-#4V*dws$E!QS`)qZ0=M4yuj=K z5(BYYH;7SiWDD<0HwJeswV~&(P9$yPtxpex=Hj?UvQS>$cNU50P&@@HFU;$iYWWOXW<_Q zvh1aI0X}w><@V+@Ks8z!525aO!_8u~XWoLX^KCsIjma<2ZD)(ci>`O!?y=c_du4!s8 zW`_b5=*6u)gh*246|`tG;?V7qtkJ@dy zQ*85+L6~%fSL5!`s(f#FbOO#fs|?1;YQxEkQn#DnB`hm)(^|#+Oy}1xt*+N=EX4{8 zq_DwgrQdFS{k;tw?fNyQ+k5I&`+LJL{i%}7sQvFTO(y5N9W7gVV|c)N{=;IQOI z!3DJz294?`Qpv`xC)pF|4KGgy6ZGl|BqYew{SbjU=^$NHj^4K&Ulo+t_@4`2Ov@!w zYFbhOkdIbu)~lY*9QAUb8(I}Z5NO?sTd%-Bo?DnZUfxDovTvjM{Um@8USl(me}@{? z)jrUc+n8h5TN&x3QkOgX1a`!ge+6;x-(I_+Q~aJ|UklwFY0ewG7Rsk6Xc`Qw(WN)M zi&Je9M&rX~?PG8%u+12J!^^GGe$id02-DeF9Zw{kdPfSG$jXXN@t;q8pD9jiX-H+x z|0Of8rl@kivmCPGIlADS0qmE%@YR_xBpO^19`SSo2_Vs_AGYNkff!x^b?>2Ws*2cH zg!M_|92FH+?}#9fAllHyduvc#Mk2}ao_ax8jBGx7Xo%tfaQN)ztpwRh6rIqkyzBx0i zY2!qGJ(30Aa|WSBbS-pYi>$&=AEOeJxd2{1e<=rF(-WsZXrMBuj;6V}&CPSYOXM50 zmA=IC2t-9C-RMl!Z8yuiYF*o!B8MmZs>ODR5rurjaHgU#H_2S^sec#Gz6ZdFyhaym zd>9*!j;>%VAL=>?RntVbH0AWkl_$wq5#-5A$vd44$;7M(pg+)zPZA|FJEJk>@T&R{ zNySsx_oqIp^5z>Mwrz;?-s!1k{grtshM&g^an{6H;b4BFh{a({ZNzG#|7^;oxn>Z8 zN962%;CCf}7xZFx!%Fh*SnV4x1i>d{@~jJs6_co36;VYkQjEgv#%pW0){eHK=BS>M3ja8aG8!X&KKef{k>w%il#IsYWWrJMaR|u|L{U5v%EX*ms`*6$ z;oq@V?3=A{RlB8SojOFqr=NZ>n(bN_M=o)q4dJTjI(gHzRdlRt8@pFJ21sKaTe$@M zs)iCtIm6l6U3ekS%I_VXxwL%e-s9G>LXX7L*Lt;^=fN~$s&urgOskd!>sI=+fs<)Q z-?>*_c%tED3J)=r5aS>sh^*^KxlOEZ{T7_Exss#YD%tVfQ0gbhoC`Z)X8l7WxJ^2Q z7O#k@`QV^y>j{;;SQt2B0=8`2GB;=So>I`9Hy|*Q{n=-xs;Wgx)jP@Y7=!rNyKDSB&Y|JVe;%#0bE8Q2&E_y}{F<%OQjd(}j{k(!2A4h{Q9 z$os)NWL$X!?$Kz*Gob?FvdDGkc6@zmS9--JnF$Lnl8}_ILBEgXr=EPcOq|56Z{vsT z(^KB(dhy;4^Wy(BJ18WFi+r8kYu2oxz*E~sH5`?GI=jBRxCsfv_Uhu9w5xzOzIi&d zS`Nsvn|-Ogd=rK;z*Om+x9wkt&`_pPHY-9~5h6l~shzRb&?ZOEOD~nFtWU!{D!E95 z%l9xLw?m~xkf@PJEj0rU7hG}I7fkyn0Sp?gv>STf>=BaGrUnsD_m?_}5`owRInA79~o^1)fr zcXzDGX^_=&A0wo2wIu8E!Vxre!iV7%r5*ll{bF$9ldP?Xc)xLj__&u4)G+@h;HdT> z0hLeGP2T^T@LR>mpuEoTh_~yjEe$7nZ~76w35{W_EBY-cEWQhzRd8-_8(+pEHcX)M zj2(8M zqkVG;`cY8+wG{bo1`~Adq_N4$^*P_TCsfj3Cj1{kLm4wM0rnsL6nCQ*dm4o$I-n;H zb$4-bFlST!x1sAw=G3W;2{Hw2B<}u>0I;@h2Lq$adpxr|phrXSxfL$hltW_tf54!s zyNM$@U)jS%@lOqX`%!2XVFiewU%E-8XPNCo0bdV&_WbLsV-1#^cPjyYA zc+c%>@D_K1!|5X6M(*SnHA|L90)9Cz(#7g`y+3sYl^BRf%1ifYE{-|6G${NF^`->7 zCL!rqc~cy@&@VS>f`+;JYl5}aj_RP{ed-<$s1i@tF%yEPZoz_S-xK=XW3VrySKC3D2kdqdcF7q@gM!7tb!BC97$fzDpS5ISem6Ov6_eIX)&%Qs*4Ic_@RWa!>S`SO9XAem5unCGK z5ZHbFF}A7hHZ}4G=`S}!-!JZqsg=#@qYxEmD3hXgYv75#!r((&PQqO8^ND5@NH8{pA$=r_MDLFiXqhulgv8UtRhRKykmWh z?TqDHFX#vBZq4wO>S|!ftxR$#BbtXIJ$oct8GBKHVMD)6@Vda_snPH6{}>UN737?4 zDk}XNg95SrD>mazvn?%`j>{$icdIBda!hVy_|RHEU67X5Z;^mNi=v@&MSxRgJdU~p z!g!AMCi3T-o8BBKA+@H%rrKbIs?2GR1>S320!#@S}1xk^oVn)W>j07p}1eZZqpuib|r;MK2MNjl{GSq(H-DUkQ=V z4DV$scF-t~Kw-ykKh6P`1-+I{7#Jy{099*U`^yElZL zO2$Z7mom%0CyBtzOwGYRf6EzTV_TNlB73q0nUUJU&jBCZ1U~aEmT7^~UQS8n!b(ta zio@h|eg0j1EMNY();|y{WeY#g#N8II#0-2gdn_1Inz^(*qim>ct@EH@4fd?Igq_5;%4C$fG=W-%q=#5wqJN2bAf zg6$T< zc(Uq~u$*82d^k?{MK@k|wH{$x`pP()-+cl_eTINV1f*MKYu8^mVsqo1;!e(BA%u5qVJ*w44yc(`!b>{_|b$L9^EVM&he zNW2QAFgKUi1{F;qUB`+Xdg^L@e+;}sqF%(lgv4P6nQxsQVGd&9zcGWb>gveLobuGd zRduQwxoSj6#H${czPqc5rKvW5!GV#m1?vxX;4f^Mz{}zx#wrMKv zIX~2s=bEJ z6*)o1&vbKo*cB2C%oNqXR{11sfQ4v3nO&W%?gGo-^Ucv9W8-Cqk-eMG%}wOnAg|~` znX8bBEYoA`l?R^|?)zH|q{L#3=kZ%zVL+7#(RcX`{mG86_8O$o*xp0;;v1o9=#=&Q zpdsqkDwxqF=k@jb7oy7#$c(|G_$~xMXK18*6}lBM-2Ug*?~=fIm+CuF^9jZbcYWVt zdI*!M-+|c9(+st;k~2zYl|TC-T_4Ohg1u-X0sJik{#p0&Z8xx5pIx~5cO^E~`S#}O z6xee44aX$elG(Ie?%L=5gpQYzU(#ntO2+qBGLLPy-IoCx67J*YXFG-bNRR#Y3qkmJ zIq*d{UGm1tqwp)ZWB}Vz-nP|mmC*rQ1X|`VZLt0mQ+2fWGPp6!4Z>OwHdc4XikIWp zOl)1umO|RjnnlF>rFM-R)3z0|qsrg<-n9}$Fccw!5uu1CS_h>i_2t}ZxWg2+6)gkI zHcgm)$`MP|I@>Ur(T1o@@zcMqdp64-WCKMW7$q;!`{t|^ug;Wi7V%|{3wWZluXr=L zNzeTQOfa4!u{1R&L76I*oXRi8I30|}f434_d8~=>L>DOrm$Zd64ztv2J6=BX==?b5 zE962{+0mu&h>pMys)$NhH+o^oerYUyDh7?nL8;V~-`Zj-&;9MA`Bmk;_im-=SaOnSh|2uN-?)7#bz5P*b- zu}P9-fj27^`4ZjK=GrqT^8|D}JrW15pJu;UMGNieJklTXp5HSzpi^#CII4~Xkq>I{ zS07beFq^kfdq_~qm2pK1Y3s0Xb4Pm;xZslxk>Ds4@ggu5#Th(S>%-}{HNq7H{W;Ex z2%O_S8d{jb6`ZIPg5L+rs2d!m7;~s1KZ#q@ohW7s#-=U76u2PRTZGD>2Y;O(gxn-a zO8!wCW%Vxxb!mgpgWaS*i^jA~9-PD<5k#GNtOLV3U{NVHy5BsbXIC?B((?*==GVrw zvgu-0wBWMPY@f#tt8x$Y2FL^LiB~_J7?>f89!c;_NT-)0lbp~$i}jtl?uX$n4Kvxec}6)K&vJ=& zJDg+O*bKWTSyMCkr4t1lA+}5kykCAW%0<}13TRt!1tq*`orTRbcXXYcN!VCfe1ksS z;~s{uX^vYMf0AAlSo(e4;V{xfao-&Vjn_URp}W2M+j`fL&7Srz?`(al*HHV|7Y7eE z2`i3(jA#xVQQHQOgwj~+R-*&;*P|UHIk5Pb@fpg6fUkkWYDLS1Jj%r}t9tBPdBRfH^ zu~^gIPL*V-`90=Q>GRUzGFOPV)-#h*ylKT(WmQ>4mM(>|SE{f~D^vD)zn03S8WI?Ge zN};I=^;Rdwo7Ktah8RP*%FtoGpQz~mE&p*&#v{vMUf~1k4zL;2&TQV#b~U>j_4r>u zte!e&J>xZmz1V&N5}CR7V;Gd#GX~2KOz#=bxpx_kxi3@6*2H;J51pP7=f5&_pv75h znB!7aUNxhqan6DZTbm<(>C++cnMrcTx4VuU{_<;Z4jTviGRsGE?0p~uL7EsaHx~4N z$Tw&XnXL)I0t`%*l>`#UUeSI#)d__YCbGQBQhN1oX1aIa?u6J-bo`>jL~;LjX?|B8 z4g_yLs22+tbaY`w{WXoZ772BK(;_IHj?Q9cSuYk>G3MZPr_))?bP=V&L;0|{-x2se zfM0%PmYJj@P_fiWlMTi%tqrzJP{g$$`HN@YUp^9wFfJ%yDreei?F9z?os;8Zt zI_8u+ZtqG&<9iphf7(n?`n$Qw-5#y%W=)PfpJ_d8I$4mIr~bX&;rcoXstT-!eL7P6 z&UT@3f*8gCir+?W#=oxVM|X?=Le$A$C;kC@e}^drI_Bp@>050^f~?(n>F)=Q7VGu- zp`J>I$FPRISVku6G9R-GJmxd7)4Bh=9oyFx-=6~muy4`>f4Qak!1kJ|B@KOyf`fUY zm)3{je$aELV^Q6n3j$?+JKG6X{>)ZOC|u7cd$nNR=!^uz8o$7nBxawAnhKF=Mwt^+(c!L0;z7w%UJZgj#RBdn~ZIBVGe-Q-=BD! zF3lSoXbW#?Wi%$P53KW{%htFrG`lY-bH=vTR#(KYS5%FUk0Zs--XUT?(w95aXASQP z#q4yy^%6T&wO%&-WUTzYMYWMc5S`ET6roE-V$Yk5L1uRNhHZ=h(%0wZg)?AA+KT_o zIfHcRkKnXCE4pbxiRBvJK0Vy8jNQ|R4gz{|=#O*rDgrkTYWLke3$FM5$8lSOl z-C+!pba~=?>N+Joz?fj8qS*G`KmN$(IOceQIXa&-3K-P4eo!+c;{_wbj(SV^O;XwH zsg|iH31E%U(^bIcb*}9n3>WkTm-OWHJ!2}9=^zp!jq&2PqnTxYG($9DrhNAWQ6YD! z!dkH|^<6i0R#1H~tHvsoI z%(Pya9^=gPqoU@(F<;pc85qoxNRpx8ROCUDn=Y>TrP@aOIRQ98dVZjPr@bA5x5}Mo z(JA74)BCtCLTS%Mh(?={6(m!`8`78ao;krz+4&2^$0k@D7h$$XDJ0FuPu?;e*W6DDIu`B0zTnX5(NPJ|v$7_JmP0#)5UGpyCY7d&eQL<4|OXt{=(Fff$a z2%(&*vww9TB0+Ffd9RZR=WJ+APK_EPF5mR% zs##cq8DJPjS!X>flT|`j)gdus;zb&#MJZ7xaM=bhlxYQLl#o+0d%}XQ@`-0z*r>AZ z2w6X(!mn}f)jTA>%Riq5=(R-LEsIi5|3-pY)uvyPcfWWkCL~VDE4oH3S(oHilAt@U znA$7tN~$#)2F5z$cKw7EmGmhiZor3Z1KaE=^C;Eh%Vvq_gjNS8w09gTDrInP>)62( z7)BBNovo4H%}DwDkvhw4aTO%GHC&z!j46 zY+9wSQ)gA6{A$zR9JE=a%S@l*laY~OkbygtL8H1`u?K~kYFzqROaCOtaDYyWK4GAO z@N<+{=^F^)iWBA}~H zoM1O2dr*P+zbr>aT#OZPQrQ~v8I58hdl(6VF4|i9FKyw+wz9SC8>h?=@l4FJS-(b! zCqF(}4K?#IuChV2b$SulFU1JXdALdcYEz7p|4aXjn{zG^@pj_r-`5BbYZmIkxalSF z>Y&VZR3#S|0=?oA^4nFeM>BzAwN;#ZH+q8y7mWb_%sY>_ybVmXRY(rg*Ppq(+#Q*V zjEwxBrT}~fukSpJy8W&@p6!2JkVyS|cXQz-p0I8WKR#{=9$CK*!7U{Rhqn{TIZwG} zm9<=6F4$A$4mx}JL2n!0r={Y3k*R3{?pCY3Eq9|5VcL;ltAaIm zil^CoD-G}QmvM1Pd$rPY7o_GpoZRJQ`(Og-bU)O-p)yru*c2Czqmw&0Q$rKuM%tyq zsFeqhyFXg<0^}kkKdJPBg`p-Gg~Z70qHsb%QCTcUd;-`fn(v8GpMrmcS$x71gl<{K zx?EADP!j$9Bar-@$o+Y2g)~e-L3&^i`O`{AQ0E8nv$5jFmvW%`j4rsS^>&7iN&(s5 zzC${sPnbCa>Kd;jZHy@{f#9eo6@(}dN}biWt5LR_suDv-Wz9v(OkvV+#6Pq90|A*j z;|xq5dzvq0Rz0{DEUTPKkYf%AcpDgr9Sdou%nFsw)budRLCiNoO6e=ak~FtohSx-S zPth01HZn?cI(1^4Hq3s_!24_SS|&^(CW;b-z@?GtXV)zzfq)k3Qd#E%d|d6t4*=gL7y(o-SuFsobX~#`v7M!ip}EWS z1$X}a^0L=eM2NOe^`Gj=kHutNS8N0KD<{!zP4gp%8PG_-z%IXuVM-aX6;8eFsNAeM zq`2kl4_zi_8{r$l5b4#~GwYD`3JLGCozG2;4zTz&5A4{>WIE+^tJ~! zE*nr!A_Ds5Rm3zbmOPQ1B(swIM3|mC$>U{aMUVDKntg)&2pvvAFb%mmx-Po8g(+Kc zpM1)Pus2Z(D<+q*ecE2qe5bh?G|JSa$iF{$qKIU z1nFl34!rrhKjSZ@bySeOd{sNXudH^eUd?}fQ>EQ*dnyY)lDszEMn)-OTv8FRGkuut zYLK?)^*czusWl<=5!fOv{)2c!GumtMTnNUpi^n*OSR%RMWFz=2O#w4+PXNE~joZ<2 zZ~NgwMnV^Hpo523PY@dYaYP?mgn|_ge|t|1TsdZPbU9`?j9$4d2VfEdlx(S<&b#yD zM=x)GGGXZzEjWX1^?vhlllS{|L$M(K(|5{+Yek2N3>|y?knAJuTP9)T>FLviLGA*I zbM2RD&v!H#Dk_Fx-^q3{Jx2{0xt-S z&+0*iY6E71>(d&D!vd=`#crIxlYJEep? zQu5EDc@Ii}GU%Mpf}+yB`-AEXg&_S|LcD@-jl2>JC^6;UwwM;z_R(D`ZU7ekgbaDs=;&*2Pfjy5 z$(X4`hb@`E<&Gst{gVmu|5(pG zZp*^M5|_hTmGpTU2N~NG>OOB63oQ2PJm%uT%x2>a&{zX+7o{O5u^UFx@5cB z#CQ=ygz^0~sA%>>yin}+f!f1zXiW;TJ9II?bIH4$s$8DY`;gW|DTBJcxKk}>;9C)d zQ-F$6ixfsFgkj(O$8tIMKpums$eBK}(229d)}F%uBo~3OtwBQLSp;8 z04JEFKR}uaczn3KyVK9+qrzfghXt4{UFu^YU6UqbL4VA=I51vnCHX{6n?L)QBpkKe z^O{WB`rFz+tWoA4_ZS~4NKik)UF zP1lLlDQ0xIvbfrjB$_HMX@h#&)8>0lB|e<62_04a7AAq9*^;P?wxZCei9a|boTLIg zvf)OKi&=c>X4Hwpx2kjZSQ|TsWz}@}WK=_)xok_S&GrkWc<&Y$+!q~nv1qKB7=H4N zy4sc5wzga}J3OAI_avNoXKe-Er<(-aT$k%-BaUu8x|%`1qSCA=P+Wg60&*p#yw1ah ziHXzG0en4O3qI#UCZ4`XZ4LM@E8*dWb7qg z62-_>!@a1UYd%%g5(X84Aw>D>v(pVPu|VHcBcvy%TRKr1#xya1P1)rQp18kbtUA3N zFtOLFoJ@`3^tOz;RV?*qZU6B#n3nv>0=DT}<~GqpegW~OYGeEqvnTpb`16yO1y;27 zs6F?~Y0MKiqGIHVJ$^h9Y)^N(M^7IfSgo@y2r1J04p4cU>vm{YEN#XbOp|88N0KcSbc|BIO-eJZC&&J59o@XJlkIxVYUMVg(U3O@yB44H|U z7x4Tf1eulzeCm^jIkZ?US64L8;tQgyScvDA%>kloE=uQO zMm@q82LLE6I2o9Vzmae*6fQOqjYZ#71YP`dT+TPzbS$WxR{2Z^RTh}T3x3V;d54!W zX`5B`)fGTj5rwmxQ)r{KfK7R82iYdI%vx9#>~nqk-|NuJZ?%(6>_u7z)reR*8T{0e z%kOCKy!&_xq8sf6g?sem%@*uSwIdO-p@$u|1{W8B4=s-Y zlDPn}{tUI=KOT7X8wse!z5(|-dz~^!krj2;Pk`ZpGUQ!Hz}JJeQEvX_?u)Q}v3O8J zttw2To&eg6ZtFm~es^~6;}4k6{k#lY&Tm^B;egk-zwu-0Z&m;AUt*TSa7&v8RZ{IH zyIxm@S=$bCJ2X1*C)wACD{|CC=`gC2JoD_=rQmnxM91chTBTR*XlCBMiXHx@X5ki?es86LHmWI zlsV2%l{n<$<7t8N-*Va;+<-fSD|*@jTKM4$-&8&O|FKK%j=rd_rbXc^^D+ig1!)Xs z7Rr(}-rC%cA}o9Y79e!)_h~PW5~{Y0i*_-{hVy7DWKj+yIQ)cF_47gz(ytDwz<1@A z&oivPFwf^QddRv2%fC|=pTIihj7vb6;j@k+zeh%BO#gD;qsIl+SpQ@=S>d_hBjEMZ zPitz5z?H-_E0P9^8WRCX$N!SOp|h)dwu@+8%!Glrd_=ml1|YqTGy_vS0&Y?9-0`NL zbK5WG?5l}Jizyif%`(b&h4Pg-Gsv@_GQP^-1XF)W7j#&vJn9}Y%0qi9uK7Ae4ub_p z6M=w?u{q*VXbPs5aj9bJ_jdu~pQGL9MS8_b=;$!sQlXxrjHrybC^YUChovqwW^vI& z7tU?w*YdgspczT>H6c!bI1~wh(ex53jKU*dg zJ6sy&Fq+0b(9ef_0aUEaxl;*lz}xK?8te^HnqLmo0C(y}N;#(TfT~E6Ew*a5P}FC6 z!Af_Nx&1@`wyucGr)dIGdDgrZJQ(+Pk>ew`=OdTtfsZd9uLB=@?oFipZ-8og;0F;? z1+2u?)8qLJ>SvYF4*NIf3zs#ukEr~IDsVXQMwPgFN7B!_W8ZsmAIN*L_q_cDird3w=5;JI_^uf5;%mp?~)q5|!l z>%bPQA{-GPhrdl&1o<4$C%lOtMabbt+CJ5U!_5l`-ur%)Zekosj7Ly^VlW}W*YdIqF>I%+MO<(YwV(N@$>8c;?e9rURrFuX z#VfBC=a2^{T9>n&9JyQBZ}e034eYh-Vk9_%p0d+sDwx9=GBSS%7(E5!gMvvQENL9~ zfFOlbYSB8bdAfTt@{y3o*)2&luI>wLmM?y2oqMPk5~|>W=*$&pd?-CWQP>a?4ZV`= zu_%Npy&CKH`%?82r?>oFHX2_@#XKL8VJkUr{}>S5IiA`LtWk6c!-;TofiQp_B`uz5Bf;wye;pX90NBWo^AQN zjJG74E-ei7V{gwRKbB1mwEfPTn9sHm)Sv)z)3Y!gztBU2#nM^*1>+IGB=TEi@xtHe zyNZfDZ9ye23GjMB|ExC-cm|Yc0ZlK+SaK*QGmA-buOQ^7CE6o0Om>%~O(s#{?bxc4 zea);MKv~sa=yt3TArlTw2kOf&FOA|N_`NtFSglRi>(I&F_8%7k3st-wLM;EQ%>Kx; z^16Z^`}5|HplHkjOt(I=v#8-9*!1U5NlCi;)gz>Hu^l&4&7W`ebZr-_8&yl9j}XXp zjbFOop!oy@K-k{oaa$_j`fzTpZaMJ&29O80|4xCTt3ZM9M*#WO<4mi%d4G6;8Ydnw zt;+tHUN;_ia}DrQnWGye$Vr)#wn#l3iyY?mcik8wNYHF+O2O%CXlOOUf^>k&fAOMj z-1fq&+t1g(MvJ+cLklm3r)RZNc(66U~7hD;pwAUDSr8{Zq*id7?u-82^Us6@U zAu&L85#?n4uclvORdD*l>%tfXzS__unJKF$9v_~$-$^Nng(M*n6S^Pq33@>XXgm|E z6@2M(a8SZeZ4itl##>KuM-vr4N}If2{AW`1WIdp0APf}TR-K1ci?))Ueer}TJO7iD z`5!cE0z^d7{5INM8R20pig?!nJ%SMBH)5q)foHtKtRdh^$SphN43;J-BTjBF=zrw_ z)4JzeV^g)$zbCckD#MNKMPN+AbQ19=r3VHSN;WP$#K4rS?A(|~n2M)g?@}7f=XEsL z6k;p_R8_Gg*0RF7gVX7f+3ZY&@hAofSO3*!7<4j0I2f|hiD2+DAUM&m3#c*9{v45YwZ{N0ruENAs z3Zz2Yp*cZLCfOo1TWfVdShS}TIJ$e@jG3~Sb6Vx8mwPh|IdfRiD_Sz%YG7iJfJ8f} z_6nnnI_=?0IW4mn_-F;$Sz6H{6f&dwc4|aTlz*!MP}`B+VvGG(pe?s@Mt=?vpeCYn zC%?aUIY59OG*tikE;)P^B}BYlk+R36GK}0>s+Z*6yLibv)Uo}cC<&uvO@S~7EE^~~ zVPn4xsMkHcIeVjYc^HlN0$@;nv20@UoN4^Sn%2(>r5^vl-%$}mr-n`$l3k0lK=VW0 zG2N-yD49_N*_hw;6=J>7u|f;CToxrp0AzXBHgQSw#y$vq@;8~?yl*#joNJ0&3$kum zXy6%XUh2NP0EPhibgDwgGrqX+wyRHcpj54~^y~h}^8MTMaO2v205KQMUXI#L2byzy zI;wVnwkF`7Wt8&gwE~(Sqtfhwo{3v`Zzz9J9O{?OtUJ%fAX5b#u<=f8_Wh6IQ-Jb+ z6_M-H-|s%|=L0t4+v&$s^Ve>Yas5lt!8Bho+U{#jifX(2@xxllD6v`TeC%VlEDTlk z?Hk^{pKY#nU#LRpk?FEEWuTO9L8E(L=gfIaRlCHV?x?$gI8Aq|826nV*7*5rw)(SH z4>t73Al7IP;DL{4nu9p9^eI_|@%;pSFmt!ax0Vq3WL}!rJB&o`x8LMBU<&c>JByF; z8<849*vl;0Gd+a-RX43!mFhg+h8fR3zEdjXp~Z7jCX?|QOsxd#tE}vPyNa;GD@?F& zl_q}qNTYr0vB7`~<46-a4{Y5jX*bCK8XIm6qbI?0mgYhnG)akbgoamu3FFs;lim-0 zK@Xw;3EEY4MCw`(2#W88m_NFqT|uRlZ?z)Q-%4Og7R*W)UK1n^*^+7}`-8PLUbzi8 zlg1)~IJlBK=Y(>Ox!6WGv;?fQ6)c}+4LZ}mp&1HJ1n!gzBi$i0`iRLkF$K(-mP^kU z129W!Y?Ef-ik)o(?)+K%g-f3{!{+uD_A@t<(Dcjwk#lw$G(E(YO?!^o8BpRNM$|7f z4jW^mL2KZ+a zUHj2Ow>^iCxVoOfLi4Qcf{1zpx7F3f{dWSjax##86B?7CZvE@5DiN9t=RA0N5~YVl zfV8XvalA(>8AKMHfB^9er{)e$Ig?l%#XSeawAo#N4zNu)JgEO{9{`r0H`{&x+if^> zwz;~FQ@Xp{^FTO=qFF^=)Jff`Sba@(`;HRI9SkmQUf7Scx!OI&C{At+@n9H%Qsz%N zss84RV+{0b=sMhQe%r=#+fX|*93e=d->jc)n123v6?o5RlHaqGW>>Rd!~KdtYbFB_ zWNO=FA=x*p#{0Lo=bP66F}=U%e&O-ow$!6D{orHb&HnQL1&$XdCt`6ZzEA2GyXS|8 zF9L%DultGfC38nKU{*$NTO_UEXgtXMc5wNY9DOi2ZjA4QxGzuw8MrS%>VFAfmRl@c)P4kwER`0Eh>}qtsS2YDiaUQ!J zSEQo1OA2|eHuYvwkeO=Ua4gG&foKjEN7YH?AQ3MoNthdn8o)4$o#_{Ugoz9B;;vyh z%E|nFIZl!aDB_JJ^Phj(*_%XJqGX?Q?8q3u^W^cxva!+>v&h;uCJZ2cv z1KH#b-ltaxteZ%Mpn-s!TSEGj&_d=Pd@80xBRY5Mm+cr3O7!02DWdNKshK=fi&iQx zt=U7$@aeyEzr^TO;Pn00y=AV~KUDej{LQ_E0Lmd~)5DF#m_|C?a{j?F!8Ygg!TwHyF?GSZlJ@r*POSb6#O;P<*oJZ>-l ze{9(UswJn5_NNzs4ck5gN)W}ymOROM4Kwi3l(prgr`Xx8vxP`%bO-#NfKPi36P2|) z7Y`2Y+q$o(zqL>6&5&0HlS~5VDU`6(a4UtY%Mk4ezqHL$@VK@ z&8jA5RCOBGt#=bz(U#*?`0VrlwRJwBveApzGdN={P82;Wb?C%4wo$Gu-t3dy!RN(dDNO~PH>ZG4IVf~#Y z0P%Ai+1e%t<&1C7&82ra6n$Vkgh|8Ezv@|ix2GSnq22agYOyy+I}>xPInpmJ%g3N) zw)wdNqmaL7XXctWEd|@;zo_G?X=-n=a@=QMU~dx9wwonr1hu`c@fAS?7p|F!hfc1x z1w*akSB&U|+%)y$(FSzX$=8_2;sobcWi7}>2$*dzuiX5eMZpXD>qhzg1GjCrj3UdH z%8OOs3ot&uhN|@PKuO ze6tdN(4J-*T8hqxy}KCG@5=J+gQZ~&0fR?rbUg9x?r}Hq-sBuO`k{6Q)3lGDk5S#X z(Csf8E6g(x&J`9K_$6)NgulzIB4`i!HKe4g0;OE`?McukSS=a{?BdP(k8G~y%{A1R zH5UKGx{m2%OakXCH-!1%T6e z2`>U#1=Vsty|k}KtW zk)@9?lN`kW1isq#nOk?Kak^ETfA%)833#}UWSll#Y_{*<-R~ByR<%ntvaEJrysXlX z@Xi^7<}}8JG=6qp)?Zw`F?QV7laJE`Jw6)Vzx`_eI+;D94S{HCmRQ>(Pc5x(FSBhg z{|=@m5XboC6ic5x_&7`@c^0ftOcUGt1TP4qK)gz`M!96{dBVJT4Z_J&(raM8{N~+k zO^Rs3FY_9w%WZtvTNi--Pu>L{7XFdANzM2)B- z(Is(tw#A~o9^RCYdQ}j4O0eEYnl5H<2I32op1Ix(7@e#*rG(BdQMLXDd2ZMOgvbEg zRF$T+Z6hL>q~*eEUKT{H?jGQoF=BgmolWc#mJoQAWYj5uP^7{Bb_}3Rn&^L6raC>G zv}KV>3uSN7RS4Qzd4QNmS!B=0^_T_T$L>g)e$+Z*{EQ1&{9SpN<1B5h`&+BD5+ZMH zxTrKE505rci{}pK*Xbq?4zOay$&H*(x$6MY?eWra+93riDk`8ARY%j>#vRdLc5>0~^k5Pnu3Gi|6mCcces$~k!6N~?u#Gm-T|gbrIJ z3_-Op`n6oWme-?vcPDB^*DX*ig4(vSZh1LpORGwc9}723iQZgEPTx@^C&Jdt^QVd5 z*~RKZM3hPQ)fJA^6-mTuK%n2_sz;9$12GDk&SgrU`1>t{Kfd4gX=KtA`0c(BLBAJ? zXwH;EIlXRDJ%R)WmsuP7zE75n)clV+Dl)}iY%+l8H4RCI8U5PL-Hh0C+u ztJi-fsinm~6j!{Cm6^fxm&Fe-1Bt%C30=vhM+cErHjfs%vw76OyDhN=g@;&NE2ocJ zt30YS;+E<6kCzWc$4!=2;k;%BS187Nw^RjmtdRrn?+PG*>-iccqw_KcE8bF^atf8J z#~h8US7x4=$(Y?2hJ3~#Bv89pw%4TY@B#PVT;OANvS2CNe99zPHZO+j^W1*+0x|K>t`l^sWw|Th zY;Vup*WdjbsQk_Ez7rVafB>QuB~+_t^9nNgu$loW56-MifzHRfQ4ebU6jH4|mq-r@ z%Qn-ta~G`x7NNp}t@?3iZn7xgf5T^GZsL{Y&D$1fV#Z=@TeHUO!gD_g3y($^2yJPd zaknW|PA#d)4h+ICV^sxRhhFpTb0#mrTd;ab3mZ7RX?Z5>2G>7w89J!59v>H+NydUS zf@Md_FsuhJN6ge#8cJtB_S3vZKTr@FaY>S>lF5y4oURFfhnkJtF_g9bEOEJcxLVO{ zO&ubd) z6g;z2whpZh!?d3ndMR#vH0NVwD*$7Jie9S(^LwG0GL_%`qs9!(y?t=!XSKD@MQyf! ztMEM+aaGp@JI_QUtH)0Kd-LX-17exQBjLsq!a>b5rQ>qM?XmsFdiQOd(2Vc`CLB=_ z)(f*$h@H(%>C!6%?)3q85UZiQMp@e3j3X&?Q+sl^3qH#G+7-R#C2!RkT#BkpnMn?WUI?4umS1*s^ zvR?UKJfK8}_za=+WyCFvW$Tq&=J+tq8c+57R~_Bm#tF}1fVrw}$`;pxAe}0EBqqjL zsnqb34Z0v)aEKK1Q?U*JWg4KO6B0E77@$)~n_AXfa!K8G0ZE z<)c$;idV+h*b1?v){}`O2@S5=%%LMp$TaN6vi?{x5PZLqTF7|i#6{HF9^u4){-2DM$;;x;Q`aS3a|8=Fm;1x|vI9~E|zr3+~P9`9$GH0Ph zM}QGI+^MWm&0nL(Ld`xMMvD%d(>A7Xet!4B=i@$vxEiI&&MNiPNbg!LaSdh80qNLF zV$ZM~6{t`Jdryf?AG^0zTRuKdrdL=y0U-QE`94dxArP^G4`NPC~8}Q{q17u5XlA(qqL0nw04Jl-f!!igyZ_n8 z=sE36C2{R#o$jgWZF+N^FaRQ9!HYMOyIYIqo7ZpL3X>er!ze*O_6`h&dtBix@T z5is}DR8U+)-rV$94N6X{G-tEZ>Z^sZ^>HV=@B| zXIK6UkFy7{u8y|FV`9|lhD24JN#KYsk}5ToP4YNG*YI_lKzif{42j&rCVBV&-S_p|_!^Jb9|4#b}`(5_vYUaPk_Hx(yF zPux3a#RN^Ro)oklc)ZO%_o%W;aC?04`n9Z?E9HCoPrg)mH)g+SXgy7z|3hSiU8S0} zU=y6lA?~ZL<$H_Z%;?OJC~Hn&kUJlr#Q<-kYNxnjd^|cUCQV4M{*Z5MEcIAya(6O= zBBXcNCSKlsjqy!*<}fAqx5%JSJVp>Dza~?B_PN*~cu^IMR{~5tGPF)Q0%M~PSrhLF zrMC*!Ox@bLI@;Fzeb~a>vO4ct(LruQLqJOEwhk(1-2|T8W@Y>Pu&+^ zYDoRLwd_W>v#=nO28^6l@hm&wSFXT9o3!QV+JrQSs#qO^fGDsyQ})acGO(q#@UM9~ zJkI~ii86|~R>~>s{Zw1QZ6bHfLHyd)%$~BTVCrmYu{XYZ=tgK3cZkt{6kT;x(_b4O zJ$lrSfe4IJP+~Ml4-j#53DO`jk(Q3pBL%6^i1Y{%Mu?QObT>#VDJ}i(J?DG&|IWGR zKF|H6lj$k3=;!OSdO{ZYOuVr~P>LmKqh#$RrN&_4p+@?9F>$kaHV=nMRc`Hzshz5S z3HJOrhB2@bUJel|fa1Hye(~fqw%y+UO6StFZxGPo>y=6z<~%cQ$>y?QDy*l1Idx6H z6Wfk&ySR<@WI#2iW;}z6$(z68YF@Ixdn#C7@wCvkTy-`^#qHQkcGXF&Gl@X z`O^pdl&efw=$eCahyX-?FyKXp((@`x(ko|RO!%!&vMC}(4|MbMo*>EhCyaVEC0TAFU!vN|9HxJKTKWqNXGkIdG38J0XyiWc6=ihA z{>S=8d(ipdL-6fc^WphK>nJ1JdGqeJ{Pikz@bPE1)>qvF+CA$JN=u{+gl!5n)PxDy zV_0C8Vno(d&`5ePY!?)b8pY3~#Uqjbgj9-EH^Q3Wl1NY=kThY?2h$(P0VyW^=tR93 z%oI54bZcuU_;%&4%{<`r<~lFv{88K0=KZPs{k1Mx{=*A@q09f@F>QBt0v?s!{7)!; zZ5?Qrr|N#VZpNjO?MtRLq59BkM~w}%x#nguj!EU^a2bjh8rxDvWsqNly6-s$# z5lrG1j^V-oY9>?C^tFa&+BARNI6PR&zvWclxW75AkV zAh`4S4v?_N-`Er-ICNQKN@x4d0gf$zr&&uq3jW}V7%tgy9OhwalMc58S)L9Wpg z@JsPRU9t3!_$vBeIj~9@GrCH@%*ZhBOkAQ8Bg=g({t9%&#!j`$;OZs@&KoEal3z&bKz2w$08>{bxiVj2A*03$4LZClc92h8uKOLj?ijCPX4r>nU0W)W-Mg zH~=ncCGhNADC+F|YF#Vn3|HTETq5uD9@~`Iw7fJkA&c*1{TC+Zb#fP}@Vvo2MGItv1X@dFH$&C|n4eod${TAL&o&11Kfs%L6Fv>eJTED;+Kt3p5N^zQ7n`oSa(h+a z2BkiQC_Ga;XfT(^@7{DEcB*{bpyKol9vw)bNFX{zwM!sg!09&;i$KmR*rm%!*7*Nf zd>}y>0+3z(lIJ`$SlmB*g|`+d{^2P$Klq4)6scW=4@OAj`7Qh7)Im1%~if9 zBzZ=7Kq>pu|3hJPS2kecP3u1WyOiL4semD_f|+vYmDx=G=G6BQ=6)(GwkJ0k>5t7xZ0z2$f?6Ic4enae6(ao^n}V{CqkZ%pmtR}$j*KUTic#TM zM6Ml46Pyw+5<;|9B<21?8Q1)+r@lLMwR4PUX1+Df+xmm6ohY%Ajl=x0l4s0`r7x3n zHmq65x;;vvG#k;?Bg+f8Dxhz?Iiu1i{d~`27j(R_lXq($e1-c4v~n$OPd}&g+M_i; z;4KkK>95$MlOHBcl7?!3D> z`9QD0Ti4lb-TuFxnech0MP0ksbR(ca)VL+ZWZ?!Q|g*ZO#FR!mw?jW zyE|B|Jq#|`^pVkQHp|{xmrQ_uq5d)06=EyEv3A5Hgrywi0N z@GoI!F{UHby)vZO$RXT9S)vUPH2_Ca;-Ow6 zr0QSbGjA4uv@-Ax(qHTTy`tFF{jw1Hssq$F^W?VDll910o%q)%57YzrVgp@*lO8%| z;tl%ufI?A&9s)&ZlCoH}R4cI8!csB@GZ6bOR})g?I9zSfmW*(t!*k7a&&^sO=8G}@ zBt2xDXdTyu-#xi;mgQ0{kmoFc0e^h(jC~Bp_1$0GZ&}=8f-grb%v?UOnU6@U&_)6a zi@8TiIz44r)N>7Hr0kU!_r0unbj{+@ba&8{LRe$ru_Zo zbFGP0yKiljCN+o4?q%N8)CS6wh_dTW$6OKiZzv85Dato${aQKn1{4_U;Z)^(dI}sW zhLEM&VELJ_E4h3(cON1#j&*<-G)AxS4A8{q0db=%!qtl-_+$i0zZ zF0OCbVOq+*dP|9A!V_??9gYuL;}(%O~W&LtZDa?K%_0o6lhlW|2#(z7P|#c45SV()ULnWG}k@ zv+oss@s@q*O@Qdrqt#TdZ2BveucxSo8>d=tbkg}{6 z37Kstja6_4hxK#+tETVTj_Gayb`*;3Qgfb**1|q}bI5MTEomoBl`z*4ws_}Zv;UL< z%WD&eIO(n23fHG@B~*wYYq>ouK1%vwg%=DD#P@J(3&vpA5I}k~Xvnx3*Ml;dF4(dd z9pbqJ5Z~rd0crD>8(`5}C-cR|0fNP!uuTnhr%JmOYznGd(-dgD)sLe4dc-li@fzI} zMWGVjKO$$j$7Fe{2ttQ=L!|484@&Wr0mZ*EP`3|>0EO+~XxGeyi4<9O$TkHkgo$!n zir`g0_x29>D7hk`?_e~45p&6PJf2@LKa*C(?__4z!6QV=Z*HMnrWx(frMf$0T z%yRZh-IyjIAOcifh1-4vl_#yzk%U-{CKG zcZ(CLS~EJ9nxsl=ZHjIN3b38`O0XT6ugKmk?NT2eB>Y!wwTOGn56Itcjs%|_kuR_G z-7O=|`S1Qn4(Z8hoHSEQG?77}y`Tx6O0;AbNH1nRu?Jez@#hgF62uAGG+`$nG*EO; zBfp7x=!?1ftN<59W%?YJnL^KC82JZz{-WOFuE` zlIM9^GJTp`uge-YsM~*hT%kOgEx|oHp*FMa!*>}^73ukDhRO{ji$6*g1YXzy?go$jA{_9Sw;kk6ODSeE7 zZTbJ%GK6xW$!7K4#7gVkK__Egu%jK~>U6Fa2b8BfM&)VCEE=NFvG0y_0w!peLdMjgF zW~f%}J&*XQ2Kl#v;9SacmdM?q&7Iox<{<=d)g|b*q1uIQdF60rQ7+)*?tJLe_W<`A zrp}R|y5&kxISQ2@omr}p9B{V3;Ib0jC_fza)S~S^F@7d}h8PzB5PXTryWfdobHNpk z9~izRuH(a|xO;ee&*rJ8PNa0Fuq!9P!yy1&F}*S|8&`nLnU<^H-3V+w zwKsj}%>R`WsfQ-alu%hmmgDdS&lx7aCuro%|h)LrW=(foOy_ z+6P%|Kr{dfNOm!AWFG`=GJNSO6Ti+E-a(;W2>`(WNRua-M4LFj8oEa+#RS1^VIk(! z_|EPV^e-|$C!q40>#+4q$32=YFK|S4TdX{|U6?`!KRA=$6C)I(kPF$ldLgcNB-U;X zo)SjgxCEFyXT(b~@d$r50_AZ)>32ULYTeqS09&}e6e)EQk1m*&N}J|38;t_g%K!5( zse*%@@_c^A%hv&UsG0V`!w`k@jse!heXa_C>%D z{ATl)$dkT?Q#`=GL6+l-i8uH?vrB7B2F8>N3JP~@V}Qx^%lYFU=^#UcCk3%TP4=33 zzptk^p+S%9NM`zj*24!o13ro>+LKM4a zU;+>?LE0RG*RFl=`Zg$&M@%S@cbiN|JFCxB(HK)O=loD&-PI%bU+|s$-2*W~ZU!OD z{l>`MQQM{b&924GM(=&Y{qBAD@#)%}*lAZz(+19;DKoMg>jowkp=8l@-xMx_d<(`( zXvdl{iU>>sbjH&1982_kXW6!7{IBy~@)o5$Z5qT~gevXx&seQldm&V$H19J9aLUi_ zNAiAWqM*RLg%$ZdH-WR}tUvqvtNU^m<`uc7&JGtF?s~hp8CHe_Tf(2{+-SAkZ{a}8 zGBrjcEiDEC!D?^DbR-BAd=&OUZn^97_Cewfu^abx=c;S)y{lv{2*whr*7_`Tp|gXo z>Mesv5!jkJ(hY0kax+f`GmYO}LU`0HhfnblDJk=E=OqXm;iqOtJ9sp`#B|hhXNRg) zy@Sz)DFAtgi;5MXk$rBmRT7v={ie3I`#bxUg$g6z%o*M)LxR^t+A2&+WP^6PF<@kt zL}~j(`p8e2C{V+~*){dylr5ZqCP(u*<3!riBz)Sba%>8{O|FN*8#@sd4uF6z{hvE4 z!I*_5o{>km-12%$CJt;G04h{8x<-X#gd=w?hNLY5E_>(K62G}(Gpv97Q1xC2w2O-~ zlaCqv^u`H#w(>mu`>ZmujC1l`J%9*iU@>7uH~CBfew#>S!4t+rW8(eyNXgUgLajDR z*-+Raw6kjJ{R8#g1mM;lK{|gKf29q{pJ%-_M^mr$2iyxx1obFaFBrKu)=0bM=Kg)~ z=v_z?otCa)&9WTlej#udT0FDQ#rFG1V74PjTR|+*T%%o~W46|*BlRoLk`-_Bxk7`n z1D@Qzf~tbVG(mmQ7Kae?C(&ziZ(?svdKCHWRYGBZboWhw<6W{Qr*mCE$q@k)b0RF> z`Gi2N-BwPRwrS?A-l?0-5Ho-&Yd3O|z{`Z6gPmYyyL`c^Axy_Wg>)$3y5ZI2r~0?# zS!Cb#qZ!Ku#Yy%gQv-a^F_ZOsP+V;|F$W)Z(Bu5}0bLRO_D34W784?CrC;4GgqFEm z3)d5OuJlPDf~a4DUX=*#B{>R86jFhB+)Xy_&+$-Bv&NH8#9xCi1@BIRPfzmh1n=)w zTJ8=OFiXs4)sB1|Qj7*m&f+1jRQye-Y28sQ_}5?(e&=e9X4Xq%758E9sFrf53Ss8< zSNXw-fa6UKew8TzDO3nnCYCPzhFV*PgV|`^t2qJMxVn4o()!)USl&12_UMry^@L;F z3W>VI=~8o6^-Ak&7o(m|z6hBSy4QxH=}#PDed3+(OK5 z9(Ve}JYSKri{u?1zE^3AY@gI}sOl#3BltEj_`iDv%CVm6hXFOca-rxDx8RBF?AaGK z)8LN>b6X5)i}s!A(v=@C$>GYaibr(<608VwrpDiZG-1oTHBNCmos*rT<2T`kIl`ka z*S;>ZyGxjep372*DGChP;Qd1gI(}3&zyhOwdG(OR+&%|8e(C75OnqV5SDYlAAG-}G0~O&K%@+d zVp0navp_^c z8Y_7r363b<2znS5i(c+FpEW>t*E6$F)q?$x%u3s(SKmqA&GEKG0}gY%fH8yeQG>Pc zlTM4f`#Z+t5Fu^s@hQ$@fyTq>wbfEywloL(Tpn@jB=W)k)v^WnF(Bqv40sL7%QvO+ z5}qe9D+zV!Zn{d#vS?j5v3Rt!G|R)2n`v1}UPK=493S`h<^^1Irl??zBVT{RX9sRfqQncGDbn4=`E;UUnN<1fG2zWE{y&TRX^-%GW4Kp57_R z!hANX8wvT8VzMhlF=2|!zls?w!X>moA6k>Clmn|~U}Z`ac09&~`bwz5kgVl?K)Oe1 zlI6uKk}!qD=gs^X>-uUu0ZSAGU)o&1)(%ztp~p&IujcqR`jl;^erH z75Y7fNBp~$h`i4|%?P`A*ds(Ef{DRb&fj~1a*9+R{p)_XwWLycMF`*%unbA?F75S!bKsB6I`oFT=w{`PQh<^5QpLg)Cy3+A)@)70g!`&x&hlkX9Q~2UD zUsu@FtD5x1W}O9>SAzbzUVNGsF6J^o@Q1r%o9Evd!eR))9Gr!vWGKy0z9Q?`8|=ib znZTyM{OuHmQLF803v0;P)6OC_cye<-M54E-u163 z_||I}XVsi5zqtJU`fq(}P;gE2w?*d$$1ES11JA^fr6FEJryp#|Jhl%`Z-5q3p<*ED zuv62ju0A;Y0Q^PBhuhKx@>ywKm_%e1Jc9A$1&(6{lJw%he26%+r5D9NR?1o1(NJh@sLSSDcU*? zLa3}wpp)ojXIUE$-6Ki(#3@cD`=n-_9ULDHM@6F zjY*l!|9o^$@w=V@@MAP-iXz%(#!UA*Uy}cGe(+pT8=Jp}tl>8#+kfhv{L@6ET>ahb zd$y2@YAG3C@)3}vCkcv5oB|Is;5|KaDR=tV zuMaQgsnwrS<-yzXcj;!aI(m9K42t`AdX!kDuj(h+C60(c$T%Q{_K@iI;G(B{`A_)5 ze%hoYRD`(x%ubm@0v_j&jq|FQxX_sG`=7eDrI#*#fVwZkr_zml2f&$@=}is-cWpn&}He(nXN2As%7N zUx|^Bo>AH#*DCo1k|XXu|2C#-UF3W(uU7&X^f**FVjxJ#v~pc#4MWV?)YRsY=hLR_ zgzMa^-S5^0L;6%ETYRia7%BU*sT{Hr$7Qw;4b;~Qw|B?=v5eJnN?X-!w~+%Z1avxN zt3hq|2bgM!b;Gxt3Z!&ymOn67SHfcmA?z zaFprKqg7WoC_8|z8>~JCjer!>6Vw;}CFW{h`>+5-INkMrTU;M_fo+^flDL`tmu%0f zBCqgY81~J;_BH@g-%6hyI&Z;4$aPR-6gwhYsw8&msZ8kPe4x4MxZN84%HTvzFI^zx zV=1k-(PpdB?_+L30G|OS`z_K)v818d{h!WSmDm@bIfm=GvDeSqIy$q(2!&MBvlEl& zj1?uH%E``O2bqnEy_Vb^Jt~ecpXaSC%-?yd#SW!|K>_-Es_DucKWHsrQzT1u-&d%H)NHN;-M33Xnv{-&?kXWyx|Tg zjm^PDPMyk_N!{X!x?o8b%?aqXlOf^3c`r3JbwgBVr`7#k=R)UQqDOJ!IoN^4a7OFQ zldH0cwZw!5Z^E_QT~ao2-seM>+xwa&cOY*ZYudPT1Nh!cXX_4>S4ORhx<9(=vK zQZYk76QKaqDfdjug%CCvUD$DjH=3;awTSDe=vDguymam5#*`~|+QWcJ>yE?T*XvOW z%{If$1DNz`b2;Cu4M`2&Zr?Qp2ywkzV)?|-zESXnH(T%(-`{rK-XzTAzFo^p*wgn# z=e8~CTGf2>ym?f;pVO?1GR@6g#6z04I6SDR+&Sr!a-$H$p_UmMfMr2OLuW8tqwMVq7r<@iD)IjixROyEA?|hNhEctHIP2_FIQw*Y^ z+4qq?d#j7~Z81szczAGV1IM@#0D50+6%bNghi8lM;YRQCD3UA^@8r){b7Nmv8xG7h zrLRdO+9V?DW@RooyCF%cz<0ngDT<2E1n>{ZZXljg5Q4~-};V3<-QO9 zIgcP86ztmt)-ZMC#zk$}kkp4xlo@xUNu^iTyO>XEs#{>XsNaBtKR)w^ zxY&m4>?a)1WKWY!i_bn-sR6c5 z7n`p;S5p{pI0~w4+L|wG3imII{v(D(Ect1$7}=P9;H1S_v8PO`40o{*85WYx{YF-s zIM`$C)<+dSWKOZK2n8&z@72qm7rey5Hu`vq{p?@c6hpXHR)R{g&h?Dzh)p9wF|Qn` z8}QSlH6I*AkQztW6JG3|nc$II;wZ=ZNJ;;TD8*n&=3Ds9s@?s;ZPXdUKd^-|q(wd+ zf&#MlfO;XD1uAtZKj599DP?8%T+I>~+_9kreZm4Hk=PggVy#N0o=;z?Qb##I`+8J_ zBJjVfr{V7%>^doD&yradV#Ys|SG77t4Q5czKlQ`DR(f{$d-29Zg5E{)?dW za*#IJW8xCnH-W3MWw1x+%FLyqp|4=@xYxIq!lqyNpHF7tjSJhOb3#4^sWzv4dOtQ< z(1ADXe^rmKAV!GC4ieS>8z;WK<1n(1_|^8HRBSMEq)Wh+lUy=A^@PAZHw~+xDk@^^ zP=A|re(^rzsG_aAgsO^=cr-z(HCT+%JB?B8#JuzV-%63G#Z6Pa)f?v;E zN4iRL((z|J_8`@uXp_SSE{?AKsY_u4g!QjYn;gX8p$ddIO%cZNoOH2d=yx+&`+h?J zP~-bnV!O2M@A5cgfa)YW3BOkxuE9^J2_+u3VNNJg=ht*_AG=9hOVmAGtZqkvm?Hb2 zgx0|WahWdFcm)cAf$e~r z1!svYh>F$<4GkVq5}`P;W)kJo9yR3&M?0LA!6cprq@L@&JH^MXMw(>3QU0G&dC@;F z&=fXBYg zB2*0GeDtNCGtTcMNVO>^go+$nlO7h~GskmYn>1kTy0Zl4r_WWHU&jk({kukv50JeG zw~L8U-AGg|S;N2#f{;zS&A589I-iqyoqWC`6e^9+jss}jGvCZfK;R|>0dW-YmLp#Y z0yxZA_exUdnTj3D)>}Gdci{`B(~XtR4q+Yp1%vFRzdgn9mfM>&@HZhxHx{`9$>btCyJQ3!vyg z_;Vkyj34|K_WH5R(~k(d78l z)29=oxo5LKq|XM&-vvy|z2^1YC&mB5Btp#_H@KQ&Pr`k(jDq$(?OmrCyd175hu1Mj zxuH;rkvf>VXq{8h5$fypRY@8vI8BB}8EUW(5{}D0ZX}x!O{e%J4(Wn-ChAf^Bf(O{ zc7>25A!3elY^E^~TKwA_L-x@B4H*RKe=qlOMrz+*F2~u$WhL<6-@C25Rne6kO`N^y zkoq~%!>B1Q?3TaI>ul%jeKA0+(pi#!g>?5^z$qAqVfyZSSg?4V%4vF63nd3GjB z=_|dNz0I4h3;1=l<1R*Fz?DU;lu&V^%)!X`K;&VsqQSQBx5fCp?|ye{{qJL(?6mUC z54~OkDhhDN>hME}%m8kh2vteF`W)t6Z;dil&(J)v3jWBk3wZ}qR^^Gou89(LeGsS? zoRHjfB6dSHFFCUqg@5Y3TTP5iAIVED--tuHpA;)++Lnbkb&#Pc~9!6h<{PPVg!UlI7Hkt9`Th=G=HK455gH3x}#&~KXWqseKzba zM4UZ^&={zMTNcBAqf1}rD2(~aj3&}rSeqqLT{mq58f1N=bF~lzj|r=v zP2|D>AZ8b#`W-7JM8iYdr904QLW2V$Mlv#BC}ou8Is(os#Mui*o#VubahS8HYijQX z>Rvs=w-XTdQeGx9OsI|6d@JW9kC`+p!wgY z4w4@9ujfn-17Dx6GUFm3>XhPwjUGnNo&Zx?gou2mi%b+?-CN3mWkjg2xlleac!Mw+CCdI6eKAMBDw< z?SjP2jFwT>t1klcG8}942_-he+(oN%$v_+-5CzW22s1hXMgH3>!UxU@C9F4caFwlT zx)vEG`L)cU=Lbd5&R$Oll_uNmu)-mT7#RLz-SP}~cCrv9GTsQ#m!#psN~R1Lkhl2k zr|!lSdK?{eve5RksmkbWS!%Q%Dv7|W$8rNSqxf?W5J!)nTO9qk!sr3Vk~~Y5_v5V(dhg<*OnScSA2}bE!#K`96W{@wIIYCd36$jX$ds4g{!8qwc$wYuO zE39z0D_lVp=)|7SJ_X;iZkemZU0yQwM^D3pEF{tflp0UHCSaboK9IM6sh~7Wcy;L3 zw7**%y1U`Hh-a0y{ns?VpT8jp?EkYiyhLCIU?n9P58Y|Nrr->QH5A(qjxtVKHNs;+m zw@C0u4nO9vC$GysBvk$sf2O5O#35|W&0uGfFxf@=4&Pm@*52~Gm-yYoUy8-kBsyFo z#N>8dk?;_tvO%3!dxuio#D*7DkQ+Ij^Yr?;bu+VG9ItDiuNP$1aoA#cLA;5 z(}D?63CHh{(p25W5eAweB#6^v4ICYnq8k(9h_aF`gc2J_YIqosLMsf{6J=~#HXr!s zt%b<*dvqwgO*Q!`s)f@g7vpy616Q;&UkY6``WeiM?Dk)2d@$whRgR2m&CnR43czzqKN@&5ZVqz@(q-o9uuF#!Vgxa zgNj(X`;$_2q&En-;@PUG$ArzYXMT0+u@X~exI8#}09!P7g094R^n4*2xPoU`qX!6i zSgSsjGdAxh>JHbIQNfs@tO-$IrP=TyI}hoc#UHpLJMM>6j|?96PWjck?N24HNha{^ zBo%c^MuIY63{p9o>(I&`JOToCjwnkz@jN>%*aPcqo_AX=_Cj`ZGdWExx7p6J`^H@a zqYu}H;^r%tsi|?=MrBT2>b4)^ubv)Q+@LcVaTx2^TMR0*P8WDN9Jau+oQc%|0^Bp>P!-!^PsvyxXU6-3R)eJm8p?x!}8T@>J zi5>kF$XgTk&o@`)341tbuwp}aB-w!yQW_IN+v!#Nt#w|wnLomy5+OZwvUKNj{};5b z)}>%u4pSE%ids*~fH^s|9!BNokZ#ep{Mxp)q#A_t{@p}iUstZk_erb-G~XXxNd6Tn z9AhMU=QAyCno+N10Ec@dWxmYa_8sOUJLV6ccvVxO7?;S(Hec)uO|uK`CyIRe*9M(Uem zQ`IFB$A|2)#vSbN%F=W_pau#S^lm^J(u@#vBAWAhnhVsu0We9dfwEfxp_XcUrZ74} zs@tc+1dKEg5bdP_tWx8>Iib2jObiVB3Alc7r?2rKCP;a%nTdSW3PnVLx`|H6!u;2k@!K!Gn|%#iHS5Cz{9ikels8>}|LOkyC1#+_ zfJA6BVCR+{pL}6p^Dlr1KM-Q(6vihYzIg4BiRbXld#{V_{`-Q<>(@W$bSb5izlz;W z+xK(_*;^q)>qP&t>cLUUe#=y^Mi35t)TN5B&uwyN&corUwdz$Ij zkmx?QOdP~ks^YW&1jIl%21_RDn2n%4phh#^fr5vAZ(yFyR6>>oYX5GIk1wZgmWFok z|K% z%?Y9ERO>40bH%9Q!3?R`j&DDPiyMx+N)A$ED)^=G?0|M=AUqF4TU?1TLE2F_n` zsMTxpt8somQaJYWvaSz96kZ`&Inf5b{@b(16DRB=_9KPAU{JNX|asGe-ZW?oc z1tqFwyI9AuR$|!`fk5RgqWm()OyJlNjWEzQiJRl|9L_#yE)#6t?JiFAKo{)r{@bP( zikaPd*f$s7X@=jTZT=gj3)D>xp_1vPb2XL5}(L=}mpYMgN zR1y&MP~D=LM5!ZrG)%5q?S2;g`+Z8nq3WR8HgDunw~^oQU(Gt1+OL5sgl>i5lLCVZ z!-2W5{_k8^lhv1@J5*ui4^z$kZkEyY{t18%8JY2nmmjb%TCVr@`6c3MJrk%xvognx z8-q8=<~X}?%aU0xog_xtJ|{6YD^O<>O{c9eomq9FgSn6%932b?bec2FnWqO zfrX>=4X-SWl_m-1$&b*>gW&vl^@QNr^(T!3W^w(J0Fnz2J)_P>X3~Bz3Vrj@g~yEQ zE0mLetm@%+!#5n-HeCuVQiO$%W^in19?O)bhj=8t?@&KOmvSGZw5wu9c)rxRwDge1%-o#z5HnD?mt$Ti}z+*YqIDL=(DeF%BvPNsW**Sh} zCzCkj`3eFrG5^y~j%<8;vlv{xEZ9)qEN+hc1@6(GnG`SP(4}8t$ZM>A7Vh>$B?g5C zC|Dl&FrXaE3^Ipab9#BJQlhbSU8*Xdg3w-Ht)EakIE!pQw;&g=@y`9c`O`xr%Fet! zw(dUZfRw#1a$duco?fF*I}XhJmWDnnHsb!Jhe3UA$Y^xVyTj4!VJaV8j?uSb>R7lYe1F9C&8zw2G?j)Pd1ms6UXpqs<;Q@i~kJjdr_Yq>a7Ckiq z>Wk|MH@TeRCR>I$s5^WA6h(cOm`L+}X8E_zg(V|G!jK2PiQaKt2XSh^I1nf2547tu z;Q|~|zIwO%H~Fx|=j?d%c8H|y8-W$Div^8O`^k~FD4&-}$pB&@Z*E~us{?mbgE12~ z>{8;+HoPJ-1la#Bk_iv>j5L0J%BeWAupE!~pCUUh@I{y=*^9fd(}kFtv+AX2V zOn(0d(wPMeLqVY@&dubIrPg5fd`QH5plNY?SCDOZ;>jQjP^Zv&x>;rj^@X|jIMM78 zw?X2R@)#g`>R0E`Qc&=$)AZocYS0HY9IS&ikzJH$p^CzZppm#tk%coH4lBKsu&yu| zwQdDgmDK=(l=?(+H0dBu9sUV*(TBd&F&fOWYSfxYsm-Y8EXm0Z`Y8dzc>ilnIzdt! zorzE4Kiew^WzcBJV270>OVx*ed;iy>;{h?zl#bBcacOnJNg5m~Gq-mx*P7HT*RY7B zJc4hujYrRs;QID|v>F;2k%mR~X@w3@4TORHihg8zKpH#_vDB<14njA7; z(L@6B$<*_49p?C53ak-ufo79>eusahxDa2VZ*N5L9;mbfK{N%53N}LGL@*){5f6KE zjJ6I&2M?1IL~5=y*e8V3OQX<8{Ert>{8>zlCS`byGpr8JZB6q(nUPMH@HRNRD-Zmt z30fUvZ5->kvLz}W(0%j5)micLho1NoIlH+LpO(?uXbh%dwFbTS{TZ>A(0N#%(nzTt zSqO&*A2*0rm?SRu*Eend3}`rjm9-Gxa%RS;+FYjj`z)f&q$0&nIyd3EGyBc(`&WEgZKl3e4A4Lp#~=ss(nf{T6+>z8lU_HeD?|eyCEZ$)x&1qxIK17z zHLFcjV($XO;-bI1SZyG6qp#;QSf(3!et?w7SX`sd{+FdxHPs%tBBTeGzlf-n+Ppez zaKWhwHhG-z)u+LI+oEk;85sn$Yl*b_FQCOKpwVt?7I1~%04+Y}+N$fIN6^&ioiN)QfvaA0*)6K@%KnjUQ5z#eWjwwZj0uc0SOBfo8u#f&@ zc~Gjn*F=+~Io2HW*)1ySl|_@f+>B-8CbmAx_x0VnYzth7hiB#qq0hIK7DPSC2G%sK z-jOSY+x2y|PDWDM*L@K!XeBuxAgXCn(%bGQ-rX)`dpF*6|E^3UGIIHlPUbieS^K*{ z_QA0|Z6b$*QcQUNQ+#64V+TQe5fHJyau}Im=9^Gvq{Xi)YRfPrI+4R#3BJ5!*)|;y zg0rj>j#V+(eZg<+5*2iIa}GuPQmHkV71!zbar=kG!~z?3P5(qJ99yk*m8kIdcjq8| zI^%ehSF+t&F^6t*gq|Mx{{Rj_@xGxOm%>6$@KR1^Q>w<>ZXf~2TG*6alQ7RyF4d6z zZYLzyA?KV8?BZhc+4nxVzTRo6$J1og%Fv8YB3eJvRDS}=DQ&h2`)E-fZXqoRpHe~pZxCc{+9Zt%FGxs zurDWpFfo$^4jf{LLoB7X&5>)(8jE8gaXUKzmMW|O#@vp7muQ-LBo85gi77ZyS^I2-tub zeDj6?islfqGo?KZO-%upIFsJw17h<&cKQ#RHKzjb)h1aCw7b|1>-Bec(-&XA-EDVI z*RbK~L>l|MGiF&^HIm z;#WnSBPLa~RLQyKBF*Dqsmsf0*%gEMc&odd`1FUTKU!Z-z%qoiO@Si-MAYnn8lu#y zI0F0lNPc5j8w5H_#u+D}pI~5SDuk$trk+yos7DK0XlD(!#(#lX+IB*lWhWyBpokdT zpAbMBkH?`n-*AFqV*!~MFaSe4Igx-3BVa;`fB}eG_HMUuB18l6WG`$3V3X4JXYccO zlN&5gms$Y5E+NKeWX~0R4*9qd3L<(?r7@c4OOZQWFYl87=`0an)M#`2fhtz2)#`t$ zR2M>Pk1B)Zf%%2%WtjoFnW%KmATt1nTFF@|3_}vJQUn>iqm(Kx=@gaAtBarf^hZyg zT$Jf_I-aW7G@lN~gQ$iWk9YgyaVnxI1Sgww;Pq+@Da^U#xiB(w3?UMcwyYAP5MqjT zE<}VB91v>*fdK<)h0D$6=Rf@9Km7LX?Ze^Wl%MS`R^v!DpU)<2UfBLA6Cs31!9u8^ ziqvM4=x%_II40l=8>$S`LLchi9_k}qM$IOIHbrNRPAth2r za5wV?8eIagLNu2DpAWbgJxA>)&uIH9YlUgw0WSx&nVc;FFOg!nqOrb7^u|= zq86x&9=_6koS7L0bhxb}jgG_gBdQnFUXhl%@%TCP0@hhB~a)FG|PB7IeN(w5E-ed1eZq zcenk;Tc#P8V!2`sXj#MbW`f!*_Nd`N1w|287^sp;yFN&rh$n=juT zzWL^xFTeWc)h~a3ae0~dw~hB;Dk6SfF*X)Vd*Q1vu~c`h05hvnUF>ewm}C7-QWLY~ za`!vl$JVw?#;VN`7bzf7AVTDJ>kvluYY~Y3?B(-c{P>5jZyx^ccVE7E^5n;#evFcXp#e!PCZ0#c zjk4A@P3O&%^$@+b$shng1twqxB4i_%^!Dqc^#+TNB{7qO$UP+7g-U3?7(y@rCS}AB z;_Bk+zQXN(Hl&Mn+C~i(BDJxvQ9?voWFnKOMglkXR@K|v`&#A2vuA+`0CLVEIeQup zn5v#m)78}|sy8<`pMLVO!xZDVLV!{$Ow;KQq-scEH4bGXMJ&ZI4rw|bRfGeFz;pHl zAtSUvNG&TgAhJlkT&;4E8`)R)vDcof*9bKWYq76bmpk{7ix8aYAWe9r0zyipNX$V) z;94P6lc>TJgP}9P4VXX;rF!o#RcdukfA{{ebxBtk7UH~Fu0P~=%N4%{zFR~e`iCI7>%}d!(kpM&gCn83qh!g;@^>NPU z0tKjG8Bs_*BN85=1bsK!mg{KfM8*Zg?{-K{%3ups014UCdkl~a!BnZ6j&=hgJCi)y zxM^+CW5#VYBu*R$5mZHOLGcRU8gb~d44+ILWPTJl+38yVP^(CF@CDDxEC5q0QcEdj zvRn?L!ZpFVr>{8qEp$qzW| zMXu|iun~0w_A1vzNK{#*mI9*7BEJ_~)n%`szDQFv!`fa9QX>PR(7I!6z6u_^hv3i4 zC2dGZIwS7q^4*&c^J)I{vrj*Hawl3bmv=%o=glZLuLI_C1u-<<7>h9+LxY=x;|O5WbOMz9{xIjPZZP6DK65@E_uu>U6A=M`akVNX zJN2^6vx-F4in$zah5_eboFe0M8oTvMPx&{&&@Y(|7g>I(qu z5ImuAWdkjsjOgNQe}jDP^?w3Tghl~kAVTK0q(lQl69uf$26I^YYG{LDdrQs0DWGC! zKuzq?tKKO`UNAj7-YX240ipI(_VbI1#csFRtmf14?cIm%ZWlwydEOr$ z%#@iA$J2+~JEZ8AgUmF|6A`Y)F>p8@KICZzun;(mwcsSk_MCvI_+!L7k%-K;ML?9Olnh}#~2(jxVRTu6a zIC*wyb6mdHxqnFPlzk3N6nrs=ja^G-Mijs)G~NQGSP*~n`)}U8zq?qifAaCm0Rgl% zcp^%uzS?S%off*lh!i+lFs)hCUOc_}r@#97U;c+*zd0Pfe*N~lx9`69>Pd=`&8($H zwXP7G13|O&!>+5acIme(x4BX>0V9ABVf5e}?|vLs_REo*cR*X*wPvINXP#?~XlP7W zLrh`VR*Y{xJRDEc^CuV2)-)D6W@5q=Bes;7^CaO_KBbgms>P#t%r2_jO8DTJV=Ot2Z#)pjlSM-jtb49}dDg_aGSaMhi=&OMx44s>wOz4W~=7DQr_ zs=-6N4aLk9kf`tetmTTkHGdVUZtbbn3qao!E^R+NKG8e+^GH^J4cf#OzNVi>Re;c^p&5o8YK{<~`?{gXG$`V{ktv#w^ z{kA5c02GiI-Osui*>tj{V^La(NQv8t53b{Zu2%?-DUp8x5FxisY{!KGO|6zd4Z z#LJl&(qC$Ia|SSficMvJ1n6Ga2HdT(5y(UhPyhiGLA#ysIVH5=W`Fq7n3<}GYOPQz z)C#H|`72dKv;ZiQ8Z=97Bn0lGS8M#+Y=FH``oUMSAci3gri1{V^T>hcX)=S0%gd{) zt7$sk-rbaGwp!zLK!kaor)gTR*HvV{pH9ad#|<;26qtEF<;`k64uhHA-anjXH}Ple zyDD|bQTNqwo5CtJmyFO{w+z}Fs9-VBFMjmV`$Nfs-@Li`;~#&t*>p&1h~I|vH4`u9Xg3nRS6l| zz-pov#*vWz-GBW5-reng_|c1B{NR(sWF`hErdSIFMj`_gm8HiL5vJCbQ(#~DZxjFd zAN}~h`|H2|>f6_U`LnhjoOwon@}5S?={-WR;lXXshk(yZQcDBZrsUbdf44Ri;3|0Udu+6(aU! zx+7pJwNxm@k2Xjttkx@ls%9~S!0~W6mR!IL0k5yGb169&KzcYFHtTU1696F-f+~QR z5uk|cwJoKAnO3X8t}pK&?&`jP8F46iuC*fQH05zjWu8yW#K91ADM)m=Ssmw6M60N0 z8ZHonmr>|MKLa2zPz3h>Yi<964=j{zF;f*oQeh+Y@zelBS*0ywy!lunxumL>fm)=N zme(!|Wx)$`SK)RUS6kaGDzJ7^FNOlGk{f`hJ5V=w=)5lKCe-L$?M{UCRnvlaJqDPU z7iL)kfT2-f>U_k;y>0f!|M2i{O?R`p=|!wRcAu)7>X?vw0RR#}7veQitr=+BQ28xj z=rSHsT5bzZZ3}+;^+BycHLcS5b_BQ`#EzkP=?zXidcKt}lUcl6M=|D`>eh~s1 zl1U%c6oDLl>4Yj@W>dFS^r9j`h zgDKQ%dA7jFOkz+YS|HNo0r^2*4|$86g=NsuD362m@1f?<@6TeCgAi zD{PH7rY>?c?ar(O2nu3-s8({I99riPVnw<+PT#zF4*;Kj^z0{}y-JZl1(eJTRGN>7 zAt3>S0U3jq+OM?!QmKxS|Lj*kc{`te{l#~mfBX8gw;!!Pezi@p%$3~1Tul-2Y#_L> z^m;ebz2vD$QQIhPk2E;k8Y1#0sy7X=YOex1dhT&!Ph zImrZIfjEXH5=HlWhqn6m*srPx6+{gwm_bVE`ughb{sRZxY*s0TyW59aD+eCOakbvO zdHq_du2$=Zhr?<$#FTO=A%@)57r{{%_v2O!QO04|Zr1DdnDeYvv4a&w>@<}jB^9fs zmRc!BDW%RO#JF7zbJ1K~v5#F9hE9^~@wKEbSM{OYOMzdD4L%14y!_2kHA4aPu;1WD z->6naz3#V(x8DvTl4~uVzFk{VJ1&RqLV>`u8AChb&kM)~+MV~;fXnwgWq-*W_HQ6( zg*`Ib@@#7=Q^t!5#dpt9JWcu273{}Dzbjh5zig{JxxUF~8&cYq6N~B{7nW2f0ohV$ zAG7`+z03&2UFL8;c1?OAb`GqNUnH#-pYN?ky=qN{(>k0!Qx!$X)wQAmYMqtatV!Ct z)GTTnB+y2v*20h|lq@RFLWgQL&vPlo%k*}$-K^Jycsw5SoK^4J+@9&QL!zq`_%xQ=*M#Q$UP*s0Gri$1sRRO>a#Fxv) z2iHJ&b-DT8t0%X=zx({l@7~_-Uu}20>+92pH_PeYX1I>To(uMZemj7P&3=m12pP_~ zN7m!1v8{Ft)TKDYfCvixWh6k5<=sa=gj@WO0;J9Q-BkYOcVFfzAHBH#;YZJ|Hv@qi zIfg#YdN<$+3ce8{tKAjRIv$`F5kG!<^{XF$|Nd0IdwchHpMP~Ru7+n%I7ZWq2*@or zmDDv!?OgEPnjc->6j6^Ohi*n}?WJ=^OLir}c?FC>NZMHt%l*YcMOdmMW_;ZMN%S z7)z}pG7RJKcoe-tLRB5dQKaTtoYdgsLKU0xA*C3Yhhf-mHmB2Rwi&>Pi8P4FoJ*;d zIZBn|>2$feLZVV?QVipO=+?y%73CICZHF!n$~0|0GN zRH#4zqKe2sq|8KYgw}guZ~<^GwG==7Jrc+k?B-miA1zK@MYW{=S#PELW5z$24162Y zPP8-q61oBTd5vtn&0e6UMHt>`r)OlZu_+-uf)KH1r*-}iE(D=Ip)!JbqBk$B80+IG zAR`5rvNqNxutV#B*fz>;0*Wet5D^Zm zI&2;$cz?GCq9+&W#TwTzP1J-I-Ke8|W(Le0V+@hUVW?8vwLVJ$FqJx;j)b({tW!#d z{oc$iE-FGu5QHBQsXY(@YTkDm(yaJWC#I;0qxXt&)Rj>l4pR`9L0 z0aPicd5VcmYCg#2)nz0mfGTyxtL-?V^t-SgqDg@AI&+l>LddGlt! z-!lghi7^^LDW#MgV|1qf{lnojWm7-|Q9%MmR@Lop7ectZzkC1TLnkCzN?{z6lsZpY z)pE%k-2X>Xi0(*UYgK>cRca%@bw0l6r!Te-*>0!zUwrfWlb27v|75+odRh;6?a*q$ z8jV{CaK1I^{EoNo*Si+#d}ODWcoZL78}N;Qm#vixv&@ophQ-m@bBVcc%t z?vI~;{rcf<|LXbEpMU!D(v3Q$SiMD+a~!^1zSJn_ga&Db*0a8ajO_n zzk9J8uJ`)}5Fw4Yy*?GXdAPrSI9%*juU33T6P_{}xSS07kg*WJnJ9#iLP$fBQhdz5=DX8_Hq{-g)QA0PNO2qnHO*5-vy_4Wkm_z6N-6h;eTAiE?(IZF zKhEn{YTa!M^mZj#nI5c01QbMcY_>V4V{d~!Nzjow z1kdE@GTAfZ-V{+?*^4cP2NYTo7y;^XieN+Zy1nz#v087%2%DM~5ZgT89UUAY)J336 z*5jwe%ZK$K2r~rtW>Iul(E2RacYVaz%)dea?iz{C>^36k&7HR(&HYp=m|);I3@ZdK zR+;(o@^To4aU9j`?(SBq0N~-UR}tRr5TVwRa|T4>AYwN+_j#@^*UUMqN=hlE)tDjx zoKE@v?x-ErBgFM;H62b;tBKT-F${qmVQ3s`C%CDZh*W7)ulH1{wu$)h)6Mr^$%os! zuV3F>Z`YTXPe3b_sbk-r`DoT$dY75DaVNa1wmzPL8K_9Rr?CCKs{Vo0o7k0wu4DB^ znj1p141z+06q2nsfBV;e^_$;)akbt3?30(@yWVahnkk4Vkf(NQDHfyo;ANeyfeo=~ zm%954p+6o703TiM{`AM6?ep>L5BHyc_huMZKl=FPvvnE(wNB7&72CoJaOn!Mk(<5! zYQwMJj0?X}o5>;&Q4fP^VkR;`WH3;xKH7o_64Nl+_A2w{?tXumN(|w8#3xDCrnS*= zX{j`U0})k`z#%ZNhqM}poM%-tz$r^L`{G-$p9EaT#ciuet3UlVDp^S za4sbhmRxG7<2aO32r0#cNTTXZC;_fk-A# zS%ZIA>)Ms@=%_bvJQSdB6g|0?xT!rUG#w$f++@F!vA3n?ivt{StWL^gM{**gE z2Yu)3S{X)UBsS~L6CJ-&TZHT_L&*`3wwCYE)8j6w_1Ndqzdr5{Kl!52V2Dk-LGY6ywa?&3ptv*7LIs)Ki7FAOrW()!2 z8a5Yo+}=ObyTc?>uU6rCkS$g=^OQ)WMoU0MCPDx)9hlbRuwD%@@M$`#L9Y66IF?e^ z>+#~^Vt=nCA5X_4g8^7faa@nnX*wK^wdPvYfJ)Kzc5v}TjPd^NK7<%JoK6!W#=uNa zMAqw7)kCRup6fK1&3fE!x5F?_bFQ@lU(s_qf)>+<*jVLkP8lIWq*fc+8lbh_$N{;cG6I0ddx1(@kv6=p?a9@&s+H1xozeOw zdhYSh-8Vk|<2)4D!j0^Jfq&}zDp6xmZw&o3*M8L7BUb_s+W81Vc?FaT}3Hq@o37wJk7T`~&6gN2sqX*qJ> zyx3S8Owi^%_h=ts*zmf(16W3l1@3jkAKJYPfg!N@f;uUy(y@3_qnLp}Zn>M8{oDqD%f_)Ca^BZyQQ<&OpYvwZGPOj;72>U9~ zI<*>sVuH=~p}=4Mr@y|rxqp7K{n7Kw??2g&5o#^I2s5@+W9f&(rE z0L_q$LNMzL@JAKE&d_kf4a@*2;ChGa>zdY6)%*K>o=+>5rva}}NL9?u?mYIY`As{^ z0s+PpE-zN=G1Yk{L;*NW%?R8JxTBsR4wlG0!R zqDI8iJYBC=OyPJuA~j1_)tZKoa?GWw!91057>MIAj_WG(Jo_DyQWAxn>o9ntoCUxk zM6)_g*@$+V_5M`M-$6wXXUbt;s}V9Vo3C))YP4~|n^pk&%Lr)59#`H%gT?v&0OH2f zJ^=VY=1Sl`KP)?2I3Hb0!@9UR19o~!BmO)xgX?aku6aR2Gg;7YXQV84r@ljg<_OT* zAL_pY5K!90YV96yDt-H>xsg*?nh|u-@-kL*t3hnY&)F%kAM#|5q605q3P7F0zff1P zkzPF2($uuN^9FziWwA8ZPX!RBh5zHI3oRz3oj_!2;%SNsN+tjb9?d4&?+AS~zo>{d z2Y7!;7MQ5Fod4hhm*?pDLN4-k52=RkV>|iPRE(Y3X?^|D;0r)PB_eIhLndGbW zbUN+#4+gS;D@3ej#1xnSt>m1i3CvQ8P+%B`-R=SqMAgmB%}7N7AjW7arI-;C^PF?e zxs+If)LPBNk6t1oBdW5p@}noK-+jm*-hKFLNKY=VFSgGKs6M>L({{DAg4Te8? z`TQrZu15l>V%k>HrXH)nQB7;rQjL|FfF4zovwy?VkEwNL6eF6*&tAQVA^z>RZv^@E z+jk;@-}~svxQ%s&GJyzoY{!jXmyXDlcWq*)W^V@s976wH&Xc0n?}GuLf+CP92oQ~9 zyttC}rKZj4G~c|xnai|Iv<1A-Ia-y@aP6!g1T&FZB-dI9V7p#jU+jj6`@1^>5W_jw z!|^mEJlx-(_Scse>(wxrO06}sni-%8pf6_V_@TIrBbJyQ;x%s@|MiDTQD`@Km$JlH<^oTf3;OMz!ui+%s4kkd_0%og6uDu1xvTT zoXn4}dsq$)SekiLKvGjQ5)d#K|B|9Md)vO-wT0;@ZIrf70cy7bF4IA?nOqc^{%#Nj zqK+x`f&09HYLrBBYw5PAL|YCNBss(Ijh8_6n=zYse1~u@o9UvlLUVv{n zlNmfzSo>?@d>>qX4w4}%f*T+UsF)V7xrA<%d8X|2cgfj%?NfzC3ka^u=qkLHB6(&& z`I5L#9|i_3B+5Jq>>Sb4c!RFmM?}^JM}h-gOu%FS$_#-pG&!h0c?4)*V#Xq3A|;o3 zD(iJSbG=zBrQ}k&xCzH0t=GfhaGG+iwFC}D1Vrb#R06~cJWZ!O1gxc0DZ7guqKTAY7)mK(+HQ8HKm-BZR(hHzGaFJm9FB;{%uanholfHR zuGku~Kw&+u2(YMTvoQuSq-v$M(&AT1jMVIDP=S=u)w7CZULjhJ{L+IfESW4~P6|L5 z^1C}2VdIN;zQ>uEsjWL-O!?0z;Q54Ij{R26AKwLd9sU@po3o2KWllj~7eQ9|Q(s#L$F_?Z)W5 zcxe26*LV8Aw1NmkZmVSsz-9qV{b3rIst|@|=V@Jn-Mz&?74ZMh)}O6Mk|bGTuxzSk z?hYW9%$l0%Auq`Hf2sNZA*Z&?hyZ*sQxS;l|VlsQvgTu za}l5F3>@D-5mk31!cqhx-al@)`$L4yY+1|sv@W%ThYwJnm|4g6HM(k= z(zt~|w61&ayPA4ffDPYR1Rp7FuT}oHuhr52@rb|w{a?%JEPwg-^7dCD_Qy5)9-spQ zd2V`=TkV5$lLKJT(Qix^-~7kZ-xr6DGZ&zf0ca7(j74x+Hd+4m=f~gw{s)@>pMU-R z8^>S1oX@8!u5QCJ(MQ@4MnsqeW;lioaG5#D0CPD3&(-+2O?(J42?^uvyvTWd`Pj(x zj~~BoKY#vxS@*Zg_m{J*YqU+IQdqbSQ4fq|1CDv~I3>f)JxrMSz*!@X#T$!&h)d1(S*|;5-TM6wyrZN!qcJ=czrzNUbgXA%Lx!H_O8>8w3iS4U_6pEfcFWpgf{@adV4lR%ITHj@Q0 zGsT`0K-yYNcQF_i)dqt;fxwxBIN1Y}+&TX8{|m(!Ln}VdhC>f9{!eJ`k)Hg^TuY2U zl0_efQ$&Jb;~Qo5mkfP^qm)hhRlsCJ#b3LHk4IQRSG_0pIWr!aWz zT{1X`@_pdg{@_?0VdQ|x44kIY`G#`|j7QjjAP7+jr|B*jxdc<~2q4S>VLJS_n3+YS zR5g2CZ-ipz?ylzhuC4cdZ(Vg+QXCNbzV&7zA;Qe^al8Kbb={k-bq#kWT5Bo7;BIDs zP-A^f9UlG_!gl!cfQAlC*PgV^cI-C|KNrO0LI`(5+^}|c(`ulj${xJcZ43@Hb+}_9&Hj4!iSB84 zlv_05R>L5y0Gg>nto1rUnp>)_Xsl)}>Y?lu}Zg0s)W5{_=X3QkG%z z!X${@JxI+w0*Pu}m;*WQBXj5!o)aGgz%;yxu=Li52;zvySl3{XNX3`SBNZG{+J8$KqLnn7ZI}8!{Y!!L}VC*pJBfaG+2+ri&SRT-Q31d z8r3J|4+0U=@HSdgGZU!@+*~1oS%@$rhX69cP{gG7=57F_s_vlb?srqzwzjvO)8fM2 z%(bhBpI1&g`M-X?-?m01wG_3+LlX#t-WLPz2Sp;PQi*v)PFi=D)^%&G_4e4e`~ALc zyIY_kmU*{DNa!~qeB(vn-X8z@$KT!Jzx~Hw*DoTa`s0V+ujo5{LO?&^LSu~%;m8FC zFfu0Tc{Icv;Ax_S00_kF0U>hw_NCSH_0j(M=N~`*^^RS?FY@C03ii63*QE?+_>mTk zBQYlbt$Uby??#N0I|_OhfIO;tOS?@oAmXH%`C%OB#t5&K@yoh^eSDOUz1hCq{`rsX z*T?D0mngrT*At#u(3p?`Tn)@1W-daW2a!?tfx?CyNXzLg>k4%-6nBlc*Yf&>fPVb* zpVh&(M(t4-Sz^Qe1GFz-*>%p!lQ@=z4&n+pT6aQtIoI!BUrwjxes7QMe!I8K=W*4( z@9({pudfOF+_wiJpHEauiNMxc)6ODN@U$+E=DW6jf2fVHxm<5_wTL*KPLFMi@IZG* zF2V)0o9(*qtvFYaecQQ6VTp*x_9!An*uo&-QdTB@+;(E-B3$_8@=~iW8nPSuW@bTz zQrBe#G(dxa_(DprJOT};j0cif3jh%WkvlyMKmkr_p+^EjpRzojCH`5y2WmIU%2NTB z*u$XO06!U=pG=8ow>&WJBymPUA2}^>9#IZ}oUrKODnEYuz_Xr*!1&QempdpbQvrpD zDF4lRcfC+e*U(oRQO$%+na04v6dNi~*MupOzBtAMX!SB$5EAwVv10 zX%EjJo*|c^X05m0+d$jr6A*ymR#?9jiWB@&c=-PD z>rWE-?YFO|m*1ckSzqGz%OBS;B^-AE;k5QXa!Scz^ui-xGW7%Q=Ch`a@eBh=%H^zF zc0hXjc74D9>t8>vfBcfYe?_lsYuLG#(^88t!u*sWJn*niAw)NK>+WE{2!tY;%m9ey z?mChHlYSO~MDi4@XK14axH0pYkGjH{JZvH2Tkd+h1Nzua~+mP?sWw2k4}c zds@Wl(2!3Q7*epr<)ys6ab34v+pj%@r%DeyhFX^22$K+e6h%I}@+9s=5b=l=TD=t=8II9kByy zfOg+~*Kt$1tDR4)dvw#7WTX`Rw7$E$8SL9egzg5wrI!2c<^kOl9{wNy^{=P%IkW9r z)BW}+Oa1o!%eqvOKvDv74BQ(?S=Tdwc{GpyG>e%&Toww`1v80hSzDHAcL1B$z2 zHkL7LEb<~vpXDcZ@VP7;a5@}3pV|E5fs!0&M)tivYLq8A)dC!mXXPRCjLRE#AOIt{ z`j|3sirYqK|AZ-z*(I^cJa6P!)y~(3^A`cq3{}fZGaWX@d&&*)G@?p?D8M}k0?-|& zboltGbD)LD+>7C6bX>d1Y8u1oc)?(i5l=C+zwVZv3m#5jIRZXs=X?!_yL{)V+5dnyO-&J*-rsmteL2y#mxWKwpWV)O$VAfkl z;-%7B%9qz=U25Cg?RoN{rd6P_YtXU7K3dDOyuFi^vbdZT5H|h z_ugCU;}c4eG?F7Y0D~2XF9n(D!)&u{fB$hSY%gb)^VjchT9)7{*4fmxh4xQGy3y1` zSWJucVW;E(5b0f<6Cb1Sx}vZ;^0#kBbpQF@-|s*F`N#I-XW6%tdx_3qBDgHIr0-oi z+quEq`p=mSArrpRf-U8+oQFv=H09!x(6YiPiOBBLfrTL z@gP~`$B!Sq>Fdj-H3wu7j&P5{QcA_Tlzs08(A^1O-}iN03bUz~Qia&e2?$f3M4SeL zwH65Iy|3rxzHMrHDk}iCrWPiIr7Rgemho!a*7vS;SuZb42$cy>CFF%~4+E~J#XZ!( zJW{AX(>Wv1(GC{)s6a&HfuFXQK#UYYPKZu4f=3TffAGI&SNLf%n2{gHW;Xs4?O|}y z;~)-3$_z#-Eexlt!5E$mY?ArHJZ5Gn5dZ-rIDAS!{G=0`zl~2{KE4o9hz|slCMHOc z$ot#_%&j*aE(tNtx`82%IeH{Bq({3`Di_CgWI8bT*l-|@Wz-x|j=)VA&O$j}S$bY=j@)I$$AX5jhBx5Fe5Qaov%Vd0jmml-uNuPluNcQT@; zq+y%~>9h^wH6b3hpAbkuG}ON-o2DbF0jK&G8306hJ+0xSx&uP3)pQdP0Q3}{fJJ!k z%~ZKC0(p4v9m6BpMnYfDZ|mvA%zyszN7tN_=u+i;UYYsl&+F}ab4RH)kEQeZTC1P! z004jhNkl`SPl#ka9Y6^y_=vuFy6HPo;OZtRwhk+&ln)G;hp2 zh#g4Xp{(oc7k)ke`RmH-X@5L^{_9V_-*syz@3nZ{ck&JhScEH+B>B-jqg{u;CgUL^ zJ9Zi{F-gzCoS4XoET%GZ-gX{IaK!L1Q^;$8bInb#E=0hU-7Vapx9&~h&#eJ&BK`F( zzWq*RO;Z^bA>v*LgklmF5s(A>?bh$t%W18Q+^+58&;6In?R+-H$FJy{7p_RmW5*WZ zV@!>lMdzeTn41i&?1Q02aELbE&Je|(M()QMNeR(-=ZQJ%u zgp1UBYh7KI`grU`BwIQbUY5G`{_Fip!omy?#PL>FL2N;|tQOdMbaiu3bHKphfz(m@ zg?;vJOSAaV@)96L40~iocOPvo_~aK3h%TO8_CY3u|8#r&Tk(%U#fqc-pY~#Day7nf zWF%2yl4B2$HjPYlWJDh8oJnb%Bi^T7#c>KGgnlSbWSS@;1jUF=r%Xhd?{;o1M&|A~ zDsnMSGeNHgYg+0z2cl8 zhKwklu{!vq>kOmk=Ld7}pp&zNlp9EQ*W4v!X=lv#6Nkt<-J@qKibt0}K3bk|+kq@) zB*F+Z$R#iz)-5<0ScH>Y3;-h1o8=H%s`OrfkcrOc)BUl1yuX(sZ*OmZ``h2Rh?GLD zu?P$sU5bdy<#Ilq@7tCJYH1Mkdaj7nn*QsLUyrR*DZ*HVYvFaR#Jum#R8L_ORYe`(R*r5R)TxB4RnMm%7vtX%^e{Ue0gy`r5+YulJ9i zzuNoz_V>RRg16JkfqLI6I9356WRwDkHhcs#*gL}0dOdrtCu1+djKaVv2pmvV3yUx- zO*Rr>!a#%^*(4f*rT4K3gQrRWfr}IdsS@U)(SY1?WC*p*f3@xQL0o_+kery98Y4(r zh@>;daH3a6ysY$kSxmzo{@C>K4z7Yhx%nBna*#Zq1Nc0E-a#UDS4;7(m0F3h_Z^V- zrrX}#L)B^#2-4oG5EJF!LdM&!-u1EV>uG&#?Xf+~d|9|I<;#d7#py|LT-UQZ#J&UKvMd1j*mfW~FK2enu=Rib^Uor@tfH#R>R-RU+_(O5 zeY8ibuNNTSwpKx2Pp9DOWigWb4v#H*pC8mf{(V%VEEvxn>M#&V$=Do536Vd!FdSRW zeOsB)Vn&8c0|tS>DdbEBr$dr*6PB60s-yVMcZLWtG6n~#GtMr`882BJ2O%l{ zCUJy1%?}G;$V;F-YM*1U? z1c0#QB@>anMk3OCGt<*)IiJtVvJ@%l;!veVL~Cu^wi<#)mkr=IEw0%6p->jB*hxF50w6VG=|XW)WmY#Mrh?YBiw8Zs|dtk9BX`x?I*;`E)X2DUM{1hsQ`f%ZBw+<(cRG zh`);nBBt>0oZ5W`G7p2{(fU)uj|cT9Hv^+X4*V4S>=P)NxY2-_(m-tFK24I#IH%LP zI5pac6PAZ*v78K_lm@^--1q-f|8uKlIk8wCEbfSANs|T{2^mLX*yvr+9g9YZUZ+Vrd-GqaB({?7Vh_jfm4} zb%)-%sn5j+)}^M_X504du`v(7y=AFi-d>6MdVl=-xN+f9gos$+_4S2`9*@WEaW8cV zfZjV$VL_a8nwcX4GYfNT&D@Cun76(^`m%RzT~#gJ5g>PEW4t-0zd$f_ThJoFG{R9D zz`JkP(#uemX}CBKO3bt225@5KV!+<}{&CxW{&f8*s@wHvd2AS-O80=E zT%r^q;*dZF5d<8w-mttdPej5|kl|p}rEn4v({CcuWdzs~QO2Y9z`?Qc`OxEBCxk#i zItaL%xdA%OIh_cR5dfVD99Z4lhzB}AlkCI*3=kfShM1y9#RxILhAE;UCT4aXvEY>R zC@{m^Bd<~omJx#+A{oWaBd6YfC!#W6kUfu)c+s9Gta(5LdH3V z6f(1QI3pt>Wp6UF_dcAQ@wMZOP_~dQduETUBQtwkMhVIK-R~av7uw(u`U3=0^?$*cv59W0s_5-&LeAo%-}11R`?|=16X3hS_dM z6(tY@?jxiIt>y(AE6{sX{EYO( z-y39BV7TYL0NDsGE{=fRozrN3OP4*{gLw!P3<+wHr;p zmUuAvf+3=+L`iG0F(xfO#7!{I+HCHWP<|<9FuO8)ayS+&QJj$xm)T+A7BoFKoz+&< zKq%*Xl;GaJ?ADYmplOYW!rLYS5r0+~CWongh_$~i270S9ET66snJ{<-FeWp?k9>qx{MlV11mi~#T=8+?p8}3t5)wv40x&*l$%)r{ z8@dV8i{mafBL|C|q@rao?iafHjlyMI2=|;D8?&J4?KR|IkM@T4%8@d^qe?ZjaMMc0 zX?;b5(AL4h9kV$i&8^Ps+uOP8NhdSo^TmU;L!@Q#`qxp2ds$p2S5}E;TxKThqx#-5 zG(HnEWZKIH)c+hRlLxr+?F9DWN17WxAI&*CAB1*V2tBgs3~N}KMulTy5J7b3Cjsrt zM@`vtr>3}f8d!YC6Q_5iI#hic^$d?ycL7#_z^?Puc9HT6W8``@_VEfd$syDxS> z`%6u#oy85fuQz&)`nR6A58J?j0(=KV5x1y@8mg=va3EI9|hf zWn=oexDN9%ix_0W#~Xm|gTE$&0?4b0H!Oz|UAAAmw09wqdazop+J*Wfq65s)QPx$f z@8e8`$rmwx|F+2N-@_eZ*j!!FN0kD2+QAI^AijkXhi<`YsS0 zzvaa=JtWobt|ei=$*oa1R;#RrGKcUJquOwh;}(@ zKSxQ+CN^+o1!6r7>1=zHUSK+2_?Aw}woHLcY!ETj%EVv$G|#6nF#i{oYL#p7yiR&`bqq~zja zN0XW};T6MIiv99&1L|sUS&O~iHm2hGqGDx^(_?<3P?gyJ)QpXX2d=n@fDjS~vaI z^c>@t@iSR91dLk=W=}b>l3DvIXYG+1S56WYlzp63EmdadUqSZ(b^gCip_pixrT^F% zEc@77h)W$4H>j5}+@PKeGnJ|#^ExV*b-gDMy1_Foc$0Rd_)z}^O{9e!ZngOpG9wb z#>^hpvvU~gZ~59Mul%hJ{fOi0e>(j0R}9B(tJ^JhmMJ%TvhdZsh9JP()-p%?chBCs zDJ&w<%tdKS+1D34d57jI0!!SJd{TLHpDk8KuO>J<6{;j5Tvpb`b6ZiN}bSLBa9Kz8ff zdxeZ&=xagi!d}{EiEmr(wM1P=#b&ZZvH;=M|MuXDAZ{rt^PXzvHT&MVhZ#xy|AfCy zaGE6ul*y0-&XQAdA0r%#q<90xj=$4G1GW5A*&c_%l%Mv(Ay$-_50SkxuJ`<2+bhXP zZ85BpY7~KB&>Gzyy$f#7nXMVRk_&d4$0s{bdRE%K7>&YD2fMV(1)o;V4;sxR*dQ~1 zetmN}!k+X63)Kn|?vzJ6Q2{*t?b(?sNc~e>XIRi3p>jW%y}i$W7(1i=9y0fkpKd!X zev4|lHv~v%Ql*b2&A)hoMge2mTt&ipOD_J5jY2{9rxX|lhNqv&f@g)zm+V%>B z@EJ{?J44&_%_dfFNB*5<`0!tcF1m8IAC0wDGsQDZ3o=%QIuS;*UMF9XZc=NzAH-Ci%YV_Ig|#unHd+X_)Q<~PQ%3wo^7;`vG7Kz3qVMq!Wu55`3PFDRyiF4 zZMRN=0I2vXh0}sI$r!v@@~KtIQ+wmvh~-vR(S*Jo)oMy`;Tu=_htYu!xGlLcPkXHHH8Xv`?c^VGXDY)Qgiyf90 zR>4N`dlUN{=L49843`*Zl4CT(yNH6xj8uDc2V`ZrT^?KK zCFCerl91zzFNi@6ty7}3az8+i)2t$*3!G~^@4t>o8;Ox?29N+L0Cs1ltXdJzp)xFhKeb%qgA0ylzX&MyycZt%Xh0b#e(2i>5O2Y14|!QQ{;=~5O*BCJtM z!sOk}eDxqYOL7`l}DTkA7xBDkm+yF5r*0`ql98>g+FV?1Rnbh(pt4|{Tx%Obm-1kA3K1OJ8sn?*qe!lM*TS_2hU zVi^{o9}T4A4;wpowDR&-1bHKgbs`vPeK{Gd{&a*Y?Y zoOHAxb<(!;m9utdwH_ZcxO%oC#vQN! zgdrTM4rY*+llfr7DgAqwjoU;KNJio%td{6db$Q6~f`qFp8KREC!lu+ClPBL=oU?u9 zOJ$8=>+!ICf(y`(#J3fG~qMEJdGK>e^i zv~*8*05BHUwHKeWkvxjf#2WPh+fIm8$E zNVhCb{w>VhP5Q@8hV8$;*>xIeky9{!{Al^A=BS8XEV>!o)b8ff*;XMSx1c7Z{&l&@ zt)iJz?|{pj%MpCGWXMHm>oS3G4*PvQ zu&NYv$2?YDSrp3Y>z@}G%33+^WkK3Q#MQ6o?(3n}chqDq>ulTnj8Ue%S80 zuaGjPK(|p1)S?ietbBKOp`rbvR z7c2H;xsTN*tF90(pyrT{_w970+5^Yuq&_Cb3HUm#)HopCoKZy?dMg+MD&V#e(8llSBzsXW6^3D@$~jn zrL7lK?`oUu56|1LTNDmk8*x4?Kv86K_o>Y+@Ycd&sI#|Foj#HT%>DWnk>kNID)5uz z=^d#3@zd}wThK%R>>8QM7GbGePFGc4-hR4rX`JM_Z|lj4IzavQn9mC~KhjQkeFo}c zVPFEMH#Hiq7E8#`|Lt>&gm7Ea_gk+IOJ{z}%9f=s9+oLt>W(U|&WsRlOd`*Oqp(wt zAHEt5m9i}PFZlP8gD{oIGA<(JDF*2fIq-48WVN*saKEQATRJ}2aqwP(g~dwf&AJcN8*qnG0o9Hmoe*7cU5BCGa?r^C$>E_G8lfp#9HObL*o~ z>VZ~GEmo!0#`YpD*|)(xkealtPeBp~X4?N@iU{^~V`sbcfkwfG+=-0oz-Q*_W|~Hn zubRVx-!$bdxUm@)GV)hgr?qCMsq)?2@EgegdL-0GTJt5f*@VK?7}-#2^ry?G65-3xdF|y}rq~px5 zq5n1B8imq6YsAqe0%xrp<~; zFv|EPxG)N!VM$r$(509#v4l3F^Qy`66i`A~%Lp?trDzmfe}=znrq4iqKht^@1+}G& z;`l)O6(LNb#BGapj31Zpl+^ylb*F{x)|Vr3pHuj^2wTE6;MrgVnWLZhF;Q~Nl+9ha zQk(cp9e~=j)LyPy(l|9T`n9;OE}%!u=|M^F3l`mz!9E{^#%dUZOCwT)9KdC?PXySR z-a0+q8p!Lcl~Op&j<#XafZ3ZnnPtpE6ch-3*!bFXPos>vj5?#+fxKJ#=b z>Gf%4qLT`?7rrDpMr4StoS}lbtgom!2wqow z(8{yR6+_#jxyy~i!>6I7h_a%W7Xwdj9fAS^0%CTl?GG0_7^rIgYN@-7xrF-|33QEd ztq032?_al7?PX22v~u^m58of{AzlW>Hl$CtxDw{av3N{RBnZ=-_WKl1U>6$?noQFP zjvtF7yv5CLvsyAyW2}>!yArh*-Z))Dz{kkEGx+lF>B7;I26^T(J;9}E6Ed-f6uESk z+x^6p&+iz;Jp71A3MwVK>ulC=TBR`)Bc-2L@27uir7re;mz>J_KJ{NCUloj$4*SG% zu9Lk0(AvK~ch+w-zwf?Ex{q|84nDw)@GqGe8KPf1=xVg6#1PkKAG$bS%Cno0v-9RA z3&CKcJ#ej_U@rPTej%*=HSvSQE@9$hC|%ZD`oV4#(ICcdKEghU8;WGD{%ut!BJJpI z_;bhCmyNJ^csBslEqk#(Y>J81F;butCynSDg!P$0nGs#25%To$toQIFQ*`TmFphqD z@_G8?3@O!1Nlf$K!j5O^ZcOD*XS((dBx6m5h=VQfd5uJPT}qwB=5=gc@r@hy%Av@% z)iV)C%mt)BOjQz`Is~wN$ilg~41-rzF~=`Vu>JaG{0%h^kIA3d%6tLQ{!jj@`W3%W%v z`Dz%FFj{ z7LVTP5Z0^BY5QxJ9U>V;xYdrZheh7b=}x-{fNq2FNRqY+n9$9>wbgcTv@kpB=_Jsj zqES{PG-4A}j~lH|n>s(k&kb;f-CW&lHKZvae^j+ER~=Dkwg~cxeeJ8PC&moj__P>zBP=&r-Sp?Y^xTpSmach9-uK9ZBAaToT~N z4nd@sC_J0`^kUQWJiQ3y zt`uqE@2xaK{O?Rx#(!pPP(1U}LiA<+Ofx-rP>Xqy-V;E*ndzPH5ul)^^a}|m2GWv! z+#Cncy|Pem{o5?3m_t5Qj~PFr2ZLEf2vM}!4{sG(C$Eq@Wx1EzJ7IZv3n^@A0wSDf|!V6EMJgZ3bsP)1w}Z{pc+&*u#THkYS(o5k}+zu8>o zDB3v(^Ykbxx67N2>-2~|l{m1HZBGZ`s61Vff%7t?A>S96)M{#Hu7)!oY=E^c)j=m~(`m*Q&xYcj8A$ zg{Efn;SIS-Ixi|o&fb=m@880E^M4i34eR@+Dn}+NhmzDG4WYzjZ^H{xBR|;f`Y#L? zxC52w75jwg{IiUs77fJ%z62`hR_!l{Bq7y@nY}4Mr1G#o-}DTnuw*1VN~o?(HDDh1 zaN#HIi}}Je+NMuPH@?y>vF84zrjXW^s>KfA$4{qKmn$s`EmaLnRjl+@msYcG>Y{sqn|1X9s{jE@;VB5-yWx5qf*wR6AqQ8HzfEoDxsAZu_%B zm}nJM{LP$^Shb@CW0RC^!$OeQh#n=^V=+{c-dW}C0YMbMRo`JQ)I+kszIHvAHfJ<{LOAx>jt8zY&FDq?EnFA7G z`@XZ0CaY9u$$}U@+;ej1P#gsW_|w)IYE=4^HKZ2_H84SXtbT1eRW`M_!6E?7^N;l> zR5LS0|0-(;s^}4Q$*r%Et#OHAKv8kCum<%z{789+V+c!Brlu9YsYY^-DT)HQPsLWL zl){rhYsI{*0ZtY}Ch7wCYzsxFjEs_)lb}tL0rT!E)+Cr}P$YBT%dD@S@O~X|Qc>)S zK8Bl%nhPdCBM0&B z2>E;d?EUeo##zba;a@LE5@Xw1EGzk@PulbN65pWTA&#s{Yc=!;Bpf)P9ydIRAc6(@ zJLKSsP^G5;a^cy=BkkFAyQekv7|L7?TD*4O1;A*=ytD2M|XY*~faIJ1NyYC&VdiQnbJ zWogsq*m7ceDvnyS#qK6Yz!a%_3NWbq8Y_kvAptm5E(nbU-71dd%f#{IdW3(7ixwL1TfoO(Zk$cmfc8=VB5ieeMUONtgivDplI zPuVOGjO>eg4kn1?Me_xt1$eWnDd_&h{(i>7mt83NbK2qG^y26yUfcls(LbeLfs;`x zUP|99inK=RnGFp}cogKtoJ`5A$k^QXvTw+$k7Bj!B<-sfvOlLedajSbAn+w0+x!vV zz^+v^qm+;Rbi|%C33Ww$5=sh@>iaz?ky-Io`M-aGghA+VtH#Bil|$~lACzBSDVe;k z@1d`$SlIsk;7s?wjJfGW0*o_WzTTOzxU>l)B#@(BoD*GE7hLUCn1sHz9#uXs1N-ZA3(C^e0DO{eYDnP)^dP2O`|`PUZIf^te$$3qdUx3cB0U znkI@SfGvzs1};Yd%gT((&Qvac4I3qNTf8{w zl)3~q1-7tw?Q24sbP>90;=J}e7Bh3QSKnmM5#EJtm+(u~OSk);UEJz2uH0-k7^NXf zYP>iCeJVYf-%g5KS-99#x=*s>^WpP+pmWG-qdcGxM72t2{=N1vet6et9FGeOTDm*OG$fN8M1-S)>gtfo==^hK~j-Z(*aAmD698CUqgPS~i`mbyLYPmZCsL2{-h zAx&oHy63Nl4W&XoDfwUy34B{`SZ}0UQ`qF}JE$+GBVshlNc}gUbY$zkAJ}zm*WLlr z%=ICr%O9DWp8JIo{6w3a`>p)0p&!O}K^TdBYt%=u<0MXaJ7?jZN7?1L2UOS&$l+zS zVd+W4^YScz`6W0Q?($BNstsOR>TrpWB!ZyiV$8fpy$3R0EA%qC5w!)ac`mz4MeG$_ z1s~W&p6F7gSqns)$?&j+qQ zI9-gw-QBF(&q=nN1WSBW(3>9FGO$ilAvc01^zX(TK_k+|Of^{LyL#3x0oQoIjPh$J zg7KEHzs=`AK?l9^;)XX^!ZnH__E#3FLdtyJz)x=es7HR0em%}`=ZAZ#VsI`~YQldk zxze^eCt_IjGj}X&NMnn$@m~{MnC6#d`K=Z#uPp@Zy3oL&Pf1Nz=7BcZ^zbOPPfD4N zWlZWoe^id9y#{n^6pDE?{<`B z{Bid5j~Z6%C0)I)R=@)q>r=~|)aZ$639`GyzZd_CsItMPS9})lftem){9B*282jc7 zMQCe#9BBw(9Eu6Q$X3%M2mi1u@mFvtFO!1Bcv;l9D%1PLX{xheMQr^2J(-~>RzsL2 z0kzxoQ8QNJYr{-~+%Op~ixw7wci)v1QU1Q!a&caKa{KF%>gYZdvL{F5AMQ(Jg-Y=Qa9cwbZ(o}4APeSQ|UepMf zV!V>xy5+^>w~8nR`jVqXhcf4~5{bdl9q=J$JYL-^77C^0Kg}7uVtaK*3nD;a65TaH%*ejw2EUK$Vefp*GK#i0 z3^XpP35#3#Llb<6QM#hJPy}jJPqQ+rx6;QYNV$su+uSTV2|eyBe$8AUf2oSi;7TeN_32qe zUdb`UPP|Sa$#Q-gz zP(4eGbV4W+MsE#HNXlWYl~W$45P6&VbBr9vz~t0;a1e-}lNA;H0C0A5+NK@l;eN~x zq<=K1Fa{Gg3n!9aq8=qnNh*C&$g2uu<|m~??dC-K%aM~am%c2eF?G&q%%xCjlyAh% zgy;%XH3b3Ei`EAfm~E39B_TuAQ&nceAZ~idANOfoPE=H!?9a+qMxrK~W2>vH6FYUn z4<|1?A*vFuB=|Lb1xu#)>T_Xjk}9av`cTu)l@bP25_FcudOZ9VJJWlMi*x6koqGL; zu0?4$0#*DZPiG-5d3>j#;`AS$4!;8hq(a!j0BVs#Kc8utJMrTQl)|aE%xAo#5mQ;gk`9xul_L_oW_DRC zh;M0JseL1?*(0y)E8HbNOaYI3r&gG-Bq3;tCBVfPbx~EGp5;IkSHD9KIgqmuN5w!E z-W8oQ!xPVrxz2+`EF)8RV%x22N=y>s{Y@P9Nf!L&aeYBy*f z@4hM8R!4EObCuNyGarJi#MwLQlHZ?CtVzjCLs{;g^ z?zJXKu<4P@kq^I0-{|a{RbM=>z(@=1dGHhsZNuV;3>X0R3TItgbh!&?+Sk9w02oX| z5f{dN-|@Yb;vu&a;bkIsoRMY57~RBB3?)z13HI~kbo_kGMz$Q?`ua;;U1tU8ibsY+TrHEFGyv4`YpT+Ae#OKE>U|PylW%c%X}~tS0KNAJ zG;rI=o`hR^kQT+<#Wl)}GRDs(CLYveW~N1`kF~XK0aq7t>55xh&yL4~%Zt#mqq5hY z0R%>C{`_Tm{C&`Q)pA8?v*XQiG^VYsO&xL|8}O>`a=U~8cN%HOu0xj7WKY)efl{>_uWK&;4F22>()rWk%cgyMavn*d7lQw3dGsI{o~vbIMS1BLwf=TJiMH!Y#K+(O0g7VO*BJvv*%XNV{#vjP z$TgbwJ6Rq{%-b`s`NMUTH$D6Ln=*+0h!neB50}7FmtaqiSHq8iics+~55huDhH0yG z>pS;Ho$^n_@Vk!le|t(M+?6!*ke?>FZ=osHeg56j_nxO1a2;1Gg}TNL(Emnw6@Q*$ zI0JaAHB>81g#ZyqV|`>&Psy}iM#}-+IXHRoV5K!(PqPeI?Q+@C)G|ZjJLd!8LWXv( zoL6;TpAlj?EXoHDLT?E>NP-|Yh4cFK+P8Iiv_;)0T_%A$;P)SLq}0!=o@M7shksvI ziIv6zB@GUW_nQS`lj}&4YN2yiF9S&B<%SIRBt4lE`xU>^&<(JB1>~($TRAiYV@V#u7B}X~S5;Phm0t3;o z5l>5fThbmoTW&0NA;$z7$E9^KKX(#Hnr?bQKOPrgh|(0!`u`UN23b?18M8I`5W%qtTNQPpRe2z##F198Ausz zA1*cm^(sGMFm!^d<`3;*Xj4{aVia#1n%S2#ymLMP2Y)>EeTj`Q%%T1ItAA6P!0J@@ymDkV~3& z{Hb9^df&v{Gqpf@ms2Z&Ci~`^l^-A2D^N#~-Rnvuc9hBcRl36PajVqYoLGGmaaV4s zGTWzVxlM(64NDf4s!j!8g9;S z&nz+sSgS_fHF4mmGlaAM1#{=hYS2-DB~@w`msNKJvR9ImnM`wfc4ql0GtE*CnhoZd z?t=R9L7KMjhv74;l@tH>Lvtc`e=3K*&&Y_Tq9Z8`b^NiQW4#*;Ed~VJJ0^z01yto0 zUf7Jj*HZGiGdkMZ>bU-(n6PZ{uXkBpicgl-zB{J^_@YkoQ94ta7N0VCd5Hnh=*YjG zYgcn-?$Y~0StuGnJ^qzpHYEa`O#L5(S1h4iIrrJ9?(c&y&5>tXC3(T`mWVgh|2qTy z0fOQ;EHTRia3D(s8}M5;0bd=|G^}{iJ69f=aHqh2&vP8Q;+*+2MhGBNN%dSfZbcS` zGu9-8E%InI3yjQn6U$hNxwUg!u2BNeBpg+N3hx(gbp0OiSef}O2BpG)>HYF zy-??JT+pM@9`Dwbb!t9qCAs&w#B+A&_>R~YUa<()Ke%CQdoz2OfUY9jgru zXaitPcF;XFb~?U+w&R>jW_a<_4DvPV0Uu?=sd{r@?r?hF`qNCdtGb~)HtLKQ%g^rZ zTs#cvQ#~FnRk^cNac1X;uNBa&lqk4ReaX)=7&a4aK+uJRQVg3k!Wvr{J58SM_ znXGC{&YD$5JwyY)xI?sx%>plpl?IQY_Nvbm{AG4|&2*S4FcBgH?r2wOnpUgwU^UwH z9}>FMd8S?YEQXRydeSS!3s4)c-r}>|V7=#DU&U5>wjUj>QC49Sx`pe0hSPtc>O*2? zO$9|SW+g^x`3w>2VnS;12)e9s@&YqoerpfQnGJ3Ce2a9IfmlB z(;^~PdVCXU=JOX!BdHH3QkIx?U8qk4_M70_U3#WonJqhgL00+zV|5>5BA$a$(|Mdk zWbc;@Wq#Oid+NzQh)fH0 z5sncEXy2{=8f$gCSHMo`x5}}2Hs8{SI|D1_SK(S`8@@rklx0E)4`wCs@oNrha#(rM zd_&m-kr@~u-${!RU>3623f_ppl%22mTKCU2WpIXxtF)O4O)E=IDf54SS@gnv0BXhC zGjKM~@yT?&r%k@mI3u1(ZZ?nG5rCbA&>bJtc$5Km$ub_TiaK*;H3pr#ei&;b zwnEn9z-w25A6Rl!`{at$i>exgiU?0VI04}&9uM#&E_T*V|(Gc3*lTdZq}cqM^-T0gG_ld0dc3{2q zb77>r?=XWHd%{T^-F6oUrk(^Z4>^9*hCaV~!xn-~0RW12Y(~Gme_G)M;vSxV)wkIu z$Buc+oND={OMCuUn%`P!68S^Q^eqZWg5aJ{iQ;b)kbteMt^&J>LvOCi%%%&*Mr&Pk z^wGu_p3;^07PrfZydw*9i`##fM-Y$pRROk`m6a9aql}ZG-mrEH18uYiz1PX0O~0L4 zlRS+M6GHu}S1Z#$NbYO}D?OCDT-y4$?y%}`d4_$d$MHugM>vPnY@!!~uw}gn?N>uu zS!(DeKXcsBRVI;&Bre}&u} zF#SgihEWl}fKIn(8WmbI03|X=m_OL$so9F4N0HRR6~a@a4})Ha9cec$hP19W_#rGe z-Csyp{}Jxns?X(U3i3Ty_h04MISG{ctn&Al|C`G*PjiC8>*@l3_64Vx`F+_F-*_b0 zW85^|^Ib92^a<>r#qDnb<4q_LxR_XJQUB0A&iwjPAg+F<<$y-SN$~ujrfCGfC4k<3 zi-EndAF0iGhPP>Tc2lxMT};}Lh(tB}t-5l)ojh-C2yxb4Zp${As7xzwK0g@=V1)>4 zR&K#ZG6@svgpZMi8TO%Y5b6{8#TyQ?@cd8ZWi$>;_92B=s}#GFose!ieIhAubz&=? zj{xg6Jq;mh-1HE!=pG$JvvOx-WH0y`r^R|Q4MJceX>Cc9I3vZMk-F~q$^+M6c)LEF8c>F# zgK@qnXtUErcYPlt=L-~V(B#+p;}P;%)8L>pw0$RyLfCox?~7ee1;GY3?E$ydj?Oky z1qFq(jv%BSB=qWvVAxh2zz=8_+P_pTdshTKYg5dyTh7kfGrSt(V1>-ijK(r@&dG^G zlDyN!?ul!OW{m1k4&)Eo&8B_RIpA6cVn_k#3HISDc)wc86M=iB&458NnDu5bt~444Ll`we<{dqP)QFnxjd#jL<$sXhBH@X%F$@q z@F$fS7$i|qFH^|52=}K@MB0y&v3bwOcozBRauGe(bNs=~ULu#of{AeV2aXbAKfoM6 zPi>i6qt80z8?Qaw5`*xv5O>I_znm}I(=gpw>^&Gpm`LF;@ev_hp``AN9iVN{nx%$s zPY*QKL7L(}E;ryi+&IjKALtn`1{fJEHl{Z7(996e&;Z@$*oDV;Qp@i^l&IrRE*QVd zh*=!@HCe8=^bz_I-#;r#BhZOLntSh!7az|~@4=N^+@ij?1u4QY1DBkNwm9Y1CVALk zr0M55?WW~G<#}zN6>`&zX~%%%tscDINSQ#KI?n?IAv4@{Fq_1Cv3f~O8*hCv_E@IP zopHB$?0*fbkjvavx?I0qX4(YwFQDQ(WW_%o$Bdm^#Zs5hod~Oui_=Mb ztGHoxveDIigX*GT9~2()Fgkn^(@d+k`*+1^&DJI$(LBt*ks{?0pxjhwP3$+Xe zAP_dQ+9*YmX{69AGHR_pdu+tl!#=a)7nxao}=QiNIhuXPU)1Ph9uhHui?Z>#iw zx6qVWq(^DR>$aZZ1UPAwZH}Qt3aqyieV?Sxez;c)Y{THSv+VfKHUT3%{y38`=S5=Q+$cxK45~+sbuD+bf7&i?_b>F^Mm?nCI{wm= zdbI(u#~CkjBz8QR@%1AGSncCD6J?q|}G|r&n%bE7y=Ih&OH;8$}O)KEH<|aIh+^8ViY^2w|=+N2K|ZQWgP*HlH03)SBfaHv1|Q?~ha{>7&ZMb*!PBJRh*M;n93SDI(Wu*X)0__8jx z85*7OXTj-a)cS=Lmy}jXopfKT*EBXoGU}OaYA9XIi+?{K{7_Rd|R7V9+8wx$D-TuXY*JM_O_q)_5yG{A%fIv%QWh|@*wE;==S;v zcs;Pa5x2Nvp~J@{Qa2rN(B2+q+WKz-BzxkYeiH0J2Bq}^XOmxYxysmw7ffKj8(pZ= zkF=#nJ|kT3WOR<+^9ATN^$DUM51s+y^Vz ztn6A^vii1^HQ*XA+t1~D|H5)W%cnK+M7oCH$+0*_W-|&ljD#5B{WbnXP?g+CF4Qek z0#!;R-q$qK2#`B$KYal6)ZOdVYH~5j*z%N)Ca?P$^opSNe&0r-gf$Z6xmFj-pialgkZ`2S_)dug8ons6X73g=rqLw87YClVhyDprP|!_ zNBD~df!F+0BS{YE)O-2dAITkz|GZ{@b+Emk8Ygu~Nuut&x@0${Zlp7FUnc=a3413x z_}(jn%D&j?B(@ODo_Id(~+*Pfx1O)O()_#Hd{qb&bzdLzIzjdP~#MU zruPOr4Voj>;qu&A(${cJxnJ{q51&9$Y1^TP^Jr=&Q2?6n75%r#WcQEqhujX_cdj6y zA62OyabO(@I{yi16NucMWa7@5;^dH^rE2*O@S9*DiaaVz1`sSvLZOI?M}5mHuShE| zzGkp>a+13pnVH;VN%z}vCxo6b6E}c;tZjcIsK~Lo?2fzlY^1dAJ&Hw8=f0!i#u#bY zteVh9?hulA8c*zg2Aw2-V(aVaD>?Z-y0ZENhB6Y$7;#(e?8wz=_aic$ivl&AhpaZW zmj&=Qq_-Sh&OLHF>RfS~Gtrnq^Y?a!nty&O$Wt7$3d&DUY5C9l*0ttbUCF;X{f3rg-y`CX1=3xbx-y)FBfWn&vM|x^7bkykiZ<$F zzE4EwPm^%M3@-?`(fACE`I4Rlm3YD1z!ECLtIS81<*Ac6Sn5T@2f!clJ}$f;&Ya+- zpMmA?IacLCu$9uJ2HpHDGm93lyN;(-b`_h_mWB4-_g7z@dC%xEr0ih`(=Eeei6oD}ds#VgeloTwojUqy2%n@Rt2#yqs4kUlWC;Nvd z{&MPngB3)k8V^8Mp3?6pN&sp-CwsxorW}bu2+)YDELEPx`2XDo{*1BHd!t&ApUX$* zi#BdWOzVI6RGCzf`aZ8_X@R}_^tG^us8`*%AoF)2P18?Xqtrlk#!c|aPwlyLZ{5#} zotV4T8y>A^{uE9Y>f#uA}ko$t~-<;jJ ztnwI~9j1)tZxSoAZvVjM#=}~=_56} z!dqfpI9$3k4_w8eHXy<|`+Z*JK3d71ZKBG#SiBp7svG#_&VIR4^#Mpro3L+1yZvFi zIQQ4nLxjlWNwNc-K93w=UZ?ce^Kjn1)n1yVZnB^Uph$tyNSa74DRwY#%ah2pXj+_C z4|6g=u7nuzd$-)U5MXJHkUyleE-T`lJN`9)cuvmHK-dwAY3CYY_JYj`UMCZD==I+i z%Aa?{W3vFEbO1lz%6A#%mbN=I^MRi@u3n zk})+3noSirYH*Ku(uz}L3pOf3!UoQHeZoBVU;l-ioPRMXsf}yVfb_Owmg=D`hQ&+! zxc;>)Ye+1{%1A2H?&8)yXC*C5M>ih)o_9$i`2)5?VQ4b{m&@<8<&ea6dQy3;mopH^ zNG}e8#LOG6F5dXphxqBnX~k{#aV`~L+;JgdR^QJEfj9(RbO{eE5Z^GoxJXb6R~>|| zJ}Wti!B8~05R+IvTd2tgf2q%GhYV77CYw zGP9j@RKa|aon zR~M6ab0utk-|iQJnzq+HMLcvE8J@%O6cd{SuYQ5?8j%GhbA;;B*;J^g$);QT>z+^t z`$d5OQN57xZ)xw@`hUBQe|t={Zo~V%dw1{768O!}pn;$f`u9OAzj~L|=$x3GzR?JF zu`h|R)loOwWc^e|>L&E-7~vR9{h5Muxb@I6`(_jYp=eI_Ho$#edwvHy%5kOv=|r}Y^)rGu#> z0Kc|BtUP-3sMaNDQQ{;#s7ZvBBe&LOUu$xEvOp{OE47y_3GnIbKanbTQgh>v*+5y5WZqi;lOBT-f$GS)zNoq_cmlvf z$LiS`F5ljek)++RUmW$k%k%<`wPb~57-OJCT6*)GD}jEAdQ#BQ_-fN<&Xu0GU#|cC ze?%=3YFa$q*dX|b+|XzMjBuL^q%f*WS`6!H456K1rs+kD&8wXFvMj--L<>}*D@Vr+ z>tO?99P}eBWja{Uj(;ofoZvJzd)Es)Q5ddb}omtP;I$Ij;cv7RrfqFu{} ze;Ka!h`%U@pivnK3|%vhAr*TxCD)UaJXm}*fw9}s6Ro0rovn?pv00XAx|JNT5~n45t)sG~Yq zkVmot!2;cIfHle51|Pvo5uYyOTtuWX@5GUx!_R=7A>AfzNKO+jrrGM|)J}OxXU~aK zlf-%70B~;iA~2YM0MNACp;HSFE)wDQ`y;bh%`%enw{wq5`D9(S+(Ugs`1Ba0;i*80-vtCDN0=}j*&-2n8-HP_g+|_!yJzSn zK*ZqkXS+T;A|q1&G6e4<0vh9ZrjKp44?C;*jt3)do}XFA-SUof4^DwTMJ{PuYY3P! z5kQ}Z2M6>A}lPbe&6;|YmuU= zx7+R8x9{Pus_SxE&ue((VcNT$&xC|sRaHfph)O8{k6NV_aSuXN)uw%gB@ur>5*|#4 zy){58Qf}AlkN2Nnzr37Y)`;l6xhVkDT5@*JF_ZFnq?ePoZt9cXJH4 z)b!4EpHHs%s2&6K$HA6~?El;&SrHcIp%z*F%}5pw9OCbq7^<$T77xZfWU(YpdNGk^d7NT9evp8y2l7ZZryrE zAP6wC-c^{@to4@TMpsoGh<2$pdH37Czr+gy^xgpi2?J5h^nPC>+#G@OwK8>Gl?W}| z!qmMr&0r!HMhMfcs!Od)tx3Wog5En)EK79e)L{@&U?kjC)mdz`!`@Z?n{+!XV$9OWz~S|=k%6DWT}n1iGcFrn>_?$!XX)S7=K-5?N& zz=c>uz$3HJ&GhBvf*59YKAryhmv3+9RR~o0T$X?Q>&G8I-_;YZ=S+}BKvOldp+23j zFbiau^=?vOtt%34trd}o(a_}WF3ee+a$gYvt+h;{=(_K%^`7gu;lw%E&+!DuCx{=@ zXK{?rYCWUbM!`P%*cb-@PCsLJxAeFK3`9#E*xUufFrEMpmkEp_PV5}U_&Qz(;xqpw zE1zT5^68~O5I%CLCyYOctdlQ!%-8{+&bR|m%-d!B-2oNkEtgOdj2&7G^}G*g$VWdu zkSHB?|AX#R#=FB(Dh^eOZwTiiqUI35Oucs& zOpc`#TGy&(UF~!_-yiq;{l0Cj)UvEg1m16(suq&t@|Ge1(f1t@d)IC20A%J;IA6n3 zF;ks|MG>?&B0QZ=goqp*L20NKb00xVJ@EwVUEALFt!?+d-&{KY zh!B8eR@o=-{*=Gxj)`Px5Yr4SyeM~p*%Oqg2PLC%XICC2Fy`MIX!vxp-ROj_2ntB>R6 z3JCPfY62LEsBp|01mkFAg3n5nIfWia$uyer2*5x!FoZ%RrAl2`BxMU}t8VGqJ2`pb zNdf4p>X9}tm_pK)|I$4-jlS)@_hCWI1PEq!oS{jz%ixFW_1ewW^#lO5R&(!N2`R!e zFNlZ&!rX^dL0GM&?OPfe7Z$T5bYjw8^KmYP&!@F@?PhhY0I=_^_rBESa(R(b_r15) zrA#mUa0C+OQc6P#hrM;Tc)gsCqn8QQ0s;$j-WADuZ_@S$kPv{0Jw8S1&RO+XSvdqaSVU_`OtcN`2N6ptwJuTw0%n1>jL&c%V*ztZ zm{$s8`iLO5)^e%FW@dIo(A(- zml+No7Q^#g&6@V6+Etsjz3;8#=e>@(KTFdL}>*M~| z+rIDHW7`sb&6B^BvbWxLDx>UbjRXfW|CBzN)*6)wU zw(s5M{Wo_kpG=nWk_8^AVTk#G9(l@h;gx~9K1PDP`Nn}B0O;X42u#c^0s@IrbWB6x z7}Km552k4(12+cwW`2g_49_DYz{AGwdLZ+M$Y6fd2^!MOAv(nFPrm8MEr{@#rosdC zIu3{-+M2dl&z2z{6`RMKnfb_?@`!F-2~P-ISFQyhPy|>|m>h6yVQ`Y^QopMKF?Qnt zh1u?pz3pl?!?}9fy3_&((+Ql@4svQI3zu-X-yc+@tCk|KudluJ0fmJzb0*hj%ai0H zk6fh=(c-!+Y&g+DOn?Frg?U-ZvednIz)&+LjEH4f&gb*8ED!B%*JUwgWZ}FQn0Q@J zkJ}xQwClcY-|g)bWp=Jv)zM?zUA^_~cCTxdf`|}iu7;YuqD7=1yXPg#IO+Uq6Ql3i zQ~f(yJ7mIP?vRVX1m0lsyADv#JU}yZapY|j;kvHNx-!dH0v)6iGwnSsf!*BHt5*>r ze}+z*St+8u55plMnTrZDQB}m0i*)x;Q|qSPyLL}A9S=F6fb3qvea_#BK+xbg3~(b+ z6%FfP-Ca$4*VcQp)~%~~kFbm~o(c4o(shZlgj*7MaQ5>7F=S#h-38JpGzDX0^fHKm z$ov+V%%5<8WZjs%8Xma6&&!>}fN(hQ8_(tJTtZJgX9Px~XKfl8GY$t`C3)IB3%mm! ztAyF!PA=nr>is7KJjg#t8GTdo06?lB3(Ja*RMr(s8bz9Q08};i$W-VA{cpF&`SMz8 zMMM#4t)15T*S~yYioR%6v94<^6$#gM zEv2USSJ#$VnX0|My_#z})&)fCO|>8)1VyMYElX{^Z`*@nL8Ma3VW#=m?sdWQX)RI^ zI90epT$d#xx@qfe-&v+UL$dMBZ!RCj3mzHgiG>M;07W)qQD z+xNDQmg%wW`@Ze_zW3fda~7TwPkx^1GiQ3lOkz@6hAVvr$7ye7>gGgLmU=o}%6clb za(X)hLQFbSgqv!$qV5r;xD?58C6T=%!jh)YnBzM$OX03kYek@uR1{!l)=itKS&}r( zLWY^jXv0Ur^(=`NPjiWZ|D@-YM!GAE#J$WJi1bzBQc0*tsil@$nCl3D8LsVykn9}= zcW@fDIF(`$oX|vM*Lj=}F?NCD>5IuU#59Xj12r>8Gc*tLG-?`_-LoM}Ey5>2e>7*1 zdB;3yK{#}&6UWa9CIBsIOa5=A9cjwoF^&QQ<2}vaO$a$RNGwE!sUjAjf?T<*;3TpN zgCJ^e)*G|9`Qvu``t|K}T6^2?_ib$-mb_WJ$%+sDrz1SnF}0+DXl zYlQ#)m)~38m*otAfBx%dL@<*XB}h|C4ZwYGOcH^)92&JLmj^6)4lrRY8N-(jC!?PQ zfEZCEoW!vEW4qlRJrAyb6L}qDQA{AnVe(*No(i*I;;=+WW+UKO_JhTo61u8dM#2C9 z9C^Xvfik|&5SC*u?Tu#`Qkpp*8;D7|0U6WZsL99-6a<*Oq9M?o0eKNYe!T7h*UUkD ztQLw00XpK~GpHd^S_S6t60_puw1EJv>(*M^o3^ff50A&Ty?=akv$rpo*VmH>iIkD2 z#7vA*OJ)^7^2(D>MafJb@swsB4oGC+nyHltNR+(KT4Zkx0thitntXO(dV(rDVf9 zIy*$HBIXw6``)&F1H{v5eR;W<_S@reIxm+qN$Q^gGUIPuYC04}#O->2ds%B;g{XHW zSvat(mbKPp(XRW}EJ<|`kL^)z?+X^>q9fBSx?1mj@4dBo0HlHTzH4u;8Y4B`^E02= zVk9J(-b=~h85H4ssLVV}``%hJ?e2~M=_56(-{| zqfcR2(;_iR;fNx10xy!e^ipztG&N93*z<5hS9b)VMKa?=uzT z0D>|DDh+>79b9RIe`zFSm*0CtiXR3kk=C@#!eRVK`1yh)d!>B#X!r&ZR5`5d&K9-At0?x3{;K3lhF>ZQJ$;5aD5& zMg0Bim+#+RAoTtHQF!orBagS0KveGFhQ9R>?1$K?d~%~cN=4bj}0 zXkE(tk1Ie3X9`WI83YiCy1JU2PUp3xlj7y_@|WL#w|}(MMb%Q5W$Aqf01>&}Zbx>= z<#O(Qf7~B$FK2+!e3+VhCY3V+_TEZah)B)A(_IEdc!hdCU$pn@?bfuvy}f+<_T_r} zXu5YZHBGsSuxO~NE$h;{W;J{~w!QTiI5E-Qn^a^HHQ#%$!ppkmV5R2ju*d%K{!>G^ ztVu^vgKpM)-&=33wSC*SZQr+j-+C7AT&l(hq>4lVVT2WqQZTlGHqz2_>FOC7(bV>l z@0fgd82_c#g@tpQlH4jptfkiVT-WYhRhQC>uw){8CftdLu;ieeF3zPES!zjBY=H2v zjLS_|e1|X$N4JnM7)HdfOY{_Dp&?`j9R#opBIxj2$Q_6y03!)YT?$c%Kv+QtW)cyW zl7xdX1wpQbQZ3xw5h%hi0?^EZ10uaKA?7@txXggso=xOHVFD~y5*}vI71h8jOru+< zrdyvUs6`AkVsj1yAuta{Hv=?eB&8sC%1trNu_5iTVPp+Fm)PTrra#`?BuA2%^2dA< zQRkl_LKMQnRMJfcsS+>D)sa~WWid^!0k?23MV3--kL`ASv}T-n8gp~|`uegi_2-{| z-0$0$FW-<*wHK+cZ!dnmo7EYO+K&dlja5Y zKXIaxt21;d<`jWI1_5C_RKpYqVPiuzBwqPpCx+_a5Z#qKYm^R*FXMsz1>!kua}E3-|zcl+kXGc>$=o+S*TE{)p!nM2t-U$hy{5J zAM?H)Gm)8v4T?wW!`F6fh|QVkv~uq)JXqKPGE_6&0Ji*(vz1?nB%jJAdT^In=TGd=cmb&!byN=k4Y&KF( z`*=L$a!C$mM3}o1x!L>sk8W7k*B}l6bx`xJy|)DZx3=%CwccCTrrKS*n-0!j3g?q3 z{-g|y#ZI24sgm-E#i{P@8c*XAbMrlkP;F16hp~rq1CL$mvhPc8)?00BdIJwfxiO-# za5DySh;r2BA|6gCyU125gOBqvXOJkL5wJz{79I2Ds&F zp$cYbhT2UvGzS&6;CUTM$|R0nK_o+=>U3@!a!17Y7kX zhHy5TKHJ`@n>aQCDY6ja!cti(Nkyu}mAIt%*9W(%hv~LI5aGA)--zJxc-$YG1FWY- zt-JZUF0U`ArSSgPK0fZ>zWpwxs#&Q;q`bbo;hS}+KT54(9%p4Jy|A(tT zdy*tc&OAZBL`2oh{D{cNqq0zFbk7pa?9k3U?En9=k4v=IY%dKF2510PsLCU5cQ-Rt z6%l6M2NpFCbbtz!6Hguqtrfsq@0aVf1!aF+0=^44frW1JmRI3aXkf-HW$eswl)4BC z2$F;m0D7;rUeD*Q20-8pfSAO=7>T5qp*D5306sT}Z#QlkLv)eYY6$oMs)Yz7z8oA{ z1022~)fmT9a9b746>nN4$6c#tyVR4A$G}SCWZ3E80BB%J3gY0fEK8abfN$H1g7bXb zuI(=$-+z6+{QIxJYHdHgecQ@p>Qm0|-oE|$PjBD8eavOzL>yCJ5nv`x%rR8Fxoi*# zy^qpAgqym{F2`mY!J-pZ1$Rj~wj9Bl0dUn$l$hDNGf7G#dh&cZH>H#$Y`sLZEZ5c= z3nxjpyFIM;gg(tBXYRdjTV>`rya3>q3>XkgaL}%`ZipNu!+bbAKHSCm^ZfEWA5QZ; zzkU1W`q%4LH`96n5D`IgcVP(>E-~etR678fp_7-KJuK)15n38TSQwK-B827q)H;;f zfpbKFsCqZ;aq4ed)c&p4*0L5TdTFV*0-8-k(_zRNGT@DNhI2-n2j@$q$DNhlvtREfylu^F{aipvF2oo?2rdiNi;-b3=l*3L>C>$=^GFsv&|~kKTR!NjDo6>|f^V+;m6`yJ_#%8}^P}u{Tf!wNO+IlkQNR;=cVG zijHvsFkw8c-5gcpnuy)w;OX7qECFh~!4Za3&jHct_Tc+|Z{219Oc>G1BPWh|Tmu+y zGiTy#rmc0WS8KIzYujpFx2dGZyAxPz+tzyrL?$v*MtV3Mzk9sf*5&+iHs_yy`oq_+ zpSEqezrXKYOHMhb^Y!}i{paPn-rt>aO7m3extjKv#ENhvjyZh6J%LfNvYkNLl0%_7?OQd8L(wx914Qj#|rrVzL zwG$uqVLP(OUXv37MCfYowYLobddJIoJD->NbOb^s&QcKZd^scHo5zQ_4-S}(l3qJS)VqPI;(phw+8{XXLd^K24G|r9DK6ckhlYiDjM-Z{9q9_wHf7E1YHc z8h}X{WdOlwoCY+oz3zZ$JG)Z5MtOyJ<5I`6*}7@#3PC#CXLYT$u3aQz4DfK6kB2$< z6kng7mu74AQl`T^_trmt{749I-@Z8<4%@bcbVOB|*|g`JM0i`bTI)PdDW#ms(A9^q zJ0_<-AP2iU8%s{}l-Esa*H525o|=~V_=lf={`~pNwq9#(T{gw7D{*!rGc+3_oYwnI{L|YOx<~JAukH+o zc~9M)JW_RsifXS`Z6ID<-MU%d^DtgxKz0Xw@V*h%UKJD`V4?wY4bNIAeTG7hIhV{N z`ww*}5lCVUn^H=|;soU2X5G|Vj5^7IWY-<2Yd3fE9^*?QcfwuK9uJj84aF3ZNCTM8 zM()X@GQx5G?|hUnxhTYrVW>#S5tW8Q%G}%=I+Lpd;T~|k@6>OT^pJv#jaE0%_zTt6 zt#xQUPJdL3rS9%l`sUZrcZ5G495F;h=!(XuvE${?k3($hZXov|GVyQO2JW{K|8|p) z>UJb!Vg$J&5ty^15W71v za&mI7ZByuwoMuXQN5s^h&UL%y=~y1;<9GKmPYWpJ)Vq>3%n3^Fh_x#ry?cCooQ^*| zzD@4aJe}_E?jG-tcLzQY^6<G$7$ zSuST{-dZ#8TuRERU3(bqLdn}(uUjjUF@_XsopdTCfSD;thno-?toOBDsC8m7By%!_ z{qF2td+U+=Qb($kYvh6N;EmvzjkYy3`=h%ZLBTh-;{MI#hW&CCz-vd1d=MV9g>=_9>z16xdm_C2{(7HW5+<*7on`ODEsYn6`>sDKTdU}5U+sBlqc`l_S zX3jaaUTdpUIow8-4i1hi>~8D2N|J5c`f8F3GsU+jA^~EALG(0XvVHRr*T8aJ%$ejuyl^ zmLw?y@8RIg$OH(WuSJOM@3!_1WB%!_wTP%U^WK|-H`QgUs>7BQT|;b6YOO(jO(Fm~ z&!wtet98{fmDc;VZEiM|GSr8X2q~&0BGleZjUdfgj)yt2C78U`9*!Uq2#Zqeq=^Io z!hfJ@Op;Q@Wqo-$r*b?kODXwJfBL&xm)>e3+G=;Cf&SY&BVvMvF#$G%)|>=Fm5wlK zLMBlf3c+{>v~C?L5-Xq?bqjEXYS(?_5JH5(lNnU9ffT!KgJIY5<4u(8vC;FJZh5c` zad#QsR$@04$#^w!d}ue@YWJLZK41 z(ANM`Y|!^&duVKYPu6t^aaB-e1`R;FwQg0hY4E;XW2Q4a%`s_-v-O+hu{$E;$O8!! zBLT@+=MEl)#UKLA87x}VVH6pk<5dk654{V|KMbk$Z{E(HQkFJQ4#C19p%}jn1 z*~T*0LL`792}`W?{JVE=dxyHL-c7qMwF>k7!`;%`d0X_w0L{ej-`p+Nr!+%G*H+2> zG~K1+M5ti8T{luKBm29r2(l?AR9vbk$a?yCIPg zu5D8@LfTcV`(%r7x1G0Xo=VQzwfFV-^UuKLy4kY5xY@RCEb`+IKc4RIF4t@8m6>zS z!Ym?*WZTwKSUX-X%eyykQWj=1>%CPLA)+KmEM~4vsi2Smf`Y2qx~+~Rk_f=Me);kx z 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